Skip to content

§ 3.1.3. DRAM

One-bank digital DRAM with explicit activation, a sensed row buffer, restoration, precharge, and refresh. It follows the common device architecture, but does not reuse FF/SRAM cell semantics.

Component Description Structural Behavioral
DRAMCell Charge bit with destructive sampling and restoration Yes No
DRAMRow<C> C cells sharing a wordline Yes Yes
DRAMSenseColumn<R> One row-buffer bit and restoration signal Yes No
DRAMStorageBank<W> Rows connected to shared sense columns Yes Yes
DRAMCommandControl<W> Command latch and timed phase sequencer Yes No
DRAM<W> Complete single-bank device Yes Yes

Real DRAM controllers track open rows, issue precharge/activate commands, and respect device timing. This model keeps those distinctions with abstract ticks, not a pin-compatible DDR interface. See Microchip's SDRAM controller description.

Configuration

W, N, and K follow 3.1. Defaults are W=8 in examples, N=4, K=2.

Parameter Default Meaning / constraints
phase_ticks (P) 32 Internal phase spacing, 32…10,000 ticks
retention_ticks 100,000 Maximum modeled closed-row retention; 1,024…1,000,000,000 and greater than 8P

Storage starts X. Retention is checked lazily at the next activation, avoiding periodic decay events for every cell. A cell closed for at least retention_ticks samples X; refresh after expiry cannot recover its old data. While a row is open, the enabled sense amplifiers restore it. A displayed idle cell Q is not a continuously updated analog charge measurement.

The component references use the same IO, behavior, implementation, examples, and source/test sections as FF and SRAM. Compile the C++ examples in page order: larger references compose the earlier ones. They cover binary controls, stored state, outputs, and operation deadlines, not gate propagation or analog behavior. Construction parameters must satisfy the documented geometry/timing constraints. Every recorded checkpoint appears in a scrollable table.

Configuration is DRAMDeviceConfig. Catalog contracts are memory.dram.cell, memory.dram.row, memory.dram.sense-column, memory.dram.bank.W, memory.dram.command-control.W, and memory.dram.device.W. Standalone rows use columns and retention_ticks; sense columns use rows. Only the public bank/device wrappers are C++ width templates. The command controller is catalog-created, not a public C++ template.

3.1.3.1. DRAMCell

One digital charge bit with destructive sampling and restoration.

IO pins

Pin Direction Width Description
WL Input 1 Wordline; activation samples charge
RESTORE Input 1 Rewrite charge from D while WL is high
D Input 1 Restore/write value
SAMPLE Output 1 Value sampled on activation; held until close
Q Output 1 Modeled cell charge

Behavior

A newly selected cell copies retained charge into SAMPLE and invalidates Q. With WL=RESTORE=1, D restores Q. Closing clears SAMPLE and starts the retention interval; closing without restoration loses the bit. Reading a cell alone is destructive: the bank's sense column supplies restoration.

C++ example

Binary controls with std::optional<bool> representing known charge or X. Retention is tested at activation.

#include <cstddef>
#include <optional>
struct DRAMCellReference {
    std::optional<bool> q, sample;
    bool selected = false, restored = false;
    std::size_t closed_at = 0;
    void update(std::size_t now, std::size_t retention,
                bool wl, bool restore, std::optional<bool> data) {
        if (wl && !selected) {
            sample = now - closed_at >= retention ? std::nullopt : q;
            q.reset();
            restored = false;
        }
        if (wl && restore) { q = data; restored = true; }
        if (!wl && selected) {
            if (!restored) q.reset();
            sample.reset();
            closed_at = now;
            restored = false;
        }
        selected = wl;
    }
};

Unknown inputs and timing

Starts X. Unknown WL makes charge and sample unknown; unknown RESTORE while selected makes charge unknown. D=Z stores X. WL=0 ignores restore/data. retention_ticks accepts 1,024–1,000,000,000. Expiry has no periodic event: the next activation observes loss. Output propagation takes one primitive tick.

Implementation

Structural implementation

Terminal digital charge primitive, not a flip-flop or an RC electrical simulation. Q and SAMPLE make the storage/sensing distinction explicit. No redundant behavioral-fidelity replacement is registered.

The visualizer displays separate charge (Q) and read sample (SAMPLE) panels, with WL/RESTORE indicators, using recorded pin values at the selected time. It does not invent an analog charge level or retention countdown; expiry is checked on activation as described above.

Examples

All 8 recorded rows of DRAMCellTest, 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 (3f3be5125c460b70). Changing fidelity or the circuit can change these times.

Checkpointzero-based number · timeline label Timesimulator units DInput · 1 bit RESTOREInput · 1 bit WLInput · 1 bit QExpected output · 1 bit SAMPLEExpected output · 1 bit
#0unknown-startup1 1 0 0 X X
#1activate-empty2 1 0 1 not checked X
#2restore-one4 1 1 1 1 not checked
#3close5 1 0 0 1 not checked
#4destructive-read7 1 0 1 X 1
#5omit-restore9 1 0 0 X not checked
#6activate-lost-bit10 1 0 1 not checked X
#7write-zero12 0 1 1 0 not checked

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 sequence starts unknown, restores one, closes, samples that one destructively without restoring, observes loss, then writes zero. Device timing tests separately cover retention and refresh.

Embedded simulation

The standalone DRAMCellTest opens this component as MEMORY_ROOT. This component has one registered structural implementation.

Open DRAMCell in the simulator

Source and tests

Test Coverage
DRAMCellTest The complete standalone sequence shown above
MemoryDeviceTimingTest Retention, refresh before expiry, and loss after expiry through structural and compact devices

3.1.3.2. DRAMRow<C>

C charge cells sharing one wordline.

IO pins

Pin Direction Width Description
WL Input 1 Shared wordline
RESTORE_c Input 1 each Column c restoration enable
D_c Input 1 each Column c restore/write value
Q_c, SAMPLE_c Output 1 each Charge and sample, 0 ≤ c < C

Behavior

columns=C creates 1–512 independent cells (default 4). Each follows the DRAMCell rule with shared WL and per-column restore/data. A row may contain K words; it has no address decoder or command port.

C++ example

Row composition. Instantiate Cell with the DRAMCellReference above; each record owns its charge, sample, and closed timestamp.

#include <array>
#include <cstddef>
#include <optional>
template<class Cell, std::size_t C>
void dram_row_update(std::array<Cell, C>& cells, std::size_t now,
                     std::size_t retention, bool wl,
                     const std::array<bool, C>& restore,
                     const std::array<std::optional<bool>, C>& data) {
    for (std::size_t c = 0; c < C; ++c)
        cells[c].update(now, retention, wl, restore[c], data[c]);
}

Unknown inputs and timing

Each column applies the cell's X/Z and lazy-retention rules independently. An unknown shared WL affects every cell. Use separate settled activation and restoration steps; this is not an instantaneous atomic row read.

Implementation

Structural implementation

CELL_0 through CELL_(C−1) are DRAMCell primitives. WL fans out to all cells; restoration and output pins remain per-column. Cells are laid out horizontally.

Behavioral implementation

Stores C charge records and applies sampling, restoration, close timestamps, and expiry directly. It preserves expanded row pins but removes the cell objects.

Examples

All 5 recorded rows of DRAMRowTest, 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 (3f3be5125c460b70). Changing fidelity or the circuit can change these times.

Checkpointzero-based number · timeline label Timesimulator units D_0Input · 1 bit D_1Input · 1 bit RESTORE_0Input · 1 bit RESTORE_1Input · 1 bit WLInput · 1 bit Q_0Expected output · 1 bit Q_1Expected output · 1 bit SAMPLE_0Expected output · 1 bit SAMPLE_1Expected output · 1 bit
#0initial1 1 0 0 0 0 X X not checked not checked
#1activate2 1 0 0 0 1 not checked not checked X not checked
#2write-row4 1 0 1 1 1 1 0 not checked not checked
#3close5 1 0 0 0 0 1 not checked not checked not checked
#4sample7 1 0 0 0 1 not checked not checked 1 0

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 two-column example restores one and zero, closes the row, then reactivates it and checks both samples.

Embedded simulation

The standalone DRAMRowTest opens this component as MEMORY_ROOT. Use the explorer to compare structural and behavioral implementations.

Open DRAMRow<C> in the simulator

Source and tests

Test Coverage
DRAMRowTest The complete standalone sequence shown above
MemoryDeviceContractTest Structural/behavioral agreement at settled checkpoints, including mixed hierarchy selection

3.1.3.3. DRAMSenseColumn<R>

One shared column's row-buffer bit and restoration control.

IO pins

Pin Direction Width Description
PRECHARGE Input 1 Prepare the column with all wordlines low
SENSE Input 1 Enable sensing/restoration
WL_r, SAMPLE_r Input 1 each Selection and sampled charge, 0 ≤ r < R
WRITE Input 1 Replace the active row-buffer bit
D Input 1 Write value
Q Output 1 Latched row-buffer bit
RESTORE Output 1 Feed Q into the selected cell

Behavior

After precharge, sensing exactly one selected row latches its SAMPLE and asserts RESTORE. Subsequent SAMPLE changes do not overwrite the latch; WRITE changes it. No active sensing means RESTORE=0 while the buffer can remain latched. Switching to another row without precharging invalidates the buffer. rows=R accepts 1–512, default 2.

C++ example

Binary controls, including zero/multiple selected rows. An empty data optional means X. The reference retains the buffer when sensing is disabled.

#include <cstddef>
#include <optional>
#include <vector>
struct DRAMSenseReference {
    bool prepared = true, restore = false;
    std::optional<std::size_t> sensed_row;
    std::optional<bool> q;
    void update(bool precharge, bool sense, const std::vector<bool>& wl,
                const std::vector<std::optional<bool>>& samples,
                bool write, std::optional<bool> data) {
        std::optional<std::size_t> row;
        unsigned count = 0;
        for (std::size_t r = 0; r < wl.size(); ++r)
            if (wl[r]) { row = r; ++count; }
        restore = false;
        if (precharge && !count) {
            prepared = true;
            sensed_row.reset(); q.reset();
        } else if (precharge || count > 1) {
            prepared = false;
            sensed_row.reset(); q.reset();
        } else if (sense && row) {
            if (!prepared || (sensed_row && sensed_row != row)) {
                prepared = false; sensed_row.reset(); q.reset();
                return;
            }
            if (!sensed_row) { sensed_row = row; q = samples.at(*row); }
            if (write) q = data;
            restore = true;
        }
    }
};

Unknown inputs and timing

Starts prepared with Q=X. Unknown wordlines, more than one selected row, unknown SENSE, or invalid precharge conditions invalidate preparation and Q. Unknown WRITE during sensing makes Q unknown. PRECHARGE=1 with all wordlines low prepares regardless of SENSE. It is a one-tick digital primitive; samples must settle before SENSE.

Implementation

Structural implementation

Terminal stateful sense primitive. Its latched Q forms one bit of the bank's row buffer; RESTORE feeds the selected cells. It models the result of sensing/restoration, not voltage differences or transistor gain.

Examples

All 6 recorded rows of DRAMSenseColumnTest, 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 (3f3be5125c460b70). Changing fidelity or the circuit can change these times.

Checkpointzero-based number · timeline label Timesimulator units DInput · 1 bit PRECHARGEInput · 1 bit SAMPLE_0Input · 1 bit SAMPLE_1Input · 1 bit SENSEInput · 1 bit WL_0Input · 1 bit WL_1Input · 1 bit WRITEInput · 1 bit QExpected output · 1 bit RESTOREExpected output · 1 bit
#0precharge1 0 1 1 0 0 0 0 0 X 0
#1activate2 0 0 1 0 0 1 0 0 not checked 0
#2sense-and-restore4 0 0 1 0 1 1 0 0 1 1
#3latch-survives-sample-change5 0 0 X 0 1 1 0 0 1 not checked
#4write-row-buffer7 0 0 X 0 1 1 0 1 0 not checked
#5conflicting-rows9 0 0 X 0 1 1 1 1 X 0

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.

Precharge, activate row zero, sense one, change SAMPLE to X while retaining the latch, write zero into the buffer, then assert conflicting wordlines and check invalidation.

Embedded simulation

The standalone DRAMSenseColumnTest opens this component as MEMORY_ROOT. This component has one registered structural implementation.

Open DRAMSenseColumn<R> in the simulator

Source and tests

Test Coverage
DRAMSenseColumnTest The complete standalone sequence shown above
MemoryDeviceContractTest Integration in the new structural devices and comparison with their compact implementations

3.1.3.4. DRAMStorageBank<W>

A matrix of charge cells connected to a shared row buffer.

IO pins

Pin Direction Width Description
PRECHARGE, SENSE Input 1 each Shared column controls
WL_r Input 1 each Row wordlines, 0 ≤ r < N/K
DRIVE_c, DATA_c Input 1 each Column write enables and values
COL_c Output 1 each Row-buffer bits, 0 ≤ c < W×K

Behavior

Activate one wordline, allow cell samples to settle, then enable SENSE. Each column latches and restores its sample. DRIVE_c writes that column's row-buffer bit and restores it into the selected cell. Close the wordline before precharge. Address translation, byte masks, and legal command sequencing belong to the device.

C++ example

The bank composes the preceding cell and sense-column references. Each call settles charge sampling, column sensing, and restoration in that order; it does not model the individual gate delays between them.

#include <vector>
struct DRAMBankReference {
    std::vector<std::vector<DRAMCellReference>> rows;
    std::vector<DRAMSenseReference> columns;
    DRAMBankReference(std::size_t r, std::size_t c)
        : rows(r, std::vector<DRAMCellReference>(c)), columns(c) {}
    void update(std::size_t now, std::size_t retention, bool precharge, bool sense,
                const std::vector<bool>& wl, const std::vector<bool>& drive,
                const std::vector<std::optional<bool>>& data) {
        for (std::size_t c = 0; c < columns.size(); ++c) {
            std::vector<std::optional<bool>> samples;
            for (std::size_t r = 0; r < rows.size(); ++r) {
                rows[r][c].update(now, retention, wl.at(r), false, std::nullopt);
                samples.push_back(rows[r][c].sample);
            }
            auto& column = columns[c];
            column.update(precharge, sense, wl, samples, drive.at(c), data.at(c));
            for (std::size_t r = 0; r < rows.size(); ++r)
                rows[r][c].update(now, retention, wl.at(r), column.restore, column.q);
        }
    }
    // COL_c = columns[c].q. Cell charge and samples remain independently visible.
};

Unknown inputs and timing

Selection conflicts and unknown controls follow DRAMSenseColumn; individual charge records follow DRAMCell. Closing without valid restoration can lose data. Both bank fidelities retain scalar pins and the 4,096-cell limit. Compare settled phases, not transient gate waveforms or exact retention-threshold ticks across fidelities.

Implementation

Structural implementation

ROW_r contains W×K cells. COLUMN_c is a DRAMSenseColumn connected to SAMPLE_c from every row, with Q/RESTORE feedback to every cell in that column. WL qualifies which cell accepts restoration. Rows are stacked and sense columns align beneath them.

Behavioral implementation

Uses charge records plus a row-buffer vector, with independent selection/preparation state. It preserves destructive sampling, restoration, precharge, per-column writes, and retention without expanding the cells.

Examples

All 9 recorded rows of DRAMStorageBankTest, 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 (3f3be5125c460b70). Changing fidelity or the circuit can change these times.

Checkpointzero-based number · timeline label Timesimulator units DATA_0Input · 1 bit DATA_1Input · 1 bit DATA_10Input · 1 bit DATA_11Input · 1 bit DATA_12Input · 1 bit DATA_13Input · 1 bit DATA_14Input · 1 bit DATA_15Input · 1 bit DATA_2Input · 1 bit DATA_3Input · 1 bit DATA_4Input · 1 bit DATA_5Input · 1 bit DATA_6Input · 1 bit DATA_7Input · 1 bit DATA_8Input · 1 bit DATA_9Input · 1 bit DRIVE_0Input · 1 bit DRIVE_1Input · 1 bit DRIVE_10Input · 1 bit DRIVE_11Input · 1 bit DRIVE_12Input · 1 bit DRIVE_13Input · 1 bit DRIVE_14Input · 1 bit DRIVE_15Input · 1 bit DRIVE_2Input · 1 bit DRIVE_3Input · 1 bit DRIVE_4Input · 1 bit DRIVE_5Input · 1 bit DRIVE_6Input · 1 bit DRIVE_7Input · 1 bit DRIVE_8Input · 1 bit DRIVE_9Input · 1 bit PRECHARGEInput · 1 bit SENSEInput · 1 bit WL_0Input · 1 bit WL_1Input · 1 bit COL_0Expected output · 1 bit COL_1Expected output · 1 bit COL_10Expected output · 1 bit COL_11Expected output · 1 bit COL_12Expected output · 1 bit COL_13Expected output · 1 bit COL_14Expected output · 1 bit COL_15Expected output · 1 bit COL_2Expected output · 1 bit COL_3Expected output · 1 bit COL_4Expected output · 1 bit COL_5Expected output · 1 bit COL_6Expected output · 1 bit COL_7Expected output · 1 bit COL_8Expected output · 1 bit COL_9Expected output · 1 bit
#0precharge1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 X not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked
#1activate-row-zero2 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked
#2sense-empty-row4 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 X not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked
#3write-row7 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 0 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
#4hold-row8 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
#5close-row10 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked
#6precharge-again12 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 X not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked
#7reopen-row14 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked
#8read-restored-row17 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 1 0 1 0 1 0 1 0 1 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.

Writes alternating bits across row zero, releases write drive, closes/precharges, reopens the row, and checks that restoration preserved all 16 columns.

Embedded simulation

The standalone DRAMStorageBankTest opens this component as MEMORY_ROOT. Use the explorer to compare structural and behavioral implementations.

Open DRAMStorageBank<W> in the simulator

Source and tests

Test Coverage
DRAMStorageBankTest The complete standalone sequence shown above
MemoryDeviceContractTest Structural/behavioral agreement at settled checkpoints, including mixed hierarchy selection

3.1.3.5. DRAMCommandControl<W>

Clocked command acceptance and timed internal phase generation.

IO pins

Pin Direction Width Description
CLK Input 1 Strict 0→1 command edge
CMD Input 3 NOP, ACT, READ, WRITE, PRECHARGE, REFRESH
ADDR Input 32 Row for ACT, slot for READ/WRITE
WRITE_DATA Input W Write payload
WRITE_MASK Input W/8 Byte enables
BUSY, ERROR, READ_VALID, ROW_OPEN Output 1 each Device status
ROW_ADDR, COLUMN_ADDR Output 32 each Latched row and word slot
DATA Output W Latched write payload
MASK Output W/8 Latched write byte enables
PRECHARGE, SENSE Output 1 each Column phase controls
ROW_ENABLE, WRITE_ENABLE Output 1 each Row and write phases
OUTPUT_ENABLE Output 1 Completed read publication

Behavior

Implements the command table in DRAM below without storing array data. On a legal command edge, latch required inputs, clear old error/read-valid state, assert BUSY, then advance finite scheduled phases. NOP preserves status. Invalid idle requests clear read-valid and report ERROR; requests rejected while busy do not cancel or replace the active operation.

C++ example

Binary pin values; geometry and positive timing parameters are prevalidated. Call on input changes and every nonzero due, including when CLK is stopped. Fields below name every output: output supplies both READ_VALID and OUTPUT_ENABLE; wl supplies ROW_ENABLE. This is a standalone reference algorithm, not the simulator's sequencer.

#include <cstdint>
#include <cstddef>
#include <optional>
#include <vector>
template<std::size_t W> struct DRAMControlReference {
    struct Inputs {
        bool clock = false;
        unsigned cmd = 0;
        std::uint32_t address = 0, data = 0;
        unsigned mask = 0;
    };
    std::size_t rows, slots, ticks, due = 0;
    unsigned command = 0, stage = 0;
    std::size_t row = 0, col = 0, refresh_row = 0;
    bool busy = false, error = false, open = false, precharge = true;
    bool sense = false, wl = false, write = false, output = false;
    std::optional<bool> previous_clock; // First observed high is not a 0->1 edge.
    std::vector<std::optional<bool>> data = std::vector<std::optional<bool>>(W);
    unsigned mask = 0;
    DRAMControlReference(std::size_t r, std::size_t k, std::size_t t)
        : rows(r), slots(k), ticks(t) {}
    void update(std::size_t now, const Inputs& in) {
        // Finish the current phase before accepting a simultaneous command edge.
        if (due && now >= due) {
            due = 0; ++stage;
            switch (command) {
            case 1: // ACT: select -> sense/restore -> row open.
                if (stage == 1) wl = true;
                else if (stage == 2) sense = true;
                else { open = true; busy = false; }
                break;
            case 2: output = true; busy = false; break;
            case 3:
                if (stage == 1) write = true;
                else if (stage == 2) write = false;
                else busy = false;
                break;
            case 4:
                if (stage == 1) { wl = false; sense = false; }
                else if (stage == 2) precharge = true;
                else { open = false; busy = false; }
                break;
            case 5: // Refresh exactly one row, then advance the cursor.
                if (stage == 1) wl = true;
                else if (stage == 2) sense = true;
                else if (stage == 3) { wl = false; sense = false; }
                else if (stage == 4) precharge = true;
                else { refresh_row = (refresh_row + 1) % rows; busy = false; }
                break;
            }
            if (busy) due = now + ticks;
        }
        const bool edge = previous_clock == false && in.clock;
        previous_clock = in.clock;
        if (!edge || in.cmd == 0) return; // NOP preserves status, even while busy.
        if (busy) { error = true; return; } // Do not disturb the active operation.
        bool allowed = false;
        switch (in.cmd) {
        case 1: allowed = !open && in.address < rows; break;
        case 2: case 3: allowed = open && in.address < slots; break;
        case 4: allowed = open; break;
        case 5: allowed = !open; break;
        }
        output = false;
        if (!allowed) { error = true; return; }
        error = false; busy = true; command = in.cmd; stage = 0; due = now + ticks;
        if (command == 1 || command == 5) {
            row = command == 1 ? in.address : refresh_row;
            precharge = wl = sense = false;
        }
        if (command == 2 || command == 3) col = in.address;
        if (command == 3) {
            for (unsigned b = 0; b < W; ++b) data[b] = bool((in.data >> b) & 1u);
            mask = in.mask;
        }
    }
};

Unknown inputs and timing

Only CLK=0 followed by CLK=1 accepts a command; X/Z→1 is not an edge. Unknown CMD is rejected. ACT/READ/WRITE require binary ADDR; WRITE also requires a binary mask. Payload X is allowed and becomes unknown storage in enabled bytes. Inputs unused by a command are ignored. Completion is processed before a command edge at the same internal deadline.

Implementation

Structural implementation

Terminal digital finite-state sequencer with latched request and round-robin refresh cursor. Its outputs drive separate gates and storage in the structural device. It is not a gate-expanded FSM. The complete behavioral device reuses this sequencer, so equivalence checks independently validate the data path, not an independent controller algorithm.

Examples

All 29 recorded rows of DRAMCommandControlTest, 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 (3f3be5125c460b70). Changing fidelity or the circuit can change these times.

Checkpointzero-based number · timeline label Timesimulator units ADDRInput · 32 bit CLKInput · 1 bit CMDInput · 3 bit WRITE_DATAInput · 8 bit WRITE_MASKInput · 1 bit BUSYExpected output · 1 bit COLUMN_ADDRExpected output · 32 bit DATAExpected output · 8 bit ERRORExpected output · 1 bit MASKExpected output · 1 bit OUTPUT_ENABLEExpected output · 1 bit PRECHARGEExpected output · 1 bit READ_VALIDExpected output · 1 bit ROW_ADDRExpected output · 32 bit ROW_ENABLEExpected output · 1 bit ROW_OPENExpected output · 1 bit SENSEExpected output · 1 bit WRITE_ENABLEExpected output · 1 bit
#0closed-startup1 0x00000000 0 0x0 0x81 1 0 not checked not checked not checked not checked not checked not checked not checked not checked not checked 0 not checked not checked
#1read-while-closed-setup2 0x00000000 0 0x2 0x81 1 not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked
#2read-while-closed4 0x00000000 1 0x2 0x81 1 0 not checked not checked 1 not checked not checked not checked not checked not checked not checked not checked not checked not checked
#3activate-row-zero-setup5 0x00000000 0 0x1 0x81 1 not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked
#4activate-row-zero103 0x00000000 1 0x1 0x81 1 0 not checked not checked 0 not checked not checked not checked not checked not checked not checked 1 not checked not checked
#5write-column-zero-setup104 0x00000000 0 0x3 0x81 1 not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked
#6write-column-zero202 0x00000000 1 0x3 0x81 1 0 not checked not checked 0 not checked not checked not checked not checked not checked not checked not checked not checked not checked
#7read-column-zero-setup203 0x00000000 0 0x2 0x81 1 not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked
#8read-column-zero237 0x00000000 1 0x2 0x81 1 0 not checked not checked not checked not checked 1 not checked not checked not checked not checked not checked not checked not checked
#9masked-write-setup238 0x00000000 0 0x3 0x5A 1 not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked
#10masked-write336 0x00000000 1 0x3 0x5A 1 0 not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked
#11read-masked-word-setup337 0x00000000 0 0x2 0x5A 1 not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked
#12read-masked-word371 0x00000000 1 0x2 0x5A 1 0 not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked
#13unknown-command-setup372 0x00000000 0 0bXXX 0x5A 1 not checked not checked not checked not checked not checked not checked not checked 1 not checked not checked not checked not checked not checked
#14unknown-command-rejected374 0x00000000 1 0bXXX 0x5A 1 0 not checked not checked 1 not checked 0 not checked 0 not checked not checked not checked not checked not checked
#15nop-preserves-error-setup375 0x00000000 0 0x0 0x5A 1 not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked
#16nop-preserves-error376 0x00000000 1 0x0 0x5A 1 0 not checked not checked 1 not checked not checked not checked 0 not checked not checked not checked not checked not checked
#17bad-column-setup377 0x00000002 0 0x2 0x5A 1 not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked
#18bad-column378 0x00000002 1 0x2 0x5A 1 0 not checked not checked 1 not checked not checked not checked not checked not checked not checked not checked not checked not checked
#19precharge-setup379 0x00000000 0 0x4 0x5A 1 not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked
#20precharge477 0x00000000 1 0x4 0x5A 1 0 not checked not checked 0 not checked not checked not checked not checked not checked not checked 0 not checked not checked
#21refresh-next-row-setup478 0x00000000 0 0x5 0x5A 1 not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked
#22refresh-next-row640 0x00000000 1 0x5 0x5A 1 0 not checked not checked 0 not checked not checked not checked not checked not checked not checked 0 not checked not checked
#23activate-restored-row-setup641 0x00000000 0 0x1 0x5A 1 not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked
#24activate-restored-row739 0x00000000 1 0x1 0x5A 1 0 not checked not checked 0 not checked not checked not checked not checked not checked not checked not checked not checked not checked
#25read-persisted-word-setup740 0x00000000 0 0x2 0x5A 1 not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked
#26read-persisted-word774 0x00000000 1 0x2 0x5A 1 0 not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked
#27reserved-command-setup775 0x00000000 0 0x7 0x5A 1 not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked not checked
#28reserved-command777 0x00000000 1 0x7 0x5A 1 0 not checked not checked 1 not checked not checked not checked not checked not checked not checked not checked not checked not checked

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 sequence checks a read while closed, activation, write/read, byte mask, unknown and reserved commands, NOP status preservation, invalid column, precharge, and one refresh.

Embedded simulation

The standalone DRAMCommandControlTest opens this component as MEMORY_ROOT. This component has one registered structural implementation.

Open DRAMCommandControl<W> in the simulator

Source and tests

Test Coverage
DRAMCommandControlTest The complete standalone sequence shown above
MemoryDeviceTimingTest Intermediate BUSY/ROW_OPEN and device-level scheduled phase behavior

3.1.3.6. DRAM<W>

Complete single-bank command-addressed DRAM device.

IO pins

Pin Direction Width Description
CLK Input 1 Strict 0→1 command edge
CMD Input 3 Command code below
ADDR Input 32 Row for ACT; word slot for READ/WRITE
WRITE_DATA Input W Latched write payload
WRITE_MASK Input W/8 Byte enables
BUSY Output 1 Command in progress
ERROR Output 1 Rejected request; next accepted non-NOP clears it
ROW_OPEN Output 1 Activation completed; row available
READ_VALID Output 1 Completed read available
READ_DATA Output W Selected word while valid, otherwise Z

Behavior

Commands operate on one open row at a time. A word's linear index is row*K + slot; ADDR itself is not always a linear word address.

Commands and timing

Let t be the accepted command edge and P=phase_ticks. The listed times are sequencer deadlines; output wire propagation and structural logic require additional settling.

CMD Operation Preconditions / ADDR Phases after t
0 NOP Inputs ignored No change
1 ACT Closed; ADDR < N/K P: select row; 2P: sense/restore; 3P: ready, row open
2 READ Open; ADDR < K P: read valid, ready
3 WRITE Open; ADDR < K; binary mask P: write on; 2P: write off; 3P: ready
4 PRECHARGE Open; ADDR ignored P: close wordline/sense; 2P: precharge; 3P: ready, closed
5 REFRESH Closed; ADDR ignored P: select internal refresh row; 2P: sense/restore; 3P: close; 4P: precharge; 5P: ready
6–7 Reserved Always rejected ERROR; no storage operation

REFRESH services one row at an internal round-robin cursor starting at zero. N/K refresh commands cover a bank. The native device has no automatic/background self-refresh; the DRAM memory controller issues refresh while its bus clock runs. Direct native-device users must issue it themselves. Refresh samples an expired row as X and cannot repair it.

Write data/address/mask are latched at acceptance. A zero mask is a valid no-op write. Unknown payload bits become unknown storage only in enabled byte lanes. Initial CLK=X followed by high is not an edge. Do not overlap commands: wait for BUSY to settle low before the next edge. ROW_OPEN remains high during precharge until completion; BUSY prevents premature access. A completed read remains valid until another accepted command or an idle rejected non-NOP invalidates it.

C++ example

Complete native-port reference composed from the command control and bank above, for W=8/16/32. It includes command rejection, timed phases, refresh, retention, destructive sampling/restoration, byte writes, status, and disabled read output. Binary inputs and validated geometry are assumed; empty data optionals preserve unknown stored bits.

#include <cstddef>
#include <optional>
#include <vector>
template<std::size_t W> struct DRAMReference {
    static_assert(W == 8 || W == 16 || W == 32);
    DRAMControlReference<W> control;
    DRAMBankReference bank;
    std::size_t retention;
    std::vector<std::optional<bool>> read_data = std::vector<std::optional<bool>>(W);
    bool high_z = true;
    DRAMReference(std::size_t words, std::size_t slots, std::size_t ticks,
                  std::size_t retention_ticks)
        : control(words / slots, slots, ticks), bank(words / slots, W * slots),
          retention(retention_ticks) {}
    void update(std::size_t now, const typename DRAMControlReference<W>::Inputs& in) {
        control.update(now, in);
        std::vector<bool> wl(control.rows, false), drive(W * control.slots, false);
        std::vector<std::optional<bool>> data(W * control.slots);
        if (control.wl) wl[control.row] = true;
        for (std::size_t c = 0; c < data.size(); ++c) {
            data[c] = control.data[c % W];
            drive[c] = control.write && c / W == control.col &&
                       (control.mask & (1u << ((c % W) / 8)));
        }
        bank.update(now, retention, control.precharge, control.sense, wl, drive, data);
        high_z = !control.output;
        for (std::size_t b = 0; b < W; ++b)
            read_data[b] = high_z ? std::nullopt : bank.columns[control.col * W + b].q;
    }
    // BUSY / ERROR / READ_VALID / ROW_OPEN = control.busy / error / output / open.
    // Next scheduled device phase: control.due (0 means none).
};

Unknown inputs and timing

Command/control validation follows DRAMCommandControl. A rejected unknown idle command invalidates an earlier read just like a reserved command; rejection while busy leaves the operation running. Masked-off bytes ignore unknown payload. Stable status and data require output/gate settling beyond sequencer deadlines. Retention is measured at each implementation's modeled cell activation/close, so avoid treating the propagation-sized threshold boundary as fidelity-independent calibrated time.

Implementation

Structural implementation

COMMAND_CONTROL drives ROW_DECODE and storage phases. STORAGE exposes cell rows and sense/restore columns; COLUMN_SELECT reads one word from the row buffer; WRITE_DRIVERS distributes byte-qualified writes; OUTPUT publishes only valid data. No FF or SRAM memory is hidden underneath.

Behavioral implementation

Uses packed MemoryStorage, per-row closed timestamps, and a W×K buffer. Activation destructively samples storage, sensing restores it, and writes merge enabled bytes into the buffer and storage. The sequencer is shared with structural control; the storage/sense data path is separately implemented. Compact history is read-only; initialize through ACT/WRITE. History may be disabled for large runs.

Examples

All 29 recorded rows of DRAMTest, 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 (3f3be5125c460b70). Changing fidelity or the circuit can change these times.

Checkpointzero-based number · timeline label Timesimulator units ADDRInput · 32 bit CLKInput · 1 bit CMDInput · 3 bit WRITE_DATAInput · 8 bit WRITE_MASKInput · 1 bit BUSYExpected output · 1 bit ERRORExpected output · 1 bit READ_DATAExpected output · 8 bit READ_VALIDExpected output · 1 bit ROW_OPENExpected output · 1 bit
#0closed-startup11 0x00000000 0 0x0 0x81 1 0 not checked not checked not checked 0
#1read-while-closed-setup12 0x00000000 0 0x2 0x81 1 not checked not checked not checked not checked not checked
#2read-while-closed14 0x00000000 1 0x2 0x81 1 0 1 not checked not checked not checked
#3activate-row-zero-setup15 0x00000000 0 0x1 0x81 1 not checked not checked not checked not checked not checked
#4activate-row-zero113 0x00000000 1 0x1 0x81 1 0 0 not checked not checked 1
#5write-column-zero-setup114 0x00000000 0 0x3 0x81 1 not checked not checked not checked not checked not checked
#6write-column-zero212 0x00000000 1 0x3 0x81 1 0 0 not checked not checked not checked
#7read-column-zero-setup213 0x00000000 0 0x2 0x81 1 not checked not checked not checked not checked not checked
#8read-column-zero248 0x00000000 1 0x2 0x81 1 0 not checked 0x81 1 not checked
#9masked-write-setup249 0x00000000 0 0x3 0x5A 1 not checked not checked not checked not checked not checked
#10masked-write347 0x00000000 1 0x3 0x5A 1 0 not checked not checked not checked not checked
#11read-masked-word-setup348 0x00000000 0 0x2 0x5A 1 not checked not checked not checked not checked not checked
#12read-masked-word383 0x00000000 1 0x2 0x5A 1 0 not checked 0x5A not checked not checked
#13unknown-command-setup384 0x00000000 0 0bXXX 0x5A 1 not checked not checked not checked 1 not checked
#14unknown-command-rejected387 0x00000000 1 0bXXX 0x5A 1 0 1 0bZZZZZZZZ 0 not checked
#15nop-preserves-error-setup388 0x00000000 0 0x0 0x5A 1 not checked not checked not checked not checked not checked
#16nop-preserves-error389 0x00000000 1 0x0 0x5A 1 0 1 not checked 0 not checked
#17bad-column-setup390 0x00000002 0 0x2 0x5A 1 not checked not checked not checked not checked not checked
#18bad-column391 0x00000002 1 0x2 0x5A 1 0 1 not checked not checked not checked
#19precharge-setup392 0x00000000 0 0x4 0x5A 1 not checked not checked not checked not checked not checked
#20precharge490 0x00000000 1 0x4 0x5A 1 0 0 not checked not checked 0
#21refresh-next-row-setup491 0x00000000 0 0x5 0x5A 1 not checked not checked not checked not checked not checked
#22refresh-next-row653 0x00000000 1 0x5 0x5A 1 0 0 not checked not checked 0
#23activate-restored-row-setup654 0x00000000 0 0x1 0x5A 1 not checked not checked not checked not checked not checked
#24activate-restored-row752 0x00000000 1 0x1 0x5A 1 0 0 not checked not checked not checked
#25read-persisted-word-setup753 0x00000000 0 0x2 0x5A 1 not checked not checked not checked not checked not checked
#26read-persisted-word788 0x00000000 1 0x2 0x5A 1 0 not checked 0x5A not checked not checked
#27reserved-command-setup789 0x00000000 0 0x7 0x5A 1 not checked not checked not checked not checked not checked
#28reserved-command792 0x00000000 1 0x7 0x5A 1 0 1 not checked not checked not checked

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 W=8 device writes 0x81, changes its enabled byte to 0x5A, exercises invalid commands and status preservation, closes, refreshes, and reads 0x5A again. Wider byte-mask coverage is in the contract test. The table contains setup rows as well as command-completion rows.

Embedded simulation

The standalone DRAMTest opens this component as MEMORY_ROOT. Use the explorer to compare structural and behavioral implementations.

Open DRAM<W> in the simulator

Source and tests

Test Coverage
DRAMTest The complete standalone sequence shown above
MemoryDeviceContractTest W=8/16/32, K=1/2/4, mixed fidelity, a masked-write oracle, geometry rejection, and compact history/allocation
MemoryDeviceTimingTest Early BUSY, refresh preservation, expiry, and event-queue quiescence
MemoryDeviceDocumentationTest Compile the exact references; test cell/bank feedback, W=8/16/32 device behavior, refresh/expiry, busy rejection and simultaneous completion/command edges; replay native command/device binary-input prefixes