Skip to content
VLSI Mentor

DDR · Module 1

SRAM

What has to change for storage to become much denser than a register file while staying much faster than main memory. The bistable static cell, why it needs no refresh, the array organisation that amortises its periphery, and the transistor count that stops it holding a system's working set.

Chapter 1.2 closed on a specific pressure. A register file is superb for a few tens of values beside an execution unit and impossible at system scale, because the properties that make it fast — per-entry access paths, per-bit generality, physical adjacency to compute — are exactly the ones that do not scale. The chapter ended by naming the question this one answers.

What has to change for storage to become much denser, while staying much faster and much closer to compute than main memory? Three things change, and each has a price. The storage element gets smaller and gives up its individual access path. The access path gets shared across a great many cells, which is how the cost of decoding and sensing stops being paid per bit. And the read stops being a same-cycle selection and becomes an operation with a defined latency.

The technology that makes that trade is SRAM — static random-access memory. It is what every cache in every processor is built from, what on-chip buffers and scratchpads are built from, and, at small depths, often what register files themselves are built from. It is also the last tier before the trade has to be made again, more aggressively, which is what makes it the right chapter to sit between the register file and DRAM.

1. What "Static" Actually Means

The name is the mechanism. A static cell holds its value in a bistable circuit: two inverters connected in a loop, each driving the other's input. If one side sits high, it drives the other side low, which drives the first side high — the state reinforces itself. There are exactly two such self-consistent states, which is what makes the cell a one-bit store.

Two consequences follow immediately, and they are the whole reason SRAM occupies the tier it does.

It needs no maintenance while powered. The loop is actively driven by transistors connected to the supply, so the stored value is continuously restored by the circuit itself. Nothing leaks away and nothing has to be periodically rewritten. That single property is what the word static contrasts with, and it is the property Chapter 1.4 will show that DRAM gives up.

It is volatile. Actively driven means actively powered. Remove the supply and the loop collapses; the stored state is gone. "Static" is a claim about maintenance, not about persistence — a distinction worth fixing now, because it is the most common misreading of the term and Chapter 1.5 is about the tier that genuinely does not need power to remember.

The classic cell adds two more transistors to that loop: access transistors, one on each side, which connect the cell's two internal nodes to a pair of vertical wires when a horizontal wire tells them to. That is six transistors in total — the 6T cell — and the two wires and one control line have names worth learning once, because the entire array is built out of them.

2. Read, Write, and the Tension Between Them

A read is non-destructive. Raise the wordline and the cell — which is actively driven — influences the bitline pair, producing a small voltage difference between them that a sense circuit resolves into a logic value. The cell's own inverters keep holding the bit throughout. When the wordline drops, the cell is exactly as it was. Reading SRAM does not disturb what it stores.

Hold onto that sentence. It sounds unremarkable and it is precisely the property Chapter 1.4 will show does not hold for DRAM — and the absence of it there is why a DRAM access has steps that an SRAM access does not.

A write overpowers the cell. The write drivers force the bitline pair to the desired state strongly enough that, when the wordline turns the access transistors on, the cell's loop is driven into the opposite state and then holds it.

Those two operations are in tension, and the tension is a real design constraint rather than a curiosity.

During a read, the cell must be strong enough that connecting it to the bitlines does not accidentally flip it. A cell that loses its state when read is worse than useless.

During a write, the same cell must be weak enough that the write drivers can flip it, through the same access transistors.

One cell has to be both hard to disturb and possible to overwrite. The relative strengths of its transistors are what balance those requirements, and that balance is a genuine circuit-design problem — it is why cell design is a specialist discipline and why a memory compiler, not an RTL engineer, produces the cell. What an RTL or verification engineer needs from it is the conclusion: the cell is a carefully balanced circuit, which is part of why it costs what it costs.

3. The Array — Where the Density Comes From

A cell alone is not memory. The structure around it is where the tier's economics are decided, and it is the direct answer to the promise Chapter 1.2 made about "sharing decode, selection and data paths across many bits".

An address enters a row decoder which raises one wordline in the cell array. Every cell in the selected row drives its bitline pair. Sense amplifiers resolve the bitlines, and a column multiplexer selects the addressed bits for the data interface. A timing and control block sequences the access.Row decoderone wordline at a timeCell arrayrows on shared wordlinesSense ampsresolve the bitline pairColumn mux + I/Oselects the addressed bitsTiming + controlsequences one accesswordlinebitlinesdataenable12
Figure 1 — one address selects one wordline; every cell in that row drives its column, and the periphery is shared by all of them.

Read the figure as an economic argument rather than a circuit.

The row decoder is shared by every column. One decoder turns an address into exactly one raised wordline, and the cost of that decoder is divided across every bit in the row it selects. Widen the array and the decoder's cost per bit falls.

The sense amplifiers and write drivers are shared by every row. Each column has one set, sitting at the end of its bitlines, used by whichever row is currently selected. Deepen the array and their cost per bit falls.

The timing and control logic is shared by the entire array. One access sequence serves whatever the address selected.

That is the whole trick, and it is worth stating as a principle because the same principle governs DRAM and, later, the DDR device itself: the expensive parts of a memory are built once and amortised over many cells, and the cell is made as small as possible because it is the part that is replicated. A register file inverts this — it gives each entry its own access path, which is what makes it fast and what makes it unaffordable in bulk.

And the price of sharing is exclusivity. One wordline at a time means one row at a time. A structure whose periphery is shared cannot serve arbitrary simultaneous accesses the way a multi-ported register file can; it serves one access per port per cycle, and a single-ported array serves one access, full stop. §6 is what that costs in RTL.

4. Why It Is Still Fast, and Why It Is Not Free

Fast, because nothing has to be rebuilt. The cell drives its own value. Sensing is a matter of resolving a difference that the cell is actively producing, and no part of the access has to restore anything afterwards. There is no maintenance operation competing for the array, and no sequence of steps that must be issued in order — an SRAM access is, architecturally, a single operation.

Not free, because six transistors is six transistors. This is the density limit, and it is worth stating carefully so it is not mistaken for a number.

A static cell spends several transistors per bit — plus the contacts, the wordline, and the bitline pair that every cell needs a share of. Every one of those is paid at every bit, because the cell is the replicated part. The exact area a cell occupies depends on the process technology, the cell variant, and what the design is optimising for, so this chapter quotes no figure and no ratio; the structural claim is what matters and it is not in doubt: a storage element built from several actively driven transistors cannot be as small as one built from the minimum possible number of devices. Chapter 1.4 builds that minimum, and Chapter 1.6 works out what the difference means in cost.

And there is a second cost that scales badly: being powered. Every cell is actively driven, so an SRAM array consumes power in proportion to how much storage exists rather than how much of it is in use. Techniques exist to reduce idle consumption, and they are real engineering; none of them changes the fact that the storage mechanism is maintenance by continuous drive.

So SRAM sits where it sits for a structural reason. It is denser than per-entry flip-flop storage because its cell is smaller and its periphery is shared. It is far less dense than what the next chapter builds. And it is faster than anything denser, because its read costs nothing to the stored state.

5. RTL — What an On-Chip SRAM Looks Like From the Outside

An RTL engineer almost never writes an SRAM cell. What they write is the model of, or the interface to, a synchronous single-port array — and the shape of that interface teaches the tier's properties better than a circuit diagram does.

What this code is and is not. It is a behavioural model of a synchronous single-port memory: enough to simulate against, enough to reason about timing with, and written in a style that a tool may map onto real storage. It is not an SRAM cell, not a bitline, not a sense amplifier, and not a compiled macro. A real macro comes from a memory compiler with its own interface, its own timing characteristics and its own test and repair features, and RTL must be written against that macro's datasheet rather than against assumptions. Keeping this boundary explicit is the difference between a useful model and a misleading one.

What it does. It holds DEPTH words of WIDTH bits behind one shared port. Each cycle the port performs at most one operation. A write takes effect at the clock edge. A read is registered — the address is captured at one edge and the data appears one cycle later — which is the defining behavioural difference from the register file in Chapter 1.2, whose reads were combinational.

How to simulate it. vlog sram_sp.sv tb_sram_sp.sv then vsim -c tb_sram_sp -do "run -all"; with VCS vcs -sverilog sram_sp.sv tb_sram_sp.sv && ./simv; with Xcelium xrun -sv sram_sp.sv tb_sram_sp.sv. Every later block in this chapter simulates the same way.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// BEHAVIOURAL MODEL of a synchronous single-port memory. NOT an SRAM cell,
// NOT a compiled macro. A real macro's interface, read latency and test
// features come from its compiler; write RTL against that datasheet.
//
// THE PORT IS THE POINT: one address, one operation per cycle. That
// exclusivity is what buys the density (§3), and §6 is what it costs.
module sram_sp #(
  parameter int WIDTH = 32,
  parameter int DEPTH = 1024,
  // DERIVED. The guard keeps DEPTH == 1 legal rather than producing a
  // zero-width address.
  parameter int ADDR_W = (DEPTH <= 1) ? 1 : $clog2(DEPTH)
) (
  input  logic              clk,

  // One shared port. `en` qualifies the access; `we` chooses its direction.
  input  logic              en,
  input  logic              we,
  input  logic [ADDR_W-1:0] addr,
  input  logic [WIDTH-1:0]  wdata,

  // Registered read data, and a companion flag saying which cycle it is
  // valid in. The flag is not decoration: a consumer that has to infer
  // validity from its own bookkeeping is a consumer that will get it wrong.
  output logic [WIDTH-1:0]  rdata,
  output logic              rdata_valid
);

  // The storage. One declaration; the port below is the only way in or out.
  logic [WIDTH-1:0] mem [DEPTH];

  // THE ONLY WRITER of mem, and the capture of the read. Both happen at the
  // same edge, and the `we` term is what makes them mutually exclusive: on
  // any given cycle this port either writes or reads, never both.
  always_ff @(posedge clk) begin
    if (en && we) begin
      mem[addr] <= wdata;
    end
    if (en && !we) begin
      rdata <= mem[addr];
    end
  end

  // Read-data validity follows the read by exactly one cycle, because the
  // data does. Reset here is deliberate and narrow: rdata_valid is READ by
  // the consumer before anything has written it, so it must start defined.
  // `mem` and `rdata` are not reset -- see Chapter 1.2 §9 for the argument.
  always_ff @(posedge clk) begin
    rdata_valid <= en && !we;
  end

endmodule

Expected behaviour. Drive en=1, we=1, addr=7, wdata=32'hCAFE_0001, then on a later cycle en=1, we=0, addr=7. A testbench should observe rdata_valid high exactly one cycle after that read cycle, with rdata = cafe0001. A read of an address never written returns x in simulation, for the reason Chapter 1.2 §9 gave.

Expected waveform. At the read cycle, nothing appears on rdata. One edge later, rdata and rdata_valid both change. §7 is that figure.

Synthesis implication. The block describes DEPTH × WIDTH bits of clocked storage behind a single address, with a registered output. What a tool produces from it depends on the target technology, the dimensions, the coding style and the constraints: at these dimensions a tool targeting an FPGA may map it to a block memory, while an ASIC flow more typically expects the designer to instantiate a compiled macro and would build flip-flops from this description instead — which at 1024 words would be an unpleasant surprise. Read the synthesis report; do not assume inference. That advice is not hedging for its own sake: inference behaviour is exactly the kind of thing that varies by tool, version and target, and the repository's Inferring RAM lesson exists because getting it right is a topic of its own.

Limitations. One port, one operation per cycle, no byte enables, no error correction, no test or repair interface, no power gating, and a read latency fixed at one cycle. Each of those is something a real memory subsystem has to decide about, and none of them changes the behaviour this chapter is teaching.

Debugging observations. If rdata is a cycle later than expected, check whether the consumer is sampling on rdata_valid or counting cycles itself. If a read returns the value from the previous read, suspect that en was low in the cycle you thought you issued it. If a write appears to have been lost, check we and en together — either one low means no write.

6. One Port, and What It Costs

The if (en && we) / if (en && !we) structure in §5 encodes a fact the figure in §3 predicted: a single-ported array cannot read and write in the same cycle. The periphery is shared, and a shared resource serves one client at a time.

That turns memory access into an arbitration problem the moment two sources want the array. Here is the smallest honest version of that:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SYNTHESIZABLE. Port arbitration in front of a single-port array: a write
// and a read both want the port in the same cycle, so one of them waits.
// The policy here is "write wins" -- a deliberate choice, not a default.
always_comb begin
  // Default: nothing happens.
  sram_en    = 1'b0;
  sram_we    = 1'b0;
  sram_addr  = '0;
  rd_stall   = 1'b0;

  if (wr_req) begin
    // The write takes the port.
    sram_en   = 1'b1;
    sram_we   = 1'b1;
    sram_addr = wr_addr;
    // ...and the read, if there was one, did not happen. The requester must
    // hold its request; it will be served on a cycle when no write arrives.
    rd_stall  = rd_req;
  end else if (rd_req) begin
    sram_en   = 1'b1;
    sram_we   = 1'b0;
    sram_addr = rd_addr;
  end
end

Why this is worth ten lines of a foundation chapter. It is the first appearance of a pattern that dominates the rest of the DDR curriculum. A dense memory is a shared resource with one access path, so every design built on one must decide who gets it, what happens to the loser, and how the loser knows. The signal rd_stall is the answer to that last question, and a design that omits it forces the requester to guess.

Three consequences to carry forward. Access becomes scheduled rather than simultaneous. A requester must be able to be told to wait, which means backpressure is part of the interface rather than an afterthought. And a policy — here "write wins" — is a design decision with performance consequences, because a stream of writes can starve reads indefinitely unless something bounds it. Every one of those three reappears, much larger, when the shared resource is a DRAM channel and the scheduler is a memory controller.

7. Read Latency Is Now Real

The register file in Chapter 1.2 answered a read in the cycle it was asked. This array does not, and the difference is the first place in the curriculum where waiting for storage appears as a concrete, cycle-level fact.

sram_sp — write, registered read, and a port collision

8 cycles
Eight cycles of a synchronous single-port memory. A write of 0xA1 to address 7 occurs, then a read of address 7 is issued and its data appears one cycle later accompanied by a valid flag. Later a write and a read arrive in the same cycle, the write takes the port, the read stalls, and the read is served on the following cycle.read issued — no data yetread issued — no data yetdata arrives one cycle laterdata arrives one cyclelaterwrite wins — read waitswrite wins — read waitsstalled read finally servedstalled read finally servedclkwr_reqrd_reqrd_stallsram_addr7--7--77----rdataXXX0xA10xA10xA10xA10xA1rdata_validt0t1t2t3t4t5t6t7
Figure 2 — a registered read answers one cycle late, and a write that wants the same port makes the read wait.

Cycle 2 into 3 — the latency. A read is issued in cycle 2. Nothing appears on rdata during cycle 2; the address has been presented, and the array has not yet answered. The edge ending cycle 2 captures the data, and in cycle 3 both rdata and rdata_valid are there. This one-cycle gap is the entire behavioural difference from a combinational read, and every consumer of this array has to be built around it.

Cycle 4 — the collision. A write and a read arrive together. The write takes the port, rd_stall asserts, and the read does not happen. The requester's job is to hold its request, which it does.

Cycle 5 into 6 — the stalled read completes. With no write competing, the read is accepted in cycle 5 and its data is valid in cycle 6. Note what the requester experienced: the same read took two cycles longer than the one in cycle 2, purely because of contention. Nothing about the memory changed.

And here is the idea to carry into the rest of the curriculum. Observed access time has just split into two parts: what the storage costs, and what waiting for the storage costs. In this figure the first is one cycle and the second is two. Chapter 1.8 is what happens when both grow.

8. Where SRAM Actually Appears

Naming real uses keeps the tier concrete.

Caches. The dominant use, and the reason the tier exists at the scale it does. A cache needs storage that is much larger than a register file and much faster than main memory, accessed constantly, and an SRAM array is what satisfies that. How a cache is organised — sets, ways, tags, replacement, coherence — is a different subject entirely, and the repository teaches it where it belongs: Cache Hierarchy Review.

On-chip buffers and queues. Almost every block that has to hold data briefly uses this tier: FIFOs between clock or rate domains, packet and descriptor buffers, reorder and retry buffers. The DDR controller built in Module 17 is itself full of them — its command queues are on-chip arrays, which is worth noticing now because it means a memory controller contains memory.

Scratchpads and tightly coupled memory. Storage placed deliberately close to a compute engine and addressed explicitly rather than managed automatically as a cache.

Small register files. At small depths, the structure of Chapter 1.2 is often built from this same cell technology. The two chapters describe different structures, not necessarily different silicon.

What SRAM is never used for is the main-memory tier of a general-purpose system. §4 is why, and Chapter 1.7 turns that into the full argument.

9. Verification — What There Is to Check

The model in §5 is short and its contract has more clauses than it has lines.

The read latency contract holds exactly. Read data is valid one cycle after an accepted read — not two, not sometimes one. A consumer built on "one cycle" and a memory that occasionally takes two produces data corruption that looks like a datapath bug.

rdata_valid agrees with rdata. The flag must be high exactly on the cycles the data is meaningful. A flag that is high for an extra cycle causes the consumer to accept the same word twice.

Read and write are mutually exclusive on the port. For a single-port array this is a structural invariant, and asserting it catches an arbiter that lets both through.

A write lands at the addressed word and nowhere else. As in Chapter 1.2 §14, the "nowhere else" half needs a reference model of the array rather than the port view.

en low means nothing happens. No write, no read, no change to rdata_valid other than falling. The negative case is the one that catches an enable term dropped during an optimisation.

Stall behaviour is honoured by both sides. The array's client must hold its request while stalled, and the arbiter must eventually grant it. "Eventually" is the interesting word — see the starvation note in §10.

Addressing covers the whole array. Coverage should include the first and last words, and the address bits should be exercised independently: a decoder fault that only shows up on one address bit is invisible to a test that walks addresses sequentially.

Byte-level and word-level write granularity, if the real memory has byte enables. The model in §5 does not, and a design that adds them has added a whole new class of "wrote too much" bugs.

10. Three Assertions Worth Writing

Each of these states a clause of §9's contract precisely enough that a later change cannot quietly violate it. All three are written against the port, needing no visibility into mem.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// VERIFICATION-ONLY. Written against the sram_sp port signals. No reset
// clause on P1/P2 because the model's only reset state is rdata_valid,
// which P3 is about.

// P1 -- the read-latency contract. An accepted read is followed, on the very
// next cycle, by rdata_valid. This is the clause every consumer is built on,
// so it is the one worth pinning first.
property p_read_answers_next_cycle;
  @(posedge clk)
    (en && !we) |=> rdata_valid;
endproperty
assert property (p_read_answers_next_cycle);

// P2 -- and the converse, which is the half that actually catches bugs:
// rdata_valid is asserted ONLY because a read was accepted. A valid flag
// that can rise for any other reason makes the consumer accept garbage.
property p_valid_only_after_read;
  @(posedge clk)
    rdata_valid |-> $past(en && !we);
endproperty
assert property (p_valid_only_after_read);

// P3 -- the single-port invariant, stated where it can be checked: the
// arbiter in §6 must never present a write and a read as one access. This
// is trivially true of the code as written, which is the point -- it stays
// true only as long as nobody "optimises" the arbiter.
property p_port_is_exclusive;
  @(posedge clk)
    !(sram_en && sram_we && rd_req && !rd_stall);
endproperty
assert property (p_port_is_exclusive);

What each buys. P1 and P2 together are a biconditional: a read produces a valid, and a valid was produced by a read. Either half alone is weak — P1 without P2 permits spurious valids, and P2 without P1 permits a read that never answers. Writing both is a habit worth forming, because most real interface contracts are biconditional and most assertion sets only capture one direction. P3 pins the structural claim that makes the density argument work, and it is deliberately written over the arbiter's outputs rather than the array's internals, so it fails where the mistake would actually be made.

What they do not claim. None of them says the data was correct — only that it arrived when the contract said it would. Data correctness needs a reference model holding a shadow copy of the array, which is a scoreboard and not an assertion. And P3 proves exclusivity only for the arbiter shown; a second requester added later would need it restated. Both limitations are worth saying out loud, because an assertion set that is believed to prove more than it does is more dangerous than no assertions.

11. Common Mistakes

Treating a registered read as if it were combinational. Wrong mental model: memory is an array, and reading an array gives you the value. What the engineer does: presents an address and uses rdata in the same cycle. Resulting bug: the datapath consumes the previous read's data. Because the previous value is often plausible, the failure looks like a computation error somewhere else entirely, and it only reproduces for particular access sequences. How to detect it: on a trace, check whether the cycle rdata is consumed is the same cycle the address was presented. Also check whether the consumer looks at rdata_valid at all — a consumer that ignores the valid flag is the signature. How to prevent it: make the latency explicit in the interface (a valid flag, not a comment), and make the consumer sample on it. The full treatment of read-timing choices is in Read Timing and Read-During-Write.

Assuming a large array will be inferred as a memory. Wrong mental model: writing the array pattern produces a memory on any target. What the engineer does: writes a deep array in RTL and moves on. Resulting bug: on an ASIC flow the description can be built out of flip-flops, producing an area and timing result that is wrong by an enormous margin, discovered late. On an FPGA flow a small deviation from the tool's expected template — an extra reset on the array, a second write port, an unusual enable structure — silently defeats block-memory inference. How to detect it: read the synthesis report's memory inference section and the area breakdown. This is a report to read deliberately, not a thing to notice by accident. How to prevent it: know the target before writing the array. On ASIC flows, instantiate the compiled macro the flow expects; on FPGA flows, follow the tool's template exactly and verify inference happened.

Forgetting that the port is shared. Wrong mental model: a memory can service whatever asks it. What the engineer does: connects two requesters to one array with no arbitration, or with arbitration that has no backpressure path. Resulting bug: accesses are silently dropped or overwritten. With no stall, the losing requester believes it was served, so its data is stale or its write never happened — and the corruption appears far from the memory. How to detect it: look for a requester with no way to be told to wait. An interface with a request and no acceptance signal cannot express contention, which means contention is being ignored rather than handled. How to prevent it: arbitration and backpressure together, as in §6, with a defined policy and a bound on how long a loser can be starved.

Believing "static" means "persistent". Wrong mental model: SRAM does not need refreshing, so it keeps its contents. What the engineer does: assumes array contents survive a power-down or a power-gated sleep state. Resulting bug: a block that power-gates its SRAM and then resumes as though its buffers still held data. The failure is intermittent, depends on power-management behaviour, and is very hard to reproduce from functional stimulus alone. How to prevent it: treat retention across power states as an explicit design requirement — either the array is kept powered, or its contents are saved and restored, or the design must tolerate losing them.

Resetting the array because it looks like state. Wrong mental model: all state needs reset. Resulting bug: on an FPGA target, a reset on the array is one of the classic reasons block-memory inference fails, so the design silently consumes a large amount of general logic instead. On an ASIC target the reset distribution to every bit is an area and timing cost for values that are written before they are read. How to prevent it: Chapter 1.2 §9's test — reset what is read before it is written. In §5 that is rdata_valid and nothing else.

12. Debugging — A Cache-Like Buffer Returns Stale Data

Symptom. A block that buffers data in an on-chip array occasionally consumes a value that is one access old. It is intermittent, and it gets markedly worse when the block is busy.

Hypotheses, ordered by how cheaply they can be discriminated.

Hypothesis 1 — the consumer is ignoring read latency. Evidence to look for: the cycle in which rdata is consumed is the same cycle the address was presented. Discriminator: if the consumer never samples rdata_valid, this is almost certainly it, and the "worse when busy" symptom is explained by busier traffic producing more back-to-back reads where the stale value differs from the correct one. In quiet periods consecutive reads often return the same address's data, which is why the bug hides.

Hypothesis 2 — a stalled access was treated as completed. Evidence to look for: a cycle where rd_req was high and rd_stall was also high, followed by the consumer proceeding anyway. Discriminator: the correlation with business is even stronger here than for hypothesis 1, because stalls only happen under contention. Look for whether the requester's state machine has a path out of its wait state that does not depend on the array answering.

Hypothesis 3 — the valid flag is wrong rather than the data. Evidence to look for: rdata_valid high for two consecutive cycles, or high in a cycle with no preceding read. Discriminator: P1 and P2 in §10 separate this from the first two hypotheses immediately — if either fires, the contract is broken on the memory side and the consumer is innocent.

Hypothesis 4 — arbitration is dropping accesses. Evidence to look for: a request that is asserted, not stalled, and not followed by an access on the array port. Discriminator: P3 and a simple count — requests accepted versus accesses issued — settle it. A mismatch means the arbiter is losing accesses rather than deferring them, which is a different and worse bug.

Hypothesis 5 — the write never happened. Evidence to look for: at the write cycle, en and we both high with the expected address and data. Discriminator: if the read path is correct and the data is still old, the write is the suspect, and the usual cause is an enable qualified by a condition — a stall, a flush, a full buffer — that was not expected to be active.

Root-cause discrimination in one step. Count, over a window, reads issued, valids returned, and stalls asserted. If valids equal reads and the consumer still sees stale data, the bug is on the consumer side (hypotheses 1 or 2). If valids do not equal reads, the bug is on the memory or arbiter side (3 or 4). That single comparison splits the space in half before any waveform is opened, which is the habit worth taking from this section.

13. Interview Reasoning

"Why does SRAM not need refreshing, and what does it pay for that?" Because the cell is a bistable loop actively driven by transistors connected to the supply: the state continuously restores itself, so nothing leaks away and nothing needs periodic rewriting. It pays in transistor count per bit — several devices per cell, replicated at every bit — and in static power, since the mechanism is continuous drive. A strong answer notes the corollary: "static" is a claim about maintenance while powered, not about persistence; remove power and the data is gone.

"Why is a register file not just a small SRAM?" They are different structures even when built from similar cells. A register file gives each entry its own access path and provides several ports, so it can deliver multiple operands and accept a result in one cycle. An SRAM array shares its decoder, sense amplifiers and control across the whole array and therefore serves one access per port per cycle. The first buys concurrency and pays in area and routing per entry; the second buys density and pays in exclusivity — which is exactly why an SRAM access has to be scheduled and can be made to wait.

"You have a single-port array and two requesters. What does the interface need?" Arbitration, a defined policy, a backpressure signal, and a starvation bound. The signal is the part engineers forget: without a way to tell the loser it was not served, contention becomes silent data corruption. The policy matters because "write wins" can starve reads under a write-heavy stream, so something — a fairness rule, an ageing counter, a reserved slot — has to bound the wait. This is the small version of what a memory controller does.

"What is the risk in writing a deep memory array as plain RTL?" That what is built is not what was intended. Whether a description becomes a dense memory depends on the target technology, the tool, the dimensions and the coding style; the same source can become a block memory on one target and a very large pile of flip-flops on another. The engineering answer is not a coding trick but a process one: know the target, follow its expected template or instantiate its macro, and confirm from the synthesis report rather than from the source.

"An access to your array sometimes takes longer than its stated latency. Is the memory broken?" Probably not — this is the distinction worth having. The array's own latency is fixed by its design; what varies is the waiting in front of it when something else holds the port. Observed access time is storage time plus contention time, and only the first is a property of the memory. That decomposition is the foundation of everything in Chapter 1.8, and a candidate who reaches for it unprompted is reasoning about memory systems rather than about memories.

14. Engineering Check

A design needs an on-chip buffer of a few thousand words. One requester writes it from a streaming source; another reads it for processing. The reader currently assumes its data is available in the cycle it presents an address.

Work through these before reading on.

1. What breaks first, and why? The reader's assumption. A synchronous array answers a cycle after the address is presented, so a consumer expecting same-cycle data consumes the previous read's value. It will often look plausible, which is why the bug is found late.

2. What does the interface need in order to be correct? An explicit validity signal, and a consumer that samples on it. Also an acceptance or stall signal, because with two requesters on one port some accesses will not happen in the cycle they were requested. An interface that cannot express "not yet" cannot be used correctly under contention.

3. Who arbitrates, and what is the cost of the obvious policy? Something in front of the array must choose. "Write wins" is simple and lets a sustained write stream starve the reader indefinitely; "read wins" inverts the problem and can back up the streaming source until it overflows. Either policy needs a bound — alternation, ageing, or a reserved slot — and choosing it is the design work.

4. Why is this buffer SRAM rather than a register file? Because a few thousand words with per-entry access paths and multiple ports would be unaffordable in area and unroutable in practice, and nothing here needs several simultaneous accesses. The traffic is one write and one read per cycle at most, which is exactly the shape a shared-periphery array serves well. This is the tier decision of Chapter 1.1 made at block scale.

5. What changes if the buffer must hold a few billion words instead? Everything. At that capacity the array cannot be on the same die as the compute, so the storage is off-chip; the cell must be smaller than six transistors to be affordable at all; and the access stops being a single operation over a local port and becomes a transaction over an external interface. That is the next two chapters — and the rest of the curriculum.

6. What would you instrument to know whether the buffer is the bottleneck? Counters, not waveforms, first: reads issued, reads stalled, writes stalled, and cycles where the port was idle. If the port is rarely idle, the array's single port is the limit and the fix is structural — more banks, more ports, or less traffic. If the port is mostly idle and the reader still waits, the problem is elsewhere, and the memory has been wrongly accused. That distinction — is the resource saturated, or is the requester just waiting — is the core measurement of Chapter 1.8.

15. Summary

Static means the cell maintains itself: a bistable loop, actively driven, that needs no periodic rewriting while powered. It does not mean persistent — remove power and the value is gone. The classic cell wraps that loop in two access transistors, so six transistors hold one bit, connected to the array by a wordline that selects a row and a bitline pair that carries the data.

Reading SRAM is non-destructive, because the cell drives its own value and keeps holding it. That is why an SRAM access is architecturally a single operation with nothing to clean up afterwards, and it is the property the next chapter's technology gives away.

The density comes from the array, not the cell alone: the row decoder, the sense amplifiers, the write drivers and the control logic are built once and shared across many cells, so their cost per bit falls as the array grows. The price of sharing is exclusivity — one row at a time, one access per port per cycle — which turns memory access into something that must be scheduled, that can be made to wait, and that therefore needs backpressure in its interface.

The density limit is the cell itself. Several actively driven transistors per bit, replicated at every bit, cannot reach the density of a minimal storage element — and every cell being driven means power scales with how much storage exists rather than how much is used. That is why SRAM is the right technology for caches and on-chip buffers and the wrong one for a system's working memory.

And the practical lesson for an RTL or DV engineer: read latency and contention are now part of the interface. Observed access time has split into what the storage costs and what waiting for it costs — the decomposition the rest of this curriculum is built on.

16. What Comes Next

The trade has been made once. A cell smaller than a flip-flop, a periphery shared across thousands of bits, and an access that answers a cycle late instead of immediately — and the result is dense enough for a cache and nowhere near dense enough for main memory.

So the question repeats, more aggressively: what is the smallest thing that can store a bit at all, and what does a system have to accept in exchange? Chapter 1.4 builds it — one transistor and one capacitor — and works out the three consequences that follow, each of which is a reason the DDR curriculum has the modules it has: the stored charge leaks away, reading it destroys it, and an access stops being one operation and becomes a sequence.

Return to Registers for the tier above, or The Memory Hierarchy for the map. To build arrays rather than reason about them, Inferring RAM is the inference template and Read Timing and Read-During-Write is the latency and collision treatment. For the structure caches impose on this tier, see Cache Hierarchy Review. The full path is on the DDR tutorials index.

Continue learning

Standards & specifications

Governing standard
JEDEC JESD79 (DDR SDRAM)(opens JEDEC Solid State Technology Association in a new tab)

Defines the DDR SDRAM device itself — signals, command encoding, mode registers, timing parameters and the initialisation sequence — one document per generation. Memory-controller microarchitecture, address-mapping policy, PHY training algorithms and board-level design are not specified by it.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the DDR curriculum.