Skip to content

§ 3.1.1. Flip-Flop Storage

This section specifies flip-flop storage from feedback gates to an addressable memory. An SR latch leads to a gated D latch, an edge-triggered flip-flop, and a write-enabled cell. Cells form words, words form a storage bank, and address/control/read circuitry completes the memory device. These components store state; their outputs depend on earlier inputs as well as the current inputs.

The word, bank, and device here are memory components: a word has a write mask per byte and is reached through an address. The whole-word registers and state bits used by the RV32I circuitry are built from the same cell but are storage, not memory; they have no address or byte mask and are documented in 4.3.1.

Component Description Structural Behavioral
SRLatch Holds one bit using active-low set and reset inputs Yes No
GatedDLatch Follows a data input while enabled and holds it while disabled Yes No
DFlipFlop Captures a data bit on the rising clock edge Yes No
FlipFlopCell Adds write enable to a D flip-flop so a clock edge can either write or retain the stored bit Yes Yes
FFWord<W> Stores one W-bit word with a write enable for each byte Yes Yes
FFStorageBank<W> Groups a configurable number of words with shared data and separate word write enables Yes Yes
FFMemory<W> Adds word addressing, access checks, and read selection to an FF storage bank Yes Yes

These columns indicate actual simulator implementations; the simplified C++ examples below are not counted. Cells, words, banks, and complete FF memories each support structural and behavioral selection. Structural remains the default.

The FF memory controller connects this native device to the shared transaction protocol. FF storage itself does not require a controller.

Conventions

  • Pin widths are listed in each table. FF memory words and banks use W = 8, 16, 32. N is the number of words. 0 and 1 are binary logic, X is unknown, and Z is high impedance. Z is not a third stored binary value; gates interpret it as unknown logic.
  • S_BAR and R_BAR are active-low. The other components' RST is active-high and asynchronous: asserting it clears state without waiting for a clock edge, after gate propagation.
  • State is not guaranteed to start at zero. Establish a known state with set/reset before relying on it. Every recorded scenario below starts with reset.
  • Behavior tables describe settled outputs. A rising edge means CLK changes from 0 to 1; a sustained high level is not another edge. Keep data and control inputs stable around the edge and allow propagation to finish.
  • These are digital timing models, not analog metastability or transistor-voltage models. Their delays are simulator units, not calibrated device timings.

The simplified C++ examples describe binary behavior, not the simulator implementation. They assume reset has already established zero storage and, for clocked components, CLK = 0. Call update for each input change, including falling clock edges. They omit propagation delays and X/Z handling and can be compiled together in the order shown.

Each examples table contains every recorded checkpoint of its embedded scenario, with a zero-based number, the timeline label, and the absolute simulator time. Inputs are the values in effect at that checkpoint, including retained values. Preparation actions between checkpoints are replayed by the simulator; a table is not a sequence of independent truth-table inputs. Each component has its own standalone scenario, including the bank.

3.1.1.1. SRLatch

SRLatch stores one bit using feedback between two NAND gates. It has no clock or data input: set and reset directly select the stored state.

IO pins

Pin Direction Width Description
S_BAR Input 1 Active-low set: requests Q = 1
R_BAR Input 1 Active-low reset: requests Q = 0
Q Output 1 Stored bit
Q_BAR Output 1 Complement of Q in a valid, settled state

Behavior

S_BAR R_BAR Settled Q Settled Q_BAR Operation
1 0 0 1 Reset
0 1 1 0 Set
1 1 Previous Q Previous Q_BAR Hold a previously established valid state
0 0 1 1 Invalid storage condition: outputs are not complementary

The invalid input pair does not produce a simulator exception: both NAND outputs settle to 1. However, releasing both inputs together does not specify a reliable stored bit. The recorded scenario recovers by selecting reset explicitly. Do not use simultaneous release as a way to choose a state.

C++ example

#include <stdexcept>

struct SRLatchReference {
    bool q = false;
    bool q_bar = true;

    void update(bool s_bar, bool r_bar) {
        if (!s_bar && !r_bar) {
            q = q_bar = true;  // Invalid storage condition.
        } else if (!s_bar) {
            q = true;
            q_bar = false;
        } else if (!r_bar) {
            q = false;
            q_bar = true;
        } else if (q == q_bar) {
            throw std::logic_error("Release from invalid state is unspecified");
        } // Otherwise hold the valid state.
    }
};

The exception is a guard in this example only. The simulator runs the actual NAND feedback circuit instead.

Unknown inputs and timing

The two NAND gates apply their normal four-state rules. Outputs are not automatically complementary during propagation, invalid input combinations, or unknown states. The recorded high-z-set-input checkpoint drives S_BAR = Z after a known reset state with R_BAR = 1; both outputs become X. recover-from-high-z demonstrates recovery through reset.

Implementation

Structural implementation

Internal component Count Connection
NANDGate 2 NAND_Q and NAND_Q_BAR feed each other's second input

The feedback connections implement

\[ \begin{aligned} Q &= \neg(S_{\mathrm{BAR}} \land Q_{\mathrm{BAR}}), \\ Q_{\mathrm{BAR}} &= \neg(R_{\mathrm{BAR}} \land Q). \end{aligned} \]

These are feedback equations, not two ordinary C++ assignments evaluated once. Gate events propagate until the circuit settles.

Examples

All 8 recorded rows of SRLatchTest, in checkpoint order. Outputs are test expectations verified against the simulation. Time is the absolute settled checkpoint time in simulator units, not milliseconds or CPU cycles.

Default scenario profile: canonical-default-preview / structural (175230e5d834592b). Changing fidelity or the circuit can change these times.

Checkpointzero-based number · timeline label Timesimulator units R_BARInput · 1 bit S_BARInput · 1 bit QExpected output · 1 bit Q_BARExpected output · 1 bit
#0reset3 0 1 0 1
#1hold-reset5 1 1 0 1
#2set9 1 0 1 0
#3hold-set11 1 1 1 0
#4invalid14 0 0 1 1
#5recover-reset17 0 1 0 1
#6high-z-set-input21 1 Z X X
#7recover-from-high-z25 0 1 0 1

Scroll within the table for all rows and pins. Buses use hexadecimal; values containing X/Z use MSB-first binary. Inputs show the declared values in effect at each checkpoint, including retained inputs. Sequential tests also replay preparation changes between checkpoints, such as lowering the clock. Use the matching checkpoint label in the simulation below.

set followed by hold-set demonstrates retention after the set input becomes inactive. invalid deliberately exercises both active inputs, and recover-reset restores complementary outputs.

Embedded simulation

SRLatchTest has eight recorded checkpoints. Inspect NAND_Q, NAND_Q_BAR, and their feedback wires between checkpoints to distinguish propagation from the final stored state.

Open SRLatch in the simulator

Source and tests

Test Coverage
SRLatchTest / srLatchSpec() Eight checkpoints: reset, hold, set, invalid inputs, explicit recovery, and high-impedance input propagation

3.1.1.2. GatedDLatch

GatedDLatch adds a data input and an enable input to SRLatch. With EN = 1, it is transparent: changing D changes the stored value after propagation. With EN = 0, it holds its state.

IO pins

Pin Direction Width Description
D Input 1 Data to store while enabled
EN Input 1 Active-high transparency enable; not an edge-triggered clock
RST Input 1 Active-high asynchronous reset
Q Output 1 Stored bit
Q_BAR Output 1 Complement of Q in a valid, settled state

Behavior

Here and below, “any” means either binary value, not an X input.

RST EN D Next settled Q
1 Any Any 0
0 0 Any Previous Q
0 1 0 0
0 1 1 1

Reset takes priority over enable and data. Releasing reset while EN = 1 lets D propagate again; releasing it while disabled leaves the reset value stored.

C++ example

struct GatedDLatchReference {
    bool q = false;

    void update(bool d, bool en, bool rst) {
        if (rst) q = false;
        else if (en) q = d;
    }

    bool q_bar() const { return !q; }
};

Unknown inputs and timing

The implementation evaluates the actual gates. For a known enabled latch with reset inactive, D = X or D = Z can make both outputs unknown. Asserting reset restores Q = 0, Q_BAR = 1, including after these unknown-data cases. The binary table does not specify every combination of unknown enable/reset inputs.

Implementation

Structural implementation

Internal component Count Function
NOTGate 2 Invert D and RST
NANDGate 2 Generate the active-low set/reset requests from D and EN
ORGate 1 Disable the set request while reset is asserted
ANDGate 1 Force the reset request active while reset is asserted
SRLatch 1 STATE holds the bit and supplies both outputs

The inputs to STATE are

\[ \begin{aligned} S_{\mathrm{BAR}} &= \neg(D \land EN) \lor RST, \\ R_{\mathrm{BAR}} &= \neg(\neg D \land EN) \land \neg RST. \end{aligned} \]

For settled binary inputs, the gating prevents data and enable from requesting set and reset simultaneously.

Examples

All 12 recorded rows of GatedDLatchTest, in checkpoint order. Outputs are test expectations verified against the simulation. Time is the absolute settled checkpoint time in simulator units, not milliseconds or CPU cycles.

Default scenario profile: canonical-default-preview / structural (77eec9d88233a6de). Changing fidelity or the circuit can change these times.

Checkpointzero-based number · timeline label Timesimulator units DInput · 1 bit ENInput · 1 bit RSTInput · 1 bit QExpected output · 1 bit Q_BARExpected output · 1 bit
#0reset5 0 0 1 0 1
#1release-reset9 0 0 0 0 1
#2disabled-hold12 1 0 0 0 1
#3transparent-high18 1 1 0 1 0
#4transparent-low25 0 1 0 0 1
#5disable29 0 0 0 0 1
#6hold-low32 1 0 0 0 1
#7re-enable38 1 1 0 1 0
#8transparent-unknown45 X 1 0 X X
#9reset-unknown51 X 1 1 0 1
#10transparent-high-z56 Z 1 0 X X
#11reset-high-z62 Z 1 1 0 1

Scroll within the table for all rows and pins. Buses use hexadecimal; values containing X/Z use MSB-first binary. Inputs show the declared values in effect at each checkpoint, including retained inputs. Sequential tests also replay preparation changes between checkpoints, such as lowering the clock. Use the matching checkpoint label in the simulation below.

transparent-high and transparent-low change Q while enable remains active. disable and hold-low show that a later change of D does not change the stored bit while disabled.

Embedded simulation

GatedDLatchTest has 12 checkpoints, including transparent X/Z inputs and reset recovery. Expand STATE to inspect the same SRLatch from 3.1.1.1 inside the input gating.

Open GatedDLatch in the simulator

Source and tests

Test Coverage
GatedDLatchTest / gatedDLatchSpec() 12 checkpoints: reset, disabled retention, enabled data changes, re-enable, and unknown/high-impedance data with reset recovery

3.1.1.3. DFlipFlop

DFlipFlop combines two gated D latches to capture data on the rising clock edge. Unlike GatedDLatch, it does not keep following D throughout a high clock level.

IO pins

Pin Direction Width Description
D Input 1 Data to capture on a rising edge
CLK Input 1 Clock; capture occurs on 0 → 1
RST Input 1 Active-high asynchronous reset of both internal latches
Q Output 1 Captured bit
Q_BAR Output 1 Complement of Q in a valid, settled state

Behavior

RST Clock event Next settled Q
1 Any or none 0
0 Rising edge D prepared before the edge
0 Falling edge Previous Q
0 No edge Previous Q

The component has no write-enable pin. Every valid rising edge captures data. Changing D while CLK stays high must not be treated as another write.

C++ example

struct DFlipFlopReference {
    bool q = false;
    bool previous_clk = false;

    void update(bool d, bool clk, bool rst) {
        if (rst) q = false;
        else if (!previous_clk && clk) q = d;
        previous_clk = clk;
    }

    bool q_bar() const { return !q; }
};

Unknown inputs and timing

The recorded sequence captures X and Z data as unknown state and then recovers through reset. This does not establish analog setup/hold limits or define every ambiguous clock transition. Preparing data, allowing it to settle with CLK = 0, and then raising the clock is the sequence used by the tests.

Implementation

Structural implementation

Internal component Count Function
NOTGate 1 NOT_CLK produces the master's enable
GatedDLatch 2 MASTER receives D; SLAVE receives MASTER.Q and drives the outputs
  • While CLK = 0, MASTER is enabled and SLAVE holds its output.
  • At the rising edge, MASTER closes and SLAVE opens, transferring the prepared value to Q.
  • While CLK = 1, MASTER holds its value, so later input changes do not pass through to Q.
  • At the falling edge, SLAVE closes and MASTER starts preparing the next value.

RST connects to both latches. The clock inverter and latch gates have propagation delays; this is a structural digital model, not an ideal zero-delay edge detector.

Examples

All 14 recorded rows of DFlipFlopTest, in checkpoint order. Outputs are test expectations verified against the simulation. Time is the absolute settled checkpoint time in simulator units, not milliseconds or CPU cycles.

Default scenario profile: canonical-default-preview / structural (4b69afe2687c9499). Changing fidelity or the circuit can change these times.

Checkpointzero-based number · timeline label Timesimulator units CLKInput · 1 bit DInput · 1 bit RSTInput · 1 bit QExpected output · 1 bit Q_BARExpected output · 1 bit
#0reset6 0 0 1 0 1
#1release-reset10 0 0 0 0 1
#2prepare-one16 0 1 0 0 1
#3capture-one22 1 1 0 1 0
#4ignore-high-data-change25 1 0 0 1 0
#5falling-edge33 0 0 0 1 0
#6capture-zero39 1 0 0 0 1
#7open-master44 0 0 0 0 1
#8prepare-unknown50 0 X 0 0 1
#9capture-unknown56 1 X 0 X X
#10reset-unknown64 1 X 1 0 1
#11prepare-high-z71 0 Z 0 0 1
#12capture-high-z77 1 Z 0 X X
#13reset-high-z85 1 Z 1 0 1

Scroll within the table for all rows and pins. Buses use hexadecimal; values containing X/Z use MSB-first binary. Inputs show the declared values in effect at each checkpoint, including retained inputs. Sequential tests also replay preparation changes between checkpoints, such as lowering the clock. Use the matching checkpoint label in the simulation below.

capture-one records a rising-edge capture. ignore-high-data-change and falling-edge retain that value. Only capture-zero, the next rising edge, changes Q to the prepared zero.

Embedded simulation

DFlipFlopTest has 14 checkpoints. Inspect MASTER.Q and SLAVE.Q around prepare-one and capture-one to see the distinction between preparing a value internally and changing the external output.

Open DFlipFlop in the simulator

Source and tests

Test Coverage
DFlipFlopTest / dFlipFlopSpec() 14 checkpoints: reset, input preparation, rising-edge capture, high-level and falling-edge retention, and X/Z capture with reset recovery

3.1.1.4. FlipFlopCell

FlipFlopCell is a one-bit, write-enabled register. It combines DFlipFlop with a multiplexer that chooses between new data and the currently stored bit. FFWord<W> groups these cells into independently enabled byte lanes.

IO pins

Pin Direction Width Description
D Input 1 New data to capture when writing
WE Input 1 Active-high write enable
CLK Input 1 Clock; an enabled write occurs on 0 → 1
RST Input 1 Active-high asynchronous reset
Q Output 1 Stored bit; this component does not expose Q_BAR

Behavior

RST Clock event WE Next settled Q
1 Any or none Any 0
0 Rising edge 1 D prepared before the edge
0 Rising edge 0 Previous Q
0 No rising edge Any Previous Q

Reset has priority over writing. WE = 1 alone does not write: a rising edge is also required. The cell has no address input, read enable, refresh operation, or completion signal. Its stored value is continuously available at Q, subject to propagation delays.

C++ example

struct FlipFlopCellReference {
    bool q = false;
    bool previous_clk = false;

    void update(bool d, bool we, bool clk, bool rst) {
        if (rst) q = false;
        else if (!previous_clk && clk && we) q = d;
        previous_clk = clk;
    }
};

Unknown inputs and timing

The recorded contract includes the following cases in both registered implementations:

  • An enabled rising edge captures D = X or D = Z as Q = X.
  • Asserting reset recovers a known zero from unknown state.
  • With a stored zero and no clock edge, an uncertain reset preserves zero in the tested sequence.
  • On a rising edge with WE = X, a stored zero and D = 0 preserve zero; a stored zero and D = 1 produce unknown state.

These are tested sequences, not a claim of exhaustive equivalence for arbitrary unknown clocks, simultaneous input changes, or all four-state histories. The behavioral form uses delay tables characterized from the gates. In the compared sequences it does not publish earlier than the structural form, although table-only timing can be slower.

Implementation

Structural implementation

Internal component Count Connection
Mux2to1 1 WRITE_MUX.A = STATE.Q, WRITE_MUX.B = D, WRITE_MUX.SEL = WE
DFlipFlop 1 STATE.D = WRITE_MUX.OUT; receives CLK and RST; drives Q

For binary inputs, the data presented to the internal flip-flop is

\[ D_{\mathrm{STATE}} = \begin{cases} Q, & WE = 0, \\ D, & WE = 1. \end{cases} \]

The clock is not gated by WE. The flip-flop still captures on a disabled rising edge, but it captures its own previous value through the feedback path.

Behavioral implementation

FlipFlopCellBehavioral stores one LogicValue and the preceding clock value, both initially unknown. It retains the FlipFlopCell public type and pins without internal gates. When built through the catalog, Q uses gate-derived clock-to-output timing rather than a fixed one-tick publication delay.

Each evaluation gives reset priority. With reset low, a known CLK = 0 → 1 transition captures D when WE = 1 and retains the stored value when WE = 0. Captured X or Z becomes X. On that edge, an unknown write enable follows the structural AND/OR feedback mux: only an old zero and a new zero preserve zero; every other combination produces X, including two ones. An unknown reset merges the stored value with zero. The evaluator remembers the current clock after every evaluation.

FlipFlopCellTest runs the same state sequence against the structural and behavioral implementations and compares their checkpoint outputs. This validates the recorded sequences, not every possible four-state history. Checkpoint times can differ, so the table below describes the default structural run.

Examples

All 13 recorded rows of FlipFlopCellTest, in checkpoint order. Outputs are test expectations verified against the simulation. Time is the absolute settled checkpoint time in simulator units, not milliseconds or CPU cycles.

Default scenario profile: canonical-default-preview / structural (452b10db460aab11). Changing fidelity or the circuit can change these times.

Checkpointzero-based number · timeline label Timesimulator units CLKInput · 1 bit DInput · 1 bit RSTInput · 1 bit WEInput · 1 bit QExpected output · 1 bit
#0reset9 0 0 1 0 0
#1hold-disabled-edge20 1 1 0 0 0
#2write-one39 1 1 0 1 1
#3no-level-capture44 1 0 0 1 1
#4write-zero58 1 0 0 1 0
#5hold-zero71 1 1 0 0 0
#6capture-unknown-data90 1 X 0 1 X
#7reset-clears-unknown102 0 X 1 1 0
#8unknown-reset-preserves-zero107 0 X X 1 0
#9unknown-write-enable-agrees126 1 0 0 X 0
#10unknown-write-enable145 1 1 0 X X
#11reset-before-high-z157 0 1 1 X 0
#12capture-high-z-as-unknown168 1 Z 0 1 X

Scroll within the table for all rows and pins. Buses use hexadecimal; values containing X/Z use MSB-first binary. Inputs show the declared values in effect at each checkpoint, including retained inputs. Sequential tests also replay preparation changes between checkpoints, such as lowering the clock. Use the matching checkpoint label in the simulation below.

hold-disabled-edge and write-one contrast the two write-enable values. no-level-capture changes D while the clock remains high and confirms that the stored one is retained. The test lowers the clock between enabled captures even when that preparation does not have a separate checkpoint.

Embedded simulation

FlipFlopCellTest has 13 checkpoints covering reset, write, hold, and selected unknown-input cases. Expand STATE to follow the complete construction back through the two gated latches and their SR latches.

Open FlipFlopCell in the simulator

Source and tests

Test Coverage
FlipFlopCellTest / flipFlopCellSpec() 13 checkpoints run against structural and behavioral implementations: reset, enabled capture, disabled retention, no high-level capture, and selected X/Z cases

3.1.1.5. FFWord<W>

FFWord<W> stores one word using W instances of FlipFlopCell. All cells share the clock and reset. Byte write enables decide which groups of eight cells capture new data. There is no address input because this component stores only one word.

IO pins

Pin Direction Width Description
WRITE_DATA Input W New word; bits 7:0 are the lowest byte
WRITE_MASK Input W/8 Bit b enables writing byte b
WE Input 1 Enables the masked write on a rising edge
CLK Input 1 Rising-edge write clock
RST Input 1 Active-high asynchronous clear of every bit
Q Output W Continuously available stored word

Behavior

For byte b, the cell write enable is WE AND WRITE_MASK[b].

Condition Next stored byte b
RST = 1 Zero, regardless of clock, mask, or WE
RST = 0, rising edge, WE = 1, mask bit b = 1 Byte b of WRITE_DATA
Otherwise Previous byte b

WRITE_MASK = 0 preserves the whole word. A high clock level is not another edge. For FFWord<32>, mask 0b0101 updates bytes 0 and 2 and retains bytes 1 and 3. Masking never packs or shifts the input bytes.

C++ example

#include <cstddef>
#include <cstdint>

template<std::size_t W>
struct FFWordReference {
    static_assert(W == 8 || W == 16 || W == 32);
    std::uint32_t q = 0;
    bool previous_clk = false;

    void update(std::uint32_t data, std::uint8_t mask,
                bool we, bool clk, bool rst) {
        if (rst) q = 0;
        else if (!previous_clk && clk && we) {
            for (std::size_t b = 0; b < W / 8; ++b) {
                const auto lane = std::uint32_t{0xff} << (8 * b);
                if (mask & (1u << b))
                    q = (q & ~lane) | (data & lane);
            }
        }
        previous_clk = clk;
    }
};

Only the lowest W data bits and W/8 mask bits participate, matching the hardware pin widths. Unlike the simulator's unknown power-up state, this binary example starts after reset.

Unknown inputs and timing

Structural words inherit each cell's unknown-state behavior and gate delays. The behavioral word also starts unknown and stores captured X/Z data as X. Reset restores known zero. FFWordContractTest checks unknown startup and captured unknown data as well as binary operation.

The structural/behavioral equivalence guarantee is for known-binary controls at settled checkpoints, with data and mask prepared before the edge. It does not promise identical transients or exhaustive agreement for uncertain clocks and controls.

Implementation

Structural implementation

Internal component Count Responsibility
BitSplitter<W> 1 DATA exposes individual write-data bits
BitSplitter<W/8> 1 MASK exposes byte enable bits
ANDGate W/8 BYTE_b combines each mask bit with WE
FlipFlopCell W CELL_b stores each bit; created through its selectable family
BitJoiner<W> 1 OUTPUT packs the stored bits into Q
ConstantValue<1> 1 ZERO is a retained compatibility node; it does not drive FF storage

Input splitters have their existing one-tick delay. Output packing is zero-delay wiring. This grouping adds no pipeline stage. Structural words may contain behavioral cells without changing the word's pins.

Behavioral implementation

FFWordBehavioral<W> stores W logic values and the previous clock, initially unknown. Reset clears all bits; an enabled known rising edge replaces the enabled byte lanes. It has no child cells; a catalog-selected word schedules Q using the delay table characterized from its structural counterpart.

When neither the write enable nor the byte mask definitely disables a lane, an ambiguous enable follows the cell's AND/OR feedback mux: only old zero/new zero stays zero; every other combination becomes X. Unknown reset merges each stored bit with zero. Equivalence is checked at settled checkpoints, not during gate propagation.

Examples

All 30 recorded rows of FFWordTest, in checkpoint order. Outputs are test expectations verified against the simulation. Time is the absolute settled checkpoint time in simulator units, not milliseconds or CPU cycles.

Default scenario profile: canonical-default-preview / structural (1be28d8115a568f0). Changing fidelity or the circuit can change these times.

Checkpointzero-based number · timeline label Timesimulator units CLKInput · 1 bit RSTInput · 1 bit WEInput · 1 bit WRITE_DATAInput · 16 bit WRITE_MASKInput · 2 bit QExpected output · 16 bit
#0reset9 0 1 0 0x0000 0x0 0x0000
#1release-reset13 0 0 0 0x0000 0x0 0x0000
#2mask-0-setup16 0 0 0 0x3C81 0x0 0x0000
#3mask-0-enable18 0 0 1 0x3C81 0x0 0x0000
#4mask-0-write-edge23 1 0 1 0x3C81 0x0 0x0000
#5mask-0-high-is-not-edge26 1 0 1 0xC37E 0x0 0x0000
#6mask-0-falling-is-not-write31 0 0 0 0xC37E 0x0 0x0000
#7mask-0-disabled-edge36 1 0 0 0xC37E 0x0 0x0000
#8mask-1-setup41 0 0 0 0x0FC4 0x1 0x0000
#9mask-1-enable50 0 0 1 0x0FC4 0x1 0x0000
#10mask-1-write-edge56 1 0 1 0x0FC4 0x1 0x00C4
#11mask-1-high-is-not-edge62 1 0 1 0xF03B 0x1 0x00C4
#12mask-1-falling-is-not-write73 0 0 0 0xF03B 0x1 0x00C4
#13mask-1-disabled-edge78 1 0 0 0xF03B 0x1 0x00C4
#14mask-2-setup83 0 0 0 0x5A0B 0x2 0x00C4
#15mask-2-enable92 0 0 1 0x5A0B 0x2 0x00C4
#16mask-2-write-edge98 1 0 1 0x5A0B 0x2 0x5AC4
#17mask-2-high-is-not-edge104 1 0 1 0xA5F4 0x2 0x5AC4
#18mask-2-falling-is-not-write115 0 0 0 0xA5F4 0x2 0x5AC4
#19mask-2-disabled-edge120 1 0 0 0xA5F4 0x2 0x5AC4
#20mask-3-setup125 0 0 0 0xA54E 0x3 0x5AC4
#21mask-3-enable137 0 0 1 0xA54E 0x3 0x5AC4
#22mask-3-write-edge143 1 0 1 0xA54E 0x3 0xA54E
#23mask-3-high-is-not-edge149 1 0 1 0x5AB1 0x3 0xA54E
#24mask-3-falling-is-not-write160 0 0 0 0x5AB1 0x3 0xA54E
#25mask-3-disabled-edge165 1 0 0 0x5AB1 0x3 0xA54E
#26asynchronous-reset173 1 1 1 0x5AB1 0x3 0x0000
#27reset-priority-low177 0 1 1 0x5AB1 0x3 0x0000
#28reset-priority-edge181 1 1 1 0x5AB1 0x3 0x0000
#29release-without-edge185 1 0 1 0x5AB1 0x3 0x0000

Scroll within the table for all rows and pins. Buses use hexadecimal; values containing X/Z use MSB-first binary. Inputs show the declared values in effect at each checkpoint, including retained inputs. Sequential tests also replay preparation changes between checkpoints, such as lowering the clock. Use the matching checkpoint label in the simulation below.

The embedded word is 16 bits, so its four masks are 00, 01, 10, and 11. mask-1-write-edge writes only the low byte (Q = 0x00C4); mask-2-write-edge changes the upper byte while retaining that low byte (Q = 0x5AC4). The high-is-not-edge and disabled-edge checkpoints retain each result. The final reset checkpoints clear both bytes, including while writing is enabled.

Embedded simulation

FFWordTest has 30 checkpoints. Inspect BYTE_0, BYTE_1, and the corresponding cell groups. The explorer can select behavioral fidelity for the word or individual cells; the table records the default structural run.

Open FFWord in the simulator

Source and tests

Test Coverage
FFWordTest / wordSpec() 30 checkpoints for a 16-bit word, covering all four byte masks, hold behavior, and reset
FFWordContractTest All masks at widths 8/16/32; structural and behavioral words; structural words containing behavioral cells; unknown startup and captured unknown data

3.1.1.6. FFStorageBank<W>

FFStorageBank<W> groups N words. It distributes common write data, byte mask, clock, and reset, while keeping a separate write enable and output for each word. It does not decode an address, choose a read word, or implement a bus protocol. Here, “bank” is a storage grouping, not a DRAM command/timing model.

IO pins

Pin Direction Width Description
WRITE_DATA Input W Shared data presented to every word
WRITE_MASK Input W/8 Shared byte write mask
CLK Input 1 Shared rising-edge write clock
RST Input 1 Asynchronously clears every word
WRITE_n Input 1 each Write enable for word n, for n = 0 through N−1
WORD_n Output W each Current contents of word n, for n = 0 through N−1

N is a construction-time word count, not another template parameter. The family accepts depth_words (default 4), with 1 <= N <= 4096 / W in both implementations. Structural storage creates exactly N words, including for non-power-of-two depths. The behavioral bank removes those child components but retains every WRITE_n and WORD_n pin; it is not an unlimited-capacity addressable memory.

Behavior

Each word follows §3.1.1.5 with WE = WRITE_n. A rising edge updates the enabled byte lanes of every word whose WRITE_n = 1; all other lanes hold. All WORD_n outputs are continuously available.

The bank does not enforce a one-hot write selection. Multiple high enables would write the same input data to multiple words. In FFMemory, address decoding and access control produce at most one high word enable for a known valid address. Reset clears every word regardless of those enables.

C++ example

#include <vector>

template<std::size_t W>
struct FFStorageBankReference {
    std::vector<FFWordReference<W>> words;

    explicit FFStorageBankReference(std::size_t count) {
        if (count == 0 || count > 4096 / W)
            throw std::invalid_argument("Invalid bank depth");
        words.resize(count);
    }

    void update(std::uint32_t data, std::uint8_t mask,
                const std::vector<bool>& write, bool clk, bool rst) {
        if (write.size() != words.size())
            throw std::invalid_argument("One write enable per word required");
        for (std::size_t n = 0; n < words.size(); ++n)
            words[n].update(data, mask, write[n], clk, rst);
    }
};

words[n].q corresponds to WORD_n. The exceptions guard construction and the example's vector representation; the simulator exposes a fixed set of pins after construction.

Unknown inputs and timing

The structural bank adds no scheduling algorithm of its own: unknown-state behavior and capture timing belong to its words and their selected implementations. Common clock/reset fan out to all words. The behavioral bank starts with unknown bits and clock, captures X/Z data as X, and uses gate-derived output and clock-to-output delays when selected through the catalog.

Equivalence is defined for settled checkpoints with known-binary controls and data/masks/enables prepared before the rising edge. Tests also cover unknown startup, selected-word X capture, unselected-word retention, and reset recovery. They do not establish identical gate transients or exhaustive equivalence for uncertain clocks and controls.

Implementation

Structural implementation

Internal component Count Responsibility
FFWord<W> N WORD_0 through WORD_(N−1) hold the words

Each child receives the shared inputs, its own WRITE_n, and drives its matching output. Words are created through their registered family, so each word can be structural or behavioral.

Behavioral implementation

FFStorageBankBehavioral<W> stores N arrays of W logic values and one previous clock value, without child components. On a known rising edge it independently applies each word's enable and the shared byte mask. Multiple enabled words capture the same positioned data; the implementation does not reduce the enable pins to a single selected index.

Its uncertain-control rules match the behavioral word: for a lane that is not definitely disabled, ambiguous write enables or mask bits preserve zero only for old zero/new zero, following the cell's gate mux. Other combinations become X. Unknown reset merges stored bits with zero. A definite reset clears every word regardless of enable or mask.

The registered contract is memory.ff.bank.W. Its pin schema is generated from depth_words, so construction validates all N enables and outputs for both implementations. Selecting behavioral fidelity for FFMemory.STORAGE hides only the bank's words and cells; ADDRESS_DECODE, ACCESS_CONTROL, and READ_SELECT remain structural and visible.

Examples

All 134 recorded rows of FFStorageBankTest, in checkpoint order. Outputs are test expectations verified against the simulation. Time is the absolute settled checkpoint time in simulator units, not milliseconds or CPU cycles.

Default scenario profile: canonical-default-preview / structural (091f63bdebf4ca14). Changing fidelity or the circuit can change these times.

Checkpointzero-based number · timeline label Timesimulator units CLKInput · 1 bit RSTInput · 1 bit WRITE_0Input · 1 bit WRITE_1Input · 1 bit WRITE_2Input · 1 bit WRITE_DATAInput · 16 bit WRITE_MASKInput · 2 bit WORD_0Expected output · 16 bit WORD_1Expected output · 16 bit WORD_2Expected output · 16 bit
#0reset9 0 1 0 0 0 0x0000 0x0 0x0000 0x0000 0x0000
#1release-reset13 0 0 0 0 0 0x0000 0x0 0x0000 0x0000 0x0000
#2select-0-mask-0-setup16 0 0 0 0 0 0x3C81 0x0 0x0000 0x0000 0x0000
#3select-0-mask-0-write-edge21 1 0 0 0 0 0x3C81 0x0 0x0000 0x0000 0x0000
#4select-0-mask-0-high-is-not-edge24 1 0 0 0 0 0xC37E 0x0 0x0000 0x0000 0x0000
#5select-0-mask-0-falling-is-not-write29 0 0 0 0 0 0xC37E 0x0 0x0000 0x0000 0x0000
#6select-0-mask-1-setup32 0 0 0 0 0 0x3889 0x1 0x0000 0x0000 0x0000
#7select-0-mask-1-write-edge37 1 0 0 0 0 0x3889 0x1 0x0000 0x0000 0x0000
#8select-0-mask-1-high-is-not-edge40 1 0 0 0 0 0xC776 0x1 0x0000 0x0000 0x0000
#9select-0-mask-1-falling-is-not-write45 0 0 0 0 0 0xC776 0x1 0x0000 0x0000 0x0000
#10select-0-mask-2-setup48 0 0 0 0 0 0x3491 0x2 0x0000 0x0000 0x0000
#11select-0-mask-2-write-edge53 1 0 0 0 0 0x3491 0x2 0x0000 0x0000 0x0000
#12select-0-mask-2-high-is-not-edge56 1 0 0 0 0 0xCB6E 0x2 0x0000 0x0000 0x0000
#13select-0-mask-2-falling-is-not-write61 0 0 0 0 0 0xCB6E 0x2 0x0000 0x0000 0x0000
#14select-0-mask-3-setup64 0 0 0 0 0 0x3099 0x3 0x0000 0x0000 0x0000
#15select-0-mask-3-write-edge69 1 0 0 0 0 0x3099 0x3 0x0000 0x0000 0x0000
#16select-0-mask-3-high-is-not-edge72 1 0 0 0 0 0xCF66 0x3 0x0000 0x0000 0x0000
#17select-0-mask-3-falling-is-not-write77 0 0 0 0 0 0xCF66 0x3 0x0000 0x0000 0x0000
#18select-1-mask-0-setup89 0 0 1 0 0 0x0FC4 0x0 0x0000 0x0000 0x0000
#19select-1-mask-0-write-edge94 1 0 1 0 0 0x0FC4 0x0 0x0000 0x0000 0x0000
#20select-1-mask-0-high-is-not-edge97 1 0 1 0 0 0xF03B 0x0 0x0000 0x0000 0x0000
#21select-1-mask-0-falling-is-not-write102 0 0 1 0 0 0xF03B 0x0 0x0000 0x0000 0x0000
#22select-1-mask-1-setup112 0 0 1 0 0 0x0BCC 0x1 0x0000 0x0000 0x0000
#23select-1-mask-1-write-edge118 1 0 1 0 0 0x0BCC 0x1 0x00CC 0x0000 0x0000
#24select-1-mask-1-high-is-not-edge124 1 0 1 0 0 0xF433 0x1 0x00CC 0x0000 0x0000
#25select-1-mask-1-falling-is-not-write132 0 0 1 0 0 0xF433 0x1 0x00CC 0x0000 0x0000
#26select-1-mask-2-setup144 0 0 1 0 0 0x07D4 0x2 0x00CC 0x0000 0x0000
#27select-1-mask-2-write-edge150 1 0 1 0 0 0x07D4 0x2 0x07CC 0x0000 0x0000
#28select-1-mask-2-high-is-not-edge156 1 0 1 0 0 0xF82B 0x2 0x07CC 0x0000 0x0000
#29select-1-mask-2-falling-is-not-write164 0 0 1 0 0 0xF82B 0x2 0x07CC 0x0000 0x0000
#30select-1-mask-3-setup175 0 0 1 0 0 0x03DC 0x3 0x07CC 0x0000 0x0000
#31select-1-mask-3-write-edge181 1 0 1 0 0 0x03DC 0x3 0x03DC 0x0000 0x0000
#32select-1-mask-3-high-is-not-edge187 1 0 1 0 0 0xFC23 0x3 0x03DC 0x0000 0x0000
#33select-1-mask-3-falling-is-not-write195 0 0 1 0 0 0xFC23 0x3 0x03DC 0x0000 0x0000
#34select-2-mask-0-setup207 0 0 0 1 0 0x5A0B 0x0 0x03DC 0x0000 0x0000
#35select-2-mask-0-write-edge212 1 0 0 1 0 0x5A0B 0x0 0x03DC 0x0000 0x0000
#36select-2-mask-0-high-is-not-edge215 1 0 0 1 0 0xA5F4 0x0 0x03DC 0x0000 0x0000
#37select-2-mask-0-falling-is-not-write220 0 0 0 1 0 0xA5F4 0x0 0x03DC 0x0000 0x0000
#38select-2-mask-1-setup230 0 0 0 1 0 0x5E03 0x1 0x03DC 0x0000 0x0000
#39select-2-mask-1-write-edge236 1 0 0 1 0 0x5E03 0x1 0x03DC 0x0003 0x0000
#40select-2-mask-1-high-is-not-edge242 1 0 0 1 0 0xA1FC 0x1 0x03DC 0x0003 0x0000
#41select-2-mask-1-falling-is-not-write250 0 0 0 1 0 0xA1FC 0x1 0x03DC 0x0003 0x0000
#42select-2-mask-2-setup262 0 0 0 1 0 0x521B 0x2 0x03DC 0x0003 0x0000
#43select-2-mask-2-write-edge268 1 0 0 1 0 0x521B 0x2 0x03DC 0x5203 0x0000
#44select-2-mask-2-high-is-not-edge274 1 0 0 1 0 0xADE4 0x2 0x03DC 0x5203 0x0000
#45select-2-mask-2-falling-is-not-write282 0 0 0 1 0 0xADE4 0x2 0x03DC 0x5203 0x0000
#46select-2-mask-3-setup293 0 0 0 1 0 0x5613 0x3 0x03DC 0x5203 0x0000
#47select-2-mask-3-write-edge299 1 0 0 1 0 0x5613 0x3 0x03DC 0x5613 0x0000
#48select-2-mask-3-high-is-not-edge305 1 0 0 1 0 0xA9EC 0x3 0x03DC 0x5613 0x0000
#49select-2-mask-3-falling-is-not-write313 0 0 0 1 0 0xA9EC 0x3 0x03DC 0x5613 0x0000
#50select-3-mask-0-setup325 0 0 1 1 0 0xA54E 0x0 0x03DC 0x5613 0x0000
#51select-3-mask-0-write-edge330 1 0 1 1 0 0xA54E 0x0 0x03DC 0x5613 0x0000
#52select-3-mask-0-high-is-not-edge333 1 0 1 1 0 0x5AB1 0x0 0x03DC 0x5613 0x0000
#53select-3-mask-0-falling-is-not-write338 0 0 1 1 0 0x5AB1 0x0 0x03DC 0x5613 0x0000
#54select-3-mask-1-setup351 0 0 1 1 0 0xA146 0x1 0x03DC 0x5613 0x0000
#55select-3-mask-1-write-edge357 1 0 1 1 0 0xA146 0x1 0x0346 0x5646 0x0000
#56select-3-mask-1-high-is-not-edge363 1 0 1 1 0 0x5EB9 0x1 0x0346 0x5646 0x0000
#57select-3-mask-1-falling-is-not-write371 0 0 1 1 0 0x5EB9 0x1 0x0346 0x5646 0x0000
#58select-3-mask-2-setup384 0 0 1 1 0 0xAD5E 0x2 0x0346 0x5646 0x0000
#59select-3-mask-2-write-edge390 1 0 1 1 0 0xAD5E 0x2 0xAD46 0xAD46 0x0000
#60select-3-mask-2-high-is-not-edge396 1 0 1 1 0 0x52A1 0x2 0xAD46 0xAD46 0x0000
#61select-3-mask-2-falling-is-not-write404 0 0 1 1 0 0x52A1 0x2 0xAD46 0xAD46 0x0000
#62select-3-mask-3-setup415 0 0 1 1 0 0xA956 0x3 0xAD46 0xAD46 0x0000
#63select-3-mask-3-write-edge421 1 0 1 1 0 0xA956 0x3 0xA956 0xA956 0x0000
#64select-3-mask-3-high-is-not-edge427 1 0 1 1 0 0x56A9 0x3 0xA956 0xA956 0x0000
#65select-3-mask-3-falling-is-not-write435 0 0 1 1 0 0x56A9 0x3 0xA956 0xA956 0x0000
#66select-4-mask-0-setup447 0 0 0 0 1 0xF195 0x0 0xA956 0xA956 0x0000
#67select-4-mask-0-write-edge452 1 0 0 0 1 0xF195 0x0 0xA956 0xA956 0x0000
#68select-4-mask-0-high-is-not-edge455 1 0 0 0 1 0x0E6A 0x0 0xA956 0xA956 0x0000
#69select-4-mask-0-falling-is-not-write460 0 0 0 0 1 0x0E6A 0x0 0xA956 0xA956 0x0000
#70select-4-mask-1-setup470 0 0 0 0 1 0xF59D 0x1 0xA956 0xA956 0x0000
#71select-4-mask-1-write-edge476 1 0 0 0 1 0xF59D 0x1 0xA956 0xA956 0x009D
#72select-4-mask-1-high-is-not-edge482 1 0 0 0 1 0x0A62 0x1 0xA956 0xA956 0x009D
#73select-4-mask-1-falling-is-not-write490 0 0 0 0 1 0x0A62 0x1 0xA956 0xA956 0x009D
#74select-4-mask-2-setup501 0 0 0 0 1 0xF985 0x2 0xA956 0xA956 0x009D
#75select-4-mask-2-write-edge507 1 0 0 0 1 0xF985 0x2 0xA956 0xA956 0xF99D
#76select-4-mask-2-high-is-not-edge513 1 0 0 0 1 0x067A 0x2 0xA956 0xA956 0xF99D
#77select-4-mask-2-falling-is-not-write521 0 0 0 0 1 0x067A 0x2 0xA956 0xA956 0xF99D
#78select-4-mask-3-setup534 0 0 0 0 1 0xFD8D 0x3 0xA956 0xA956 0xF99D
#79select-4-mask-3-write-edge540 1 0 0 0 1 0xFD8D 0x3 0xA956 0xA956 0xFD8D
#80select-4-mask-3-high-is-not-edge546 1 0 0 0 1 0x0272 0x3 0xA956 0xA956 0xFD8D
#81select-4-mask-3-falling-is-not-write554 0 0 0 0 1 0x0272 0x3 0xA956 0xA956 0xFD8D
#82select-5-mask-0-setup566 0 0 1 0 1 0x3CD8 0x0 0xA956 0xA956 0xFD8D
#83select-5-mask-0-write-edge571 1 0 1 0 1 0x3CD8 0x0 0xA956 0xA956 0xFD8D
#84select-5-mask-0-high-is-not-edge574 1 0 1 0 1 0xC327 0x0 0xA956 0xA956 0xFD8D
#85select-5-mask-0-falling-is-not-write579 0 0 1 0 1 0xC327 0x0 0xA956 0xA956 0xFD8D
#86select-5-mask-1-setup592 0 0 1 0 1 0x38D0 0x1 0xA956 0xA956 0xFD8D
#87select-5-mask-1-write-edge598 1 0 1 0 1 0x38D0 0x1 0xA9D0 0xA956 0xFDD0
#88select-5-mask-1-high-is-not-edge604 1 0 1 0 1 0xC72F 0x1 0xA9D0 0xA956 0xFDD0
#89select-5-mask-1-falling-is-not-write612 0 0 1 0 1 0xC72F 0x1 0xA9D0 0xA956 0xFDD0
#90select-5-mask-2-setup625 0 0 1 0 1 0x34C8 0x2 0xA9D0 0xA956 0xFDD0
#91select-5-mask-2-write-edge631 1 0 1 0 1 0x34C8 0x2 0x34D0 0xA956 0x34D0
#92select-5-mask-2-high-is-not-edge637 1 0 1 0 1 0xCB37 0x2 0x34D0 0xA956 0x34D0
#93select-5-mask-2-falling-is-not-write645 0 0 1 0 1 0xCB37 0x2 0x34D0 0xA956 0x34D0
#94select-5-mask-3-setup658 0 0 1 0 1 0x30C0 0x3 0x34D0 0xA956 0x34D0
#95select-5-mask-3-write-edge664 1 0 1 0 1 0x30C0 0x3 0x30C0 0xA956 0x30C0
#96select-5-mask-3-high-is-not-edge670 1 0 1 0 1 0xCF3F 0x3 0x30C0 0xA956 0x30C0
#97select-5-mask-3-falling-is-not-write678 0 0 1 0 1 0xCF3F 0x3 0x30C0 0xA956 0x30C0
#98select-6-mask-0-setup690 0 0 0 1 1 0x0F1F 0x0 0x30C0 0xA956 0x30C0
#99select-6-mask-0-write-edge695 1 0 0 1 1 0x0F1F 0x0 0x30C0 0xA956 0x30C0
#100select-6-mask-0-high-is-not-edge698 1 0 0 1 1 0xF0E0 0x0 0x30C0 0xA956 0x30C0
#101select-6-mask-0-falling-is-not-write703 0 0 0 1 1 0xF0E0 0x0 0x30C0 0xA956 0x30C0
#102select-6-mask-1-setup716 0 0 0 1 1 0x0B17 0x1 0x30C0 0xA956 0x30C0
#103select-6-mask-1-write-edge722 1 0 0 1 1 0x0B17 0x1 0x30C0 0xA917 0x3017
#104select-6-mask-1-high-is-not-edge728 1 0 0 1 1 0xF4E8 0x1 0x30C0 0xA917 0x3017
#105select-6-mask-1-falling-is-not-write736 0 0 0 1 1 0xF4E8 0x1 0x30C0 0xA917 0x3017
#106select-6-mask-2-setup749 0 0 0 1 1 0x070F 0x2 0x30C0 0xA917 0x3017
#107select-6-mask-2-write-edge755 1 0 0 1 1 0x070F 0x2 0x30C0 0x0717 0x0717
#108select-6-mask-2-high-is-not-edge761 1 0 0 1 1 0xF8F0 0x2 0x30C0 0x0717 0x0717
#109select-6-mask-2-falling-is-not-write769 0 0 0 1 1 0xF8F0 0x2 0x30C0 0x0717 0x0717
#110select-6-mask-3-setup782 0 0 0 1 1 0x0307 0x3 0x30C0 0x0717 0x0717
#111select-6-mask-3-write-edge788 1 0 0 1 1 0x0307 0x3 0x30C0 0x0307 0x0307
#112select-6-mask-3-high-is-not-edge794 1 0 0 1 1 0xFCF8 0x3 0x30C0 0x0307 0x0307
#113select-6-mask-3-falling-is-not-write802 0 0 0 1 1 0xFCF8 0x3 0x30C0 0x0307 0x0307
#114select-7-mask-0-setup814 0 0 1 1 1 0x5A62 0x0 0x30C0 0x0307 0x0307
#115select-7-mask-0-write-edge819 1 0 1 1 1 0x5A62 0x0 0x30C0 0x0307 0x0307
#116select-7-mask-0-high-is-not-edge822 1 0 1 1 1 0xA59D 0x0 0x30C0 0x0307 0x0307
#117select-7-mask-0-falling-is-not-write827 0 0 1 1 1 0xA59D 0x0 0x30C0 0x0307 0x0307
#118select-7-mask-1-setup840 0 0 1 1 1 0x5E6A 0x1 0x30C0 0x0307 0x0307
#119select-7-mask-1-write-edge846 1 0 1 1 1 0x5E6A 0x1 0x306A 0x036A 0x036A
#120select-7-mask-1-high-is-not-edge852 1 0 1 1 1 0xA195 0x1 0x306A 0x036A 0x036A
#121select-7-mask-1-falling-is-not-write860 0 0 1 1 1 0xA195 0x1 0x306A 0x036A 0x036A
#122select-7-mask-2-setup873 0 0 1 1 1 0x5272 0x2 0x306A 0x036A 0x036A
#123select-7-mask-2-write-edge879 1 0 1 1 1 0x5272 0x2 0x526A 0x526A 0x526A
#124select-7-mask-2-high-is-not-edge885 1 0 1 1 1 0xAD8D 0x2 0x526A 0x526A 0x526A
#125select-7-mask-2-falling-is-not-write893 0 0 1 1 1 0xAD8D 0x2 0x526A 0x526A 0x526A
#126select-7-mask-3-setup904 0 0 1 1 1 0x567A 0x3 0x526A 0x526A 0x526A
#127select-7-mask-3-write-edge910 1 0 1 1 1 0x567A 0x3 0x567A 0x567A 0x567A
#128select-7-mask-3-high-is-not-edge916 1 0 1 1 1 0xA985 0x3 0x567A 0x567A 0x567A
#129select-7-mask-3-falling-is-not-write924 0 0 1 1 1 0xA985 0x3 0x567A 0x567A 0x567A
#130asynchronous-reset931 0 1 1 1 1 0xA985 0x3 0x0000 0x0000 0x0000
#131reset-priority-edge935 1 1 1 1 1 0xA985 0x3 0x0000 0x0000 0x0000
#132release-without-edge939 1 0 1 1 1 0xA985 0x3 0x0000 0x0000 0x0000
#133reset-again943 1 1 1 1 1 0xA985 0x3 0x0000 0x0000 0x0000

Scroll within the table for all rows and pins. Buses use hexadecimal; values containing X/Z use MSB-first binary. Inputs show the declared values in effect at each checkpoint, including retained inputs. Sequential tests also replay preparation changes between checkpoints, such as lowering the clock. Use the matching checkpoint label in the simulation below.

The embedded bank has three 16-bit words. Its 134 checkpoints cover all eight word-enable combinations and all four byte masks, followed by reset checks. Every WORD_n is an asserted root output, including words that should retain their old contents.

In a label such as select-5-mask-1-write-edge, select-5 means binary 101: WRITE_0 and WRITE_2 are high, while WRITE_1 is low. Mask 01 updates only the low byte of words 0 and 2. Word 1 and the upper bytes retain their previous values. Selection 0 writes nothing; selection 7 enables all three words; mask 0 writes no bytes even when words are enabled.

Each selection/mask pair has four checkpoints: setup prepares inputs with a low clock, write-edge captures enabled bytes, high-is-not-edge changes the data without another capture, and falling-is-not-write lowers the clock without writing. The final four checkpoints check asynchronous reset, reset priority at an edge, release without an edge, and another reset.

Embedded simulation

FFStorageBankTest opens the bank itself as FF_STORAGE_BANK_ROOT, displayed as FFStorageBank<16,3>. Expand WORD_0 to inspect its cells. The explorer can select behavioral fidelity for the entire bank, individual words, or individual cells. The table records the default structural profile; alternative fidelities can have different checkpoint times.

Open FFStorageBank in the simulator

Source and tests

Test Coverage
FFStorageBankTest / bankSpec() 134 standalone checkpoints for three 16-bit words, all word-enable combinations and byte masks, retention, and reset
FFStorageBankContractTest Widths 8/16/32 and depths 1/3/7; independent and simultaneous writes; all byte masks; structural/behavioral and mixed-word/cell equivalence; selected unknown cases; depth bounds and exact parameterized pin validation
FFMemoryFidelityTest Bank-level behavioral selection inside structural devices, alongside word/cell/device selections
DocumentationExamplesTest Compiles the binary bank example, replays every standalone bank row, and also checks internal bank signals in the complete memory scenario

3.1.1.7. FFMemory<W>

FFMemory<W> is the complete addressable FF device. It adds one shared word address, access checking, and a read multiplexer to FFStorageBank<W>. The structural implementation exposes those blocks; the behavioral implementation keeps the same device pins without expanding words or cells. Its decoder and read selector reuse the gate-construction routines described in 3.1.0. Helper Components, without changing their FF-specific pins, range checks, or zero-on-disabled-read behavior.

IO pins

Pin Direction Width Description
WORD_ADDR Input 32 Word index, not a byte address
WRITE_DATA Input W Positioned bytes to write into the selected word
WRITE_MASK Input W/8 Bit b enables byte b of that word
WE Input 1 Enables writing on a rising clock edge
READ_EN Input 1 Enables the asynchronous read output
CLK Input 1 Rising-edge write clock
RST Input 1 Active-high asynchronous clear of all stored words
READ_DATA Output W Selected word, or zero for a disabled/known invalid read
FAULT Output 1 Active read/write address is outside the configured depth

There is no READY pin or request/response handshake. The caller must allow propagation to settle. Reads and writes use the same address.

Behavior

For known binary controls, let in_range = WORD_ADDR < depth_words.

Operation Result
READ_EN = 0 READ_DATA = 0; contents are unchanged by disabling reads
READ_EN = 1, in range READ_DATA follows the selected stored word after propagation, without a clock edge
READ_EN = 1, out of range READ_DATA = 0, FAULT = 1
Rising edge, WE = 1, in range, RST = 0 Write the mask-enabled bytes of the selected word
WE = 1, out of range No word is written; FAULT = 1, even before an edge
READ_EN = WE = 0 FAULT = 0, regardless of address
RST = 1 Clear all words; address/read/fault logic still follows its inputs

In particular, FAULT = (READ_EN OR WE) AND NOT in_range; a zero write mask does not exempt an active invalid address. A valid simultaneous read/write observes the old word before the edge and the updated word after propagation. Reset is a storage clear, not an instruction to force READ_EN, WE, or FAULT low.

Width is fixed by FFMemory<W>; depth is supplied through MemoryConfig. For example, FFMemory<32> with .depth_words = 4 stores 16 bytes, while FFMemory<8> with the same depth stores 4 bytes. Word index 2 identifies the third word in both; its byte offset is 2 * W/8.

Configuration Meaning and limits
depth_words N words; default 4. Non-power-of-two depths are supported. Structural expansion is limited to 4096 cells: at most 512/256/128 words for W = 8/16/32
read_delay_ticks Behavioral output delay in simulator ticks, not CPU cycles, for a device built without a proven delay table. Devices built through the catalog use the table proven on their gates instead. Default 1; structural selection requires 1 but retains its actual gate propagation delays
initial_value Default unknown. Structural selection requires unknown startup; behavioral storage also supports configured initial fill
record_history Controls behavioral backing-store history, not the simulator's separate wire/pin history

Larger memories use the behavioral device rather than expanding every cell. This is a simulator resource limit, not a physical FF capacity claim.

C++ example

template<std::size_t W>
struct FFMemoryReference {
    FFStorageBankReference<W> storage;
    std::uint32_t read_data = 0;
    bool fault = false;

    explicit FFMemoryReference(std::size_t count) : storage(count) {}

    void update(std::uint32_t address, std::uint32_t data,
                std::uint8_t mask, bool we, bool read_en,
                bool clk, bool rst) {
        const bool valid = address < storage.words.size();
        std::vector<bool> write(storage.words.size(), false);
        if (valid && we) write[address] = true;
        storage.update(data, mask, write, clk, rst);
        read_data = read_en && valid ? storage.words[address].q : 0;
        fault = (read_en || we) && !valid;
    }
};

This example exposes the binary composition: decode one write enable, update the bank, then select the read result. It uses the preceding small bank's depth guard. The scalable simulator implementation does not allocate this vector of word components.

Unknown inputs and timing

Unwritten storage is initially X; asserting reset establishes zero. Gate-level propagation determines structural intermediate values. The behavioral device suppresses writes with ambiguous controls, address, or mask; an unknown read enable or enabled unknown address produces unknown read data. Ambiguous active access controls/address/mask can produce unknown FAULT. Captured Z is stored as X; an unknown reset makes the behavioral storage unknown.

The shared equivalence contract is settled binary behavior, not all four-state histories. A catalog-selected behavioral device uses the delay table characterized from the structural decoder, controls, cells, and read muxes; read_delay_ticks applies only without such a table. The table is a per-path contract, and multi-input histories beyond the comparisons are not exhaustively proven. Keep write address, data, mask, and WE stable before the capture edge.

Implementation

Structural implementation

FF memory: bank, word, cell FFMemory contains address and access helpers plus an FFStorageBank of N FFWord instances, each containing W FlipFlopCell instances. FFMemory<W> ADDRESS_DECODE · ACCESS_CONTROL · READ_SELECT STORAGE · FFStorageBank<W> N words WORD_n · FFWord<W> W bits b0 b1 b(W−1) … FlipFlopCell × W · labels are bit positions, not values
N words × W bits. One representative word is expanded. Address decoding and read selection act around the bank; they are not additional storage layers.
Block Inputs → outputs Responsibility
MemoryAddressDecoder WORD_ADDR → IN_RANGE, VALID_n, SELECT_b Validate the full 32-bit index, identify each valid word, and supply read-mux address bits
MemoryAccessControl IN_RANGE, VALID_n, WE, READ_EN → WRITE_n, READ_VALID, FAULT Gate each word write enable, permit valid reads, and flag active out-of-range accesses
FFStorageBank<W> Shared data/mask/clock/reset and WRITE_n → WORD_n Hold exactly N words as specified in §3.1.1.6
MemoryReadSelector<W> WORD_n, SELECT_b, READ_VALID → READ_DATA Select the addressed word through mux trees and gate the result

The decoder checks high address bits as well as the bits used for selection: an invalid index must not alias a valid word. Non-power-of-two depths have no extra storage cells; missing read-mux leaves are zero. A one-word device needs range checking but no word-selection decoder gates or read-selection muxes. These three address/control/read helper blocks are structural only; the bank's word/cell children remain selectable.

Behavioral implementation

FFMemoryBehavioral<W> implements the compact FF device. It uses MemoryStorage for values, known-bit masks, optional history, and setup-time image loading. Writes occur on rising edges and reset clears storage. The public display type remains FFMemory8, FFMemory16, or FFMemory32.

MemoryContainer is the implementation and MemoryStorage is its host-side backing store; they are not extra hardware layers inside the structural bank. Image loading and backing-store inspection are available on the behavioral device. The structural device instead exposes its actual word/cell pins.

Fidelity can be selected at four boundaries: the whole device, its bank, individual words, or individual cells. FFMemoryFidelityTest compares these combinations at settled checkpoints. Collapsing a parent removes its visible children; saved child choices remain available when structural expansion is restored.

Examples

All 52 recorded rows of FFMemoryTest, in checkpoint order. Outputs are test expectations verified against the simulation. Time is the absolute settled checkpoint time in simulator units, not milliseconds or CPU cycles.

Default scenario profile: canonical-default-preview / structural (5627bf74381b9d40). Changing fidelity or the circuit can change these times.

Checkpointzero-based number · timeline label Timesimulator units CLKInput · 1 bit READ_ENInput · 1 bit RSTInput · 1 bit WEInput · 1 bit WORD_ADDRInput · 32 bit WRITE_DATAInput · 8 bit WRITE_MASKInput · 1 bit FAULTExpected output · 1 bit READ_DATAExpected output · 8 bit
#0initial-disabled9 0 0 1 0 0x00000000 0x00 1 0 0x00
#1release-reset13 0 0 0 0 0x00000000 0x00 1 0 0x00
#2setup-016 0 0 0 0 0x00000000 0x81 1 0 0x00
#3write-enable-026 0 0 0 1 0x00000000 0x81 1 0 0x00
#4write-edge-035 1 0 0 1 0x00000000 0x81 1 0 0x00
#5finish-write-046 0 0 0 0 0x00000000 0x81 1 0 0x00
#6read-049 0 1 0 0 0x00000000 0x81 1 0 0x81
#7setup-157 0 0 0 0 0x00000001 0x80 1 0 0x00
#8write-enable-167 0 0 0 1 0x00000001 0x80 1 0 0x00
#9write-edge-176 1 0 0 1 0x00000001 0x80 1 0 0x00
#10finish-write-187 0 0 0 0 0x00000001 0x80 1 0 0x00
#11read-190 0 1 0 0 0x00000001 0x80 1 0 0x80
#12setup-296 0 0 0 0 0x00000002 0x83 1 0 0x00
#13write-enable-2106 0 0 0 1 0x00000002 0x83 1 0 0x00
#14write-edge-2115 1 0 0 1 0x00000002 0x83 1 0 0x00
#15finish-write-2126 0 0 0 0 0x00000002 0x83 1 0 0x00
#16read-2129 0 1 0 0 0x00000002 0x83 1 0 0x83
#17setup-3137 0 0 0 0 0x00000003 0x82 1 0 0x00
#18write-enable-3147 0 0 0 1 0x00000003 0x82 1 0 0x00
#19write-edge-3156 1 0 0 1 0x00000003 0x82 1 0 0x00
#20finish-write-3167 0 0 0 0 0x00000003 0x82 1 0 0x00
#21read-3170 0 1 0 0 0x00000003 0x82 1 0 0x82
#22read-back-3171 0 1 0 0 0x00000003 0x82 1 0 0x82
#23read-back-2179 0 1 0 0 0x00000002 0x82 1 0 0x83
#24read-back-1187 0 1 0 0 0x00000001 0x82 1 0 0x80
#25read-back-0195 0 1 0 0 0x00000000 0x82 1 0 0x81
#26invalid-4-read205 0 1 0 0 0x00000004 0x82 1 1 0x00
#27invalid-4-disabled208 0 0 0 0 0x00000004 0x82 1 0 0x00
#28invalid-4-write-enable211 0 0 0 1 0x00000004 0x00 1 1 0x00
#29invalid-4-write-edge216 1 0 0 1 0x00000004 0x00 1 1 0x00
#30invalid-4-write-end221 0 0 0 0 0x00000004 0x00 1 0 0x00
#31invalid-2147483648-read228 0 1 0 0 0x80000000 0x00 1 1 0x00
#32invalid-2147483648-disabled231 0 0 0 0 0x80000000 0x00 1 0 0x00
#33invalid-2147483648-write-enable234 0 0 0 1 0x80000000 0x00 1 1 0x00
#34invalid-2147483648-write-edge239 1 0 0 1 0x80000000 0x00 1 1 0x00
#35invalid-2147483648-write-end244 0 0 0 0 0x80000000 0x00 1 0 0x00
#36invalid-4294967295-read252 0 1 0 0 0xFFFFFFFF 0x00 1 1 0x00
#37invalid-4294967295-disabled255 0 0 0 0 0xFFFFFFFF 0x00 1 0 0x00
#38invalid-4294967295-write-enable258 0 0 0 1 0xFFFFFFFF 0x00 1 1 0x00
#39invalid-4294967295-write-edge263 1 0 0 1 0xFFFFFFFF 0x00 1 1 0x00
#40invalid-4294967295-write-end268 0 0 0 0 0xFFFFFFFF 0x00 1 0 0x00
#41after-invalid-writes-3278 0 1 0 0 0x00000003 0x00 1 0 0x82
#42after-invalid-writes-2286 0 1 0 0 0x00000002 0x00 1 0 0x83
#43after-invalid-writes-1294 0 1 0 0 0x00000001 0x00 1 0 0x80
#44after-invalid-writes-0302 0 1 0 0 0x00000000 0x00 1 0 0x81
#45disable-after-range-checks305 0 0 0 0 0x00000000 0x00 1 0 0x00
#46masked-setup308 0 0 0 0 0x00000000 0x78 1 0 0x00
#47masked-enable321 0 0 0 1 0x00000000 0x78 1 0 0x00
#48masked-edge331 1 0 0 1 0x00000000 0x78 1 0 0x00
#49masked-end342 0 0 0 0 0x00000000 0x78 1 0 0x00
#50masked-read345 0 1 0 0 0x00000000 0x78 1 0 0x78
#51reset-retention355 0 1 1 0 0x00000000 0x78 1 0 0x00

Scroll within the table for all rows and pins. Buses use hexadecimal; values containing X/Z use MSB-first binary. Inputs show the declared values in effect at each checkpoint, including retained inputs. Sequential tests also replay preparation changes between checkpoints, such as lowering the clock. Use the matching checkpoint label in the simulation below.

The embedded device is 8 bits wide with four words. Its 52 checkpoints write 0x81, 0x80, 0x83, and 0x82, read all words back, attempt out-of-range reads/writes, and verify that rejected writes did not corrupt valid words. masked-read returns 0x78; at W = 8 the one-byte mask covers the whole word, so use §3.1.1.5 for partial-word masking. reset-retention confirms that FF memory clears to zero rather than retaining its contents.

Embedded simulation

Inspect the four root blocks, then open STORAGE to see the bank structure described in §3.1.1.6. This device uses four 8-bit words rather than the standalone bank example's three 16-bit words. Changing WORD_ADDR affects the decoder and read selection, while the stored words remain in place. The full table uses the default structural profile; choosing a behavioral implementation can change checkpoint times.

Open FFMemory in the simulator

Source and tests

Test Coverage
FFMemoryTest The 52 device checkpoints shown above, run against structural and behavioral implementations
FFMemoryContractTest Widths 8/16/32 and depths 1/2/3/4/7/8/16/32/33; masking, addressing, rejected writes, configuration limits, reset, behavioral history/loading, and representative equivalence
FFMemoryFidelityTest Fully structural, behavioral-cell, behavioral-word, and behavioral-device selections; mixed exact-path overrides; large compact devices
FFMemoryBindingsTest Factory/inspector interfaces and unchanged structural output-transition fixtures

This completes the FF storage device. SRAM, DRAM, and flash require their own storage/device behavior; their future implementations need not share the FF cell or word interface.