olo_ft_ram_sp_scrub¶
Status Information¶
VHDL Source: olo_ft_ram_sp_scrub
Description¶
This component implements an ECC-protected single-port RAM with an opportunistic memory scrubber.
The user-facing interface is identical to olo_ft_ram_sp plus a scrubber-enable input and four
scrubber-status outputs. ECC encoding/decoding is transparent; the scrubber additionally walks the address space
autonomously and writes corrected codewords back when a single-bit error is detected, refreshing the memory before a
second upset can accumulate into an uncorrectable double-bit error. Because the underlying RAM is single-port, the
scrubber only issues a read or a writeback on cycles where the user is doing neither (WrEna = '0' and RdEna = '0').
User accesses are never stalled.
By default the scrubber is free-running. An optional internal pacer can instead limit it to one pass every ScrubPeriod_g seconds and flag Scrub_Overrun if a pass overruns its period; see olo_ft_private_scrubber - Scrub Pacing.
This is useful in radiation-hardened designs where single-event upsets (SEUs) can flip bits in memory cells.
For background on the SECDED scheme, the codeword layout, error injection semantics and the constraints that apply across the ft area, see Open Logic Fault-Tolerance Principles.
Generics¶
| Name | Type | Default | Description |
|---|---|---|---|
| Depth_g | positive | - | Number of addresses the RAM has. Must be at least 2. |
| Width_g | positive | - | Number of data bits stored per address (word-width). The internal RAM is wider to accommodate ECC parity bits. |
| RamRdLatency_g | positive | 1 | Read latency of the wrapped RAM, excluding ECC pipeline stages. Higher values can help close timing on the RAM read path. |
| RamStyle_g | string | "auto" | Controls the RAM implementation resource. Passed through to olo_base_ram_sp. |
| RamBehavior_g | string | "RBW" | Controls the RAM behavior. "RBW": Read-before-write "WBR": Write-before-read |
| EccPipeline_g | natural | 0 | Number of pipeline register stages within the ECC decoder (range 0..2). Total read latency is RamRdLatency_g + EccPipeline_g cycles. See olo_ft_ram_sp for details. |
| ScrubClkHz_g | real | - | Frequency of Clk in Hz, used only to size the optional scrub pacer. Set it to the actual clock frequency; must be >= 1000.0 when the pacer is enabled (ScrubPeriod_g > 0.0), ignored when free-running. See olo_ft_private_scrubber - Scrub Pacing. |
| ScrubPeriod_g | real | 0.0 | Pacer period in seconds: one scrub pass is started every ScrubPeriod_g seconds (1 ms granularity). 0.0 (default) keeps the scrubber free-running; any value > 0.0 enables the pacer. |
Interfaces¶
Clock and Reset¶
| Name | In/Out | Length | Default | Description |
|---|---|---|---|---|
| Clk | in | 1 | - | Clock |
| Rst | in | 1 | - | Reset (high-active, synchronous to Clk). Resets the scrubber FSM and the read-valid pipeline; apply a reset pulse after startup, since the scrubber's address counter does not self-initialize in simulation. The stored RAM contents are unaffected (block RAMs cannot be reset). |
FT RAM Port¶
| Name | In/Out | Length | Default | Description |
|---|---|---|---|---|
| Addr | in | ceil(log2(Depth_g)) | - | Address |
| WrEna | in | 1 | - | Write enable |
| WrData | in | Width_g | - | Write data |
| RdEna | in | 1 | - | Read enable. RdValid pulses '1' exactly RamRdLatency_g+EccPipeline_g cycles after each cycle on which RdEna = '1'. Note: holding RdEna = '1' permanently leaves no idle cycles and starves the scrubber entirely (see Opportunistic Scrubbing); deassert it on cycles without an actual read. |
| RdData | out | Width_g | N/A | Read data (corrected if a single-bit error was detected) |
| RdValid | out | 1 | N/A | Read-data valid. Pulses '1' only for reads the user issued; cycles consumed by the scrubber's own reads are masked out (see Architecture). |
| RdEccSec | out | 1 | N/A | Single error corrected flag. Unmasked pass-through of the decoder's SEC flag: qualify it with RdValid = '1'. On a scrubber-owned read return the flag still appears here but RdValid is masked to '0'; scrubber events are reported separately on Scrub_EccSec. |
| RdEccDed | out | 1 | N/A | Double error detected flag. Unmasked pass-through of the decoder's DED flag: qualify it with RdValid = '1' (it can also assert on scrubber read returns, where RdValid = '0'). Read data is unreliable when the flag is set. |
Error Injection (optional)¶
These ports drive the internal olo_ft_ecc_encode instance (via the wrapped olo_ft_ram_sp). Leave them unconnected for normal operation; see Open Logic Fault-Tolerance Principles - Error Injection for the shared latched-strobe semantics.
Note on scrubber interaction. The encoder's injection latch is consumed by the next encoder write, which in the scrub variant may be a scrubber writeback rather than your intended write. Drive
ErrInj_Validtogether withWrEna(immediate injection, bypasses the latch), or pause the scrubber withScrub_Enable = '0'while preloading (see Pausing the Scrubber). See Error Injection for the flip-pattern semantics.
| Name | In/Out | Length | Default | Description |
|---|---|---|---|---|
| ErrInj_BitFlip | in | eccCodewordWidth(Width_g) | all 0 | Codeword-wide flip pattern. Each '1' bit XORs the corresponding bit of the stored codeword on the next write. Popcount 1 = SEC-correctable, popcount 2 = DED-detectable. |
| ErrInj_Valid | in | 1 | '0' | Strobe that latches ErrInj_BitFlip into the encoder's pending-injection register. The latched pattern is applied to the next write. If ErrInj_Valid = '1' and WrEna = '1' in the same cycle the pattern is applied directly without going through the latch. |
Scrubber Control¶
| Name | In/Out | Length | Default | Description |
|---|---|---|---|---|
| Scrub_Enable | in | 1 | '1' | External enable. '1' = the scrubber runs in idle cycles. '0' suspends it on the same cycle: no new operation is issued, an operation in flight is aborted, the pacer's overrun watchdog is disarmed, and the address counter is preserved so coverage resumes from the same address when this is reasserted. Use it to pin the scrubber down during ECC error-injection tests. |
Scrubber Status¶
These outputs report the scrubber's own activity as clean, directly countable one-cycle pulses; no external qualifier is needed.
| Name | In/Out | Length | Default | Description |
|---|---|---|---|---|
| Scrub_EccSec | out | 1 | N/A | Pulses '1' for one cycle when a scrubber-issued read observed a single-bit error (SEC); gated internally so user reads never appear here. The scrubber writes that address back in the next idle port cycle, unless a user write to that address (or Scrub_Enable = '0') aborts the operation, in which case the address is retried (see Opportunistic Scrubbing). |
| Scrub_EccDed | out | 1 | N/A | Pulses '1' for one cycle when a scrubber-issued read observed a double-bit error (DED). The scrubber does not write the cell back (the corrected value is unreliable). |
| Scrub_PassDone | out | 1 | N/A | Pulses '1' for one cycle when the scrubber's address counter rolls over from Depth_g-1 back to 0, marking a completed pass over the memory. |
| Scrub_Overrun | out | 1 | N/A | Pacer watchdog. Pulses '1' (and a simulation warning fires) when a new scrub period begins before the previous pass completed. Disarmed while Scrub_Enable = '0' and tied '0' when the pacer is disabled (ScrubPeriod_g = 0.0). See olo_ft_private_scrubber - Scrub Pacing. |
Detailed Description¶
Architecture¶

This wrapper composes olo_ft_ram_sp (the SECDED-protected single-port RAM: encoder + RAM + decoder) and olo_ft_private_scrubber (the opportunistic scrubber FSM).
The wrapper places the olo_ft_private_scrubber in front of the wrapped
olo_ft_ram_sp. The scrubber owns the user/scrubber arbitration: the single user port
(Addr / WrEna / WrData / RdEna) feeds both of the scrubber's user channels. Because the underlying RAM is
single-port, the scrubber is instantiated with SinglePortRam_g => true, so it collapses the muxed write/read
addresses onto the one physical port itself and drives the wrapper's RAM address from its Ram_Addr output; this
wrapper therefore carries no address mux of its own. ErrInj_* go directly to the wrapped RAM's encoder, bypassing the
scrubber.
The decoder's RdData / RdEccSec / RdEccDed are forwarded straight to the user, while the user-facing RdValid is
the scrubber's masked read valid (scrubber-owned read cycles removed). The arbitration, the registered
read/decide/writeback sequence, the read-valid masking and the optional pacer all live in the scrubber core -- see
olo_ft_private_scrubber for the details.
Opportunistic Scrubbing¶
Because the underlying RAM is single-port, the scrubber issues a read or a writeback only on cycles where the user
is doing neither (WrEna = '0' and RdEna = '0'); user accesses on other cycles do not disturb a scrub read already
in flight, and only a user write to the address currently being scrubbed aborts the operation (user data is
authoritative). The scrubber keeps making progress under partial traffic (any duty cycle that leaves idle port cycles);
a user active on literally every cycle starves it, but starvation never causes data corruption.
See olo_ft_private_scrubber for everything the scrubber owns: the FSM (states, abort behavior, read-valid masking), the user-always-wins arbitration, the SEC-only writeback policy, and the optional pacer.
Pausing the Scrubber¶
Scrub_Enable = '0' suspends the scrubber on the same cycle: no new operation is issued, an operation in flight is
aborted without writing back, and the pacer's overrun watchdog is disarmed. The internal address counter is preserved,
so the next Scrub_Enable = '1' resumes scrubbing from the same address. This is the deterministic way to keep the
scrubber from interacting with an injection-test sequence:
Scrub_Enable <= '0';
wait until rising_edge(Clk); -- FSM held in Idle_s
ErrInj_BitFlip <= some_pattern; -- preload the encoder's injection latch
ErrInj_Valid <= '1';
wait until rising_edge(Clk);
ErrInj_Valid <= '0';
... wait / set up / verify ...
WrEna <= '1'; -- the latched pattern lands on this write
WrData <= test_value;
wait until rising_edge(Clk);
WrEna <= '0';
... read back, check RdEccSec = '1' ...
Scrub_Enable <= '1'; -- resume background scrubbing
ECC Overhead, Error Injection and Status Flags¶
The ECC behavior is identical to the wrapped olo_ft_ram_sp, because it is the same instance. See the corresponding sections in Open Logic Fault-Tolerance Principles:
- ECC Overhead - internal storage width vs. data width
- Error Injection - semantics of ErrInj_BitFlip / ErrInj_Valid
- Error Status Flags - meaning of RdEccSec / RdEccDed
Constraints¶
See Open Logic Fault-Tolerance Principles - Constraints That Apply Across the Area for the no-byte-enables and no-initialization constraints. In addition, the scrubbing wrapper is synchronous-only: the scrubber observes the user port on a single clock, so there is no async read clock. There is deliberately no scrub variant of the true-dual-port RAM; see olo_ft_private_scrubber for the rationale.