DDR · Module 12
Burst Length
Burst length counts transfer positions. It is not a byte count, not a bus width, not a cache line, and not a number of clock cycles — and it is not a free menu you pick from.
Module 10 and Module 11 both used a burst and both deliberately took only what a transaction required, pointing forward at every turn. This module is where the burst itself is the subject.
It starts with the quantity everyone quotes and few define precisely:
Why does one column command produce multiple data transfers, and what exactly does "burst length" count?
It counts transfer positions. Not bytes, not bus width, not a cache line, and not clock cycles — and each of those confusions produces a different arithmetic error. More importantly, it is not a menu you choose from. It is a contract fixed by the generation, with a narrow, configured freedom inside it.
1. Why a Command Cannot Move One Word
Start with the cost of a command.
A column command occupies the command/address interface for a command event, and that interface is a shared, finite resource — Module 6 traced generations of pressure on it, to the point where DDR4 multiplexed former command pins into address bits to reclaim encoding space.
Now suppose each column command moved exactly one transfer's worth of data. Every data transfer would require its own command, so the command interface would need to sustain the same rate as the data interface. The command path would become the bottleneck, and every pin spent widening it would be a pin not spent on data.
Bursting breaks that coupling. One command, many transfers:
command rate = data rate / transfers per commandAnd there is a second, independent reason, which Chapter 10.4 §1 derived and which this chapter does not repeat: the array moves far more data per internal access than the interface carries per transfer, so the interface must spend several transfer opportunities delivering what one access produced.
2. What Burst Length Counts — and Six Things It Is Not
Burst length is the number of transfer positions a single column command moves.
That is the whole definition. A transfer position is one opportunity to move a bus-width of data — what this curriculum has been calling a beat since Chapter 10.3, and the term is used consistently here: one beat = one transfer position.
Now the six things it is not, because each is a real, common, and consequential error:
| Confusion | Why it is wrong | The error it produces |
|---|---|---|
| BL = bus width | BL counts positions; width counts bits per position | payload off by the width |
| BL = a byte count | "BL8" is eight transfers, not eight bytes | payload off by the width again |
| BL = cache-line size | a line may be one burst, or not | granularity assumptions that fail on another device |
| BL = clock cycles | DDR moves two transfers per clock period | every duration off by two |
| BL = prefetch depth | related historically, not identical, and generation-dependent | §3's confusions compound |
| BL = what the requester wanted | the requester's need and the transferred amount are different quantities | payload efficiency invisible — Chapter 12.4 |
The payload relationship is one line:
payload_bits = W × BL
where W is the effective data width of the interface under
discussion, and BL is the transfer count.
Masking (Chapter 6.11) and any error-coding overhead sit outside
this simple product.Worked, with the corpus's own verified figures (Chapter 4.5 and Chapter 4.7):
DDR4, 64-bit module, BL8:
64 bits × 8 transfers = 512 bits = 64 bytes
DDR5, 32-bit sub-channel, BL16:
32 bits × 16 transfers = 512 bits = 64 bytesSame payload, different burst length, because the width halved as the burst doubled. Which is exactly why BL alone tells you nothing about how much data moved — you need the width too, and that is the first confusion in the table made concrete.
And the clock-cycle confusion:
BL8 ÷ 2 transfers per CK period = 4 CK periods
BL16 ÷ 2 transfers per CK period = 8 CK periods3. The Burst Contract Is Generation-Specific
This section states what has been verified, and is equally explicit about what has not.
Verified, from vendor device documentation (Micron DDR4 device documentation and Samsung DDR4 device operations):
DDR4 provides three burst-length settings, selected in mode register MR0, bits A[1:0]:
| Setting | Meaning |
|---|---|
| fixed BC4 | every column command transfers the chopped burst |
| fixed BL8 | every column command transfers the full burst |
| on-the-fly (OTF) | the choice is made per command, on address bit A12/BC_n, registered with the READ or WRITE |
That third row is the interesting one. The per-command choice means a DDR4 controller does not have to pick one granularity for the whole run — it decides as it issues, using an address pin that carries a burst-length meaning on a column command. That is Chapter 7.1 §5's operand reuse appearing again, and it is why a decoder must know the operation before interpreting the bit.
Verified for DDR5 (Micron DDR5 technical material): the default burst length is BL16, up from DDR4's BL8, with BC8 as the chopped form, and the address bits registered with a READ or WRITE select the starting column and BC8-or-BL16 when the mode register enables it. JESD79-5 additionally defines an optional BL32 mode, which has its own burst-order treatment in the standard.
From this curriculum's own earlier verification: Chapter 4.4 established DDR3's full burst with a chop at half the full length.
So the shape of the contract is consistent across generations even though the numbers are not:
a native/full burst length
+ a chopped form, typically half
+ a configuration mechanism, sometimes per-command4. Full Burst and Burst Chop Are Not the Same Mechanism
A chopped burst is not an arbitrary early stop, and the difference matters.
An arbitrary early stop would mean: the requester has what it needs, so the transfer is abandoned partway. That is not what a chopped burst is. A chop is a defined, configured contract with its own transfer count, agreed by both ends before the transfer begins — and both ends count to the same number.
The distinction has three consequences:
The count is known in advance. A receiver knows how many transfers to expect from the configuration and the command, not by watching for the data to stop. Chapter 10.4 §3 established what happens when that count is wrong in either direction.
The array still did the full work. Chapter 4.4 §5 established this and it is not re-derived: chopping reduces what crosses the interface, and the array performed its full-width access regardless. A chop is mitigation of interface waste, not of array work.
And it is not free at the command level either. A chopped burst moves half the data for the same one command, so command efficiency halves — which is Chapter 12.4's subject and the reason chop is a trade rather than an improvement.
5. RTL — The Burst Contract
The engineering problem
Turn a decided burst mode into the contract both ends must count to: how many transfers, how wide the transfer index must be, and which index carries the final transfer — and report a mode the configured generation does not support.
Why hardware needs it
Every consumer of a burst — the beat sequencer, the beat collector, the monitor, the scoreboard — needs the same expected count, derived once. Deriving it independently in several places is how two components come to disagree, which Chapter 9.2 established is the hazard that produces silent divergence.
Classification
SYNTHESIZABLE EDUCATIONAL CONTROLLER RTL. Configuration logic, not a datapath.
What it models
Derivation of a burst contract — expected transfer count, transfer-index width, final index — from a decided mode under a configured generation, with support reported rather than assumed.
What it does NOT model
Mode selection (Chapter 4.4). Command decoding (Chapter 7.2). Beat generation or collection (Modules 10 and 11). Address order — Chapters 12.2 and 12.3 own it entirely. Timing (Modules 13, 14). The PHY.
Interface and parameter contract
// ─────────────────────────────────────────────────────────────────────────
// burst_transfer_contract
//
// Classification: SYNTHESIZABLE EDUCATIONAL CONTROLLER RTL (configuration).
//
// MODELS: derivation of a burst CONTRACT -- expected transfer count,
// transfer-index width, final index -- from an already-decided mode under
// a configured generation, with support REPORTED rather than assumed.
//
// IS NOT Chapter 4.4's burst_length_select, which takes what the requester
// WANTS and CHOOSES full-versus-chopped while measuring waste. This takes
// a DECIDED mode and states the resulting contract. Selection versus
// contract are different questions.
//
// IS NOT A BEAT COUNTER. Chapter 4.1's sdr_burst_engine generates beats,
// Chapter 10.4's collector receives them, Chapter 11.3's sequencer
// produces them. This moves no data and counts no beats.
//
// DOES NOT MODEL ADDRESS ORDER. Chapters 12.2 and 12.3 own it.
//
// THE PARAMETERS ARE EDUCATIONAL CONFIGURATION, NOT DEVICE VALUES. Which
// modes a device offers and how they are encoded come from that device's
// documentation for the configuration in force (Chapter 12.1 Section 3).
//
// MODELS NO PHYSICAL OR ANALOG BEHAVIOUR.
// ─────────────────────────────────────────────────────────────────────────
// A decided burst mode. NOT an encoding -- Chapter 7.2's decoder produces
// this from whatever the generation's mode register and per-command bit
// actually encode.
typedef enum logic [1:0] {
BMODE_FULL = 2'd0, // the generation's native/full burst
BMODE_CHOP = 2'd1, // the generation's chopped burst
BMODE_NONE = 2'd2 // no burst requested
} burst_mode_e;
module burst_transfer_contract #(
// Transfers in this generation's FULL burst. An EDUCATIONAL
// configuration value: 8 and 16 are the figures Module 4 verified for
// DDR3/DDR4 and DDR5 respectively, but the parameter is the contract,
// not a claim about any device.
parameter int FULL_TRANSFERS = 8,
// Transfers in the chopped burst. Half the full length in every
// organisation this curriculum has verified -- parameterised because
// that is a fact about devices, not a constraint this block imposes.
parameter int CHOP_TRANSFERS = FULL_TRANSFERS / 2,
// 0 models a generation with no chop available, which makes a chop
// request REPORTABLE rather than silently honoured.
parameter bit CHOP_SUPPORTED = 1'b1,
// DERIVED. Wide enough to hold FULL_TRANSFERS ITSELF, hence the +1: the
// contract states a COUNT, not a maximum index.
parameter int XFER_W = (FULL_TRANSFERS <= 1) ? 1 : $clog2(FULL_TRANSFERS + 1),
// Width of a transfer INDEX, which spans 0..FULL_TRANSFERS-1.
parameter int IDX_W = (FULL_TRANSFERS <= 1) ? 1 : $clog2(FULL_TRANSFERS)
) (
input logic mode_valid,
input burst_mode_e mode,
output logic contract_valid,
// How many transfers both ends must count to.
output logic [XFER_W-1:0] expected_transfers,
// The index that carries the final transfer: expected_transfers - 1.
output logic [IDX_W-1:0] last_index,
// A chop was requested where this configuration provides none. REPORTED,
// NOT CLAMPED: silently promoting it to a full burst would transfer
// twice what the requester asked for, and silently truncating would
// transfer half. Both are wrong and neither is visible.
output logic mode_unsupported
);
// ── Elaboration legality. A burst of zero transfers is not a short
// burst; it is a command that moves nothing, which is not a burst.
if (FULL_TRANSFERS < 1) begin : g_ft
initial $fatal(1, "burst_transfer_contract: FULL_TRANSFERS must be >= 1");
end
if (CHOP_SUPPORTED && (CHOP_TRANSFERS < 1)) begin : g_ct
initial $fatal(1, "burst_transfer_contract: CHOP_TRANSFERS must be >= 1 when chop is supported");
end
// A chop that is not shorter than the full burst is not a chop.
if (CHOP_SUPPORTED && (CHOP_TRANSFERS >= FULL_TRANSFERS)) begin : g_cf
initial $fatal(1, "burst_transfer_contract: CHOP_TRANSFERS must be < FULL_TRANSFERS");
end
logic chop_requested;
assign chop_requested = mode_valid && (mode == BMODE_CHOP);
assign mode_unsupported = chop_requested && !CHOP_SUPPORTED;
assign contract_valid = mode_valid
&& (mode != BMODE_NONE)
&& !mode_unsupported;
always_comb begin
// Default to the full contract. A default that is a LEGAL burst is
// deliberate: an unrecognised mode with contract_valid low must not
// also carry a nonsensical count, because a consumer reading the
// count without the qualifier would then transfer nothing.
expected_transfers = XFER_W'(FULL_TRANSFERS);
if (chop_requested && CHOP_SUPPORTED)
expected_transfers = XFER_W'(CHOP_TRANSFERS);
end
// The final index. Safe because expected_transfers is at least 1 in
// every branch above.
assign last_index = IDX_W'(expected_transfers - XFER_W'(1));
endmoduleState, combinational and sequential behaviour
No state, no clock, no reset. The contract is a function of the mode and the configuration — and that is the design: a contract that could change between the command and the transfers would be a contract neither end could rely on.
Bit-level derivation
At FULL_TRANSFERS = 8, CHOP_TRANSFERS = 4:
mode | contract_valid | expected_transfers | last_index | mode_unsupported |
|---|---|---|---|---|
| FULL | 1 | 8 | 7 | 0 |
| CHOP (supported) | 1 | 4 | 3 | 0 |
CHOP (CHOP_SUPPORTED = 0) | 0 | 8 | 7 | 1 |
| NONE | 0 | 8 | 7 | 0 |
mode_valid = 0 | 0 | 8 | 7 | 0 |
XFER_W is 4 and IDX_W is 3, and the difference is the point: a count of 8 needs four bits; an index spanning 0–7 needs three. Sizing both from $clog2(FULL_TRANSFERS) is the standard off-by-one here, and it fails only on the final transfer of every burst — Chapter 10.4 §12's exercise 2 in a new place.
How to simulate, and expected output
Drive every row of the table. Then:
CHOP_SUPPORTED = 0 with a chop request must give mode_unsupported and contract_valid low — and the count must still be a legal value, so that a consumer that ignores the qualifier transfers a full burst rather than zero. That is the conservative failure, and it should be checked deliberately.
FULL_TRANSFERS = 16, CHOP_TRANSFERS = 8 — the DDR5-shaped configuration. Confirm XFER_W = 5, IDX_W = 4, last_index = 15.
FULL_TRANSFERS = 1 is legal: a degenerate single-transfer burst with IDX_W = 1 through the guard, last_index = 0, and chop necessarily unsupported.
CHOP_TRANSFERS >= FULL_TRANSFERS must not elaborate.
Synthesis implications
A multiplexer and a subtractor. The whole block folds to constants once the mode is known, which is appropriate — a contract should cost nothing, and its value is that it exists in exactly one place.
Corner cases
FULL_TRANSFERS == 1 is legal and degenerate. CHOP_TRANSFERS defaults to half but is a parameter, because half is a fact about the devices this curriculum has verified rather than a rule this block should impose. Non-power-of-two FULL_TRANSFERS is expressible — IDX_W is sized by $clog2 so indices beyond the count exist and are simply never produced, which is a stated limitation rather than a bug.
Failure modes and debugging clues
mode_unsupported asserting means the configuration and the requester disagree about what this generation offers — check the configured generation before the requester. last_index one too small everywhere means XFER_W and IDX_W have been conflated.
Extension ideas
Adding a GEN parameter that derives FULL_TRANSFERS and CHOP_SUPPORTED rather than taking them separately would make an illegal combination unelaborable — attractive, and it embeds device facts in RTL, which §3's callout argues against. Keeping them as configuration keeps the device facts where they belong: in the device's documentation.
Limitations
One contract at a time. It does not know the generation — it is configured with a contract's shape and cannot tell you whether that shape is real for your device. And it says nothing about order, which is the next two chapters.
6. One Command, Many Transfers
Read the last two arrows together. The data interface carries N transfers, and nothing on the interface announces N. Both ends derived it from the same configuration and the same command — which is exactly the arrangement Chapter 11.2 §1 described for the launch relationship, and it fails the same way: if the two ends compute different contracts, nothing objects.
That is why the contract is a first-class object rather than a constant scattered through several blocks. One derivation, one place, consumed by everyone.
7. Four Assertions Worth Writing
// P1 -- a valid contract always states a legal, non-zero transfer count.
// The property a consumer relies on: it may read expected_transfers and
// transfer that many without checking for zero, which would otherwise be
// a silent no-transfer burst.
property p_valid_contract_is_nonzero;
@(posedge clk)
contract_valid |-> (expected_transfers >= XFER_W'(1))
&& (expected_transfers <= XFER_W'(FULL_TRANSFERS));
endproperty
assert property (p_valid_contract_is_nonzero);
// P2 -- the final index and the count agree. These are two derived views
// of one number, and a consumer that uses the index to detect the last
// transfer while another uses the count to size a buffer will disagree by
// one if this ever fails.
property p_last_index_matches_count;
@(posedge clk)
contract_valid |-> (last_index == IDX_W'(expected_transfers - XFER_W'(1)));
endproperty
assert property (p_last_index_matches_count);
// P3 -- an unsupported mode never produces a valid contract. This is what
// stops a chop request being silently promoted to a full burst on a
// configuration that has no chop -- transferring twice what was asked for,
// with nothing reporting it.
property p_unsupported_never_valid;
@(posedge clk)
mode_unsupported |-> !contract_valid;
endproperty
assert property (p_unsupported_never_valid);
// P4 -- a chopped contract really is shorter. Trivial to state and it
// catches a parameter error that is otherwise invisible: a CHOP_TRANSFERS
// equal to FULL_TRANSFERS makes chop a no-op that still reports success,
// so every efficiency measurement built on it is wrong.
property p_chop_is_shorter_than_full;
@(posedge clk)
(contract_valid && (mode == BMODE_CHOP))
|-> (expected_transfers < XFER_W'(FULL_TRANSFERS));
endproperty
assert property (p_chop_is_shorter_than_full);What these prove. P1 gives a consumer permission to act on the count. P2 is the one that earns its place — the count and the index are the classic off-by-one pair in burst logic, and a mismatch fails only on the final transfer. P3 forbids silent promotion. P4 catches a parameter error whose consequence is a whole efficiency study being meaningless.
What these do not prove. Nothing here says the contract matches the device. The parameters are configuration, and a block configured for a burst length the device does not use satisfies every property while both ends count to different numbers — which is §9's symptom and is only catchable against the programmed mode registers. Nothing says the transfers actually occurred — this block moves nothing, and Modules 10 and 11 own that. Nothing says anything about order, which is deliberate and is the next two chapters. And nothing proves anything physical.
8. DV — The Contract Is the Agreement
The verification framing for the whole module starts here, and it generalises the direction-specific checking of Modules 10 and 11.
A burst is an agreement between two ends about a count, with nothing on the wire stating it. So a monitor's first job is not to watch transfers — it is to establish what the contract should have been, from the configuration and the command, before any transfer arrives.
That gives a direction-neutral checking structure:
observe column command
→ derive the contract (count, last index)
observe transfers
→ count them against the contract
final transfer
→ check the count matched and the last marker alignedAnd it means one monitor can check both directions. A read's transfers arrive and a write's depart, but the contract question is identical: were there exactly N, and was the final one marked at index N−1? Chapter 10.4's collector and Chapter 11.3's sequencer are the direction-specific halves; the contract is what they share.
Three requirements:
Derive the monitor's contract from the programmed mode registers, not from the design's parameter. Chapter 10.2 §8's argument: a checker sharing the design's constant cannot detect a wrong constant, and here the wrong constant means both ends counting differently.
Check the count and the last marker separately. They are independent failures — the right number of transfers with the marker in the wrong place, or the marker correctly at the end of the wrong number. Chapter 10.4 §7's P1 required both for exactly this reason.
Cover the chopped contract deliberately. A regression that only ever runs full bursts has never exercised the chop path, and on a device with on-the-fly selection the chop decision changes per command — so the coverage goal is not "chop was used" but "chop and full were used adjacently."
9. Debugging — The Receiver Expects a Different Number of Transfers
Symptom. Commands are correct and well-formed. Data moves. But one end stops before the other — a receiver reports missing transfers, or a surplus arrives with nothing expecting it.
Candidate mechanisms.
- The configured burst length differs between the two ends — one is set for the full burst, the other for the chop.
- The generation's contract is not what the design assumes: a controller written for one generation's native burst against a device with a different one.
- On-the-fly selection is in use and one end is ignoring the per-command bit, so it uses the mode-register default for every command.
XFER_WandIDX_Whave been conflated, so the last marker is one transfer early.- Transfers and clock cycles have been confused, and the "missing" transfers are an artefact of counting the wrong quantity.
Evidence to collect. The programmed mode-register burst setting, read back rather than assumed. The per-command burst bit on the failing commands, if the generation has one. The expected count from each end separately. The index at which the last marker appeared. And whether the discrepancy is a factor of two or an off-by-one — that single observation splits the list.
Discriminator.
- Is the discrepancy a factor of two? Mechanism 1 or 3 — a full-versus-chop disagreement. Check whether the generation offers per-command selection: if it does, mechanism 3 is more likely, because the mode register agrees and only the per-command bit is being missed.
- Is it exactly one transfer? Mechanism 4 — the count/index confusion. P2 catches it, and its absence from the assertion set is the finding.
- Is the ratio exactly two but in cycles rather than transfers? Mechanism 5, and nothing is actually wrong with the burst. §2's table.
- Do both ends agree with their own configuration and still disagree with each other? Mechanism 2 — the contract shape itself is wrong for the device.
- Compare the two ends' expected counts directly. If they differ, everything downstream is a symptom; if they agree, the fault is in the transfer path and belongs to Module 10 or 11.
Responsible layer. All of it is configuration or controller logic. None is a device fault — the device transfers what its configuration says, and a disagreement about the contract is always above it.
Fix. Single-source the contract, derive it from the programmed registers, and add P2. And add a chop-adjacent-to-full test, because mechanism 3 is invisible to a regression that never changes the per-command bit.
10. Common Misconceptions
"BL8 means eight bytes."
Why it is tempting: the number is small and byte-shaped, and "length" suggests a size.
Concrete failure: a buffer sized at 8 bytes for a burst that delivers 64. Payload arithmetic wrong by the bus width.
Correct model: BL counts transfer positions. payload_bits = W × BL. §2.
Prevention: never state a burst length without the width beside it.
"Burst length is the number of clock cycles."
Why it is tempting: teaching waveforms — including this curriculum's — draw one transfer per cycle for readability.
Concrete failure: every duration derived from a burst is wrong by a factor of two.
Correct model: DDR moves two transfers per clock period, so BL ÷ 2 = CK periods. §2.
Prevention: write the units in the arithmetic.
"Burst length is the width of DQ."
Why it is tempting: both are "how much data," and both are small powers of two.
Concrete failure: a model where a wider bus means a longer burst, which gets the DDR4-to-DDR5 transition exactly backwards — the width halved and the burst doubled.
Correct model: width is bits per position; BL is positions. §2's worked example.
Prevention: the DDR4/DDR5 comparison — same payload, different BL, different width.
"BL4, BL8 and BL16 are universal DDR choices."
Why it is tempting: those names all appear in DDR material, so they read like a menu.
Concrete failure: a controller parameterised over all three against a generation offering only one native length and one chop. Configurations that cannot exist, and failures that mean nothing.
Correct model: a generation fixes a native burst and offers a defined chop. §3.
Prevention: scope every burst-length claim to a generation, or do not make it.
"Burst chop is just an arbitrary early stop."
Why it is tempting: it transfers fewer positions, so it looks like stopping early.
Concrete failure: a design that tries to truncate a burst when the requester has enough. There is no such mechanism — both ends counted to a number agreed in advance.
Correct model: a chop is a defined contract with its own count. §4.
Prevention: ask what both ends counted to. If the answer is not a configured number, it is not a chop.
"Prefetch depth and burst length are the same thing."
Why it is tempting: they have matched numerically in several generations.
Concrete failure: an engineer assumes a chopped burst reduces the array's work, and cannot explain why chopping saves less than expected — Chapter 4.4 §5's point.
Correct model: prefetch is what the array yields per access; burst length is what the interface transfers per command. Related historically, generation-dependent, not synonyms. §1.
Prevention: state which side of the interface a number describes.
"Longer bursts are always better."
Why it is tempting: more data per command is obviously more efficient.
Concrete failure: a requester needing a small access is handed a long burst, so command efficiency is excellent and most of the transferred data is discarded.
Correct model: more than one efficiency exists and they trade against each other. Chapter 12.4.
Prevention: name which efficiency before claiming an improvement.
11. Interview Reasoning
"Why does DDR use bursts?"
Two independent reasons that point the same way. Command amortisation: if each column command moved one transfer, the command interface would have to sustain the same rate as the data interface, and it would become the bottleneck — every pin spent widening it is a pin not spent on data. Prefetch: the array yields far more per internal access than the pins carry per transfer, so the interface must spend several transfer opportunities on what one access produced. They are often conflated, and keeping them apart matters because they scale differently — one is about how often you can issue, the other about how much one access yields.
"What exactly does burst length count, and why is BL8 not eight bytes?"
It counts transfer positions — opportunities to move a bus-width of data. So the payload is width × burst length, and BL8 on a 64-bit module is 512 bits, or 64 bytes, not 8. The cleanest demonstration is the DDR4-to-DDR5 transition: DDR4's 64-bit module with BL8 and DDR5's 32-bit sub-channel with BL16 both move 64 bytes. Same payload, twice the burst length, half the width — which is why a burst length quoted without its width tells you nothing about how much data moved.
"What is the difference between burst length and burst chop?"
A chop is not an early stop — it is a defined contract with its own transfer count, agreed by both ends before the transfer begins. That distinction has real consequences: the count is known in advance rather than discovered by watching the data stop; the array still performed its full-width access, so chopping reduces interface waste and not array work; and command efficiency halves, because the same one command now moves half the data. There is no mechanism for a controller to simply stop a burst when the requester has enough.
"Why must you generation-scope a burst-length claim?"
Because a given generation fixes a native burst length and offers a narrow, defined chop around it — the freedom is far smaller than the list of burst lengths that have existed across SDRAM history suggests. DDR4, for example, provides fixed-chop, fixed-full, or an on-the-fly choice made per command on an address bit, which is a different shape of freedom from a mode-register-only setting. A controller parameterised over a menu of burst lengths will produce configurations that cannot exist on the target device, and failures from those configurations mean nothing.
"Two ends of a link disagree about how many transfers a burst has. How does that happen with no error?"
Because nothing on the interface states the count. Both ends derive it from the same configuration and the same command, with no handshake — so if they derive it differently, one simply stops before the other and nothing objects. The likely causes are a mode-register disagreement, a per-command burst bit that one end ignores, or a count-versus-index off-by-one. The diagnostic is the shape of the discrepancy: a factor of two points at full-versus-chop, exactly one transfer points at the index confusion, and a factor of two in cycles rather than transfers means nothing is wrong at all.
12. Engineering Exercise
FULL_TRANSFERS = 8, CHOP_TRANSFERS = 4, CHOP_SUPPORTED = 1.
1. Compute XFER_W and IDX_W. Why are they different, and what breaks if both are $clog2(8)?
2. A 64-bit interface runs BL8; a 32-bit interface runs BL16. Compare payloads and burst durations in CK periods.
3. Give expected_transfers, last_index, contract_valid and mode_unsupported for: FULL; CHOP; CHOP with CHOP_SUPPORTED = 0; NONE.
4. A receiver expects 8 transfers and 4 arrive, on a generation with per-command burst selection. Name the two most likely causes and the check that separates them.
5. Reconfigure for a DDR5-shaped contract. Give all four derived values.
6. Why is a chopped burst not the same as the controller stopping a burst early? Give a consequence for a receiver.
13. Summary
Bursts exist for two independent reasons: command amortisation, so the command interface need not keep pace with the data interface; and prefetch, because the array yields more per access than the pins carry per transfer. They are often conflated and they scale differently.
Burst length counts transfer positions — beats — and nothing else. payload_bits = W × BL. It is not a byte count, not a bus width, not a cache line, not a cycle count, and not the prefetch depth.
The clearest demonstration is DDR4 against DDR5: 64-bit × BL8 and 32-bit × BL16 both move 64 bytes. A burst length quoted without its width says nothing about how much data moved.
The contract is generation-specific and narrower than the usual menu. A generation fixes a native burst and offers a defined chop — and the configuration mechanism differs, with DDR4 offering a per-command choice as well as fixed settings.
A chop is a contract, not an early stop. The count is agreed in advance, the array still did its full work, and command efficiency halves.
And nothing on the interface states the count. Both ends derive it independently from the same configuration, so a disagreement produces one end stopping before the other with no error anywhere — which is why the contract deserves to exist once, in one place, derived from the programmed registers.
14. What Comes Next
The contract says how many transfers. It says nothing about which positions they visit.
Chapter 12.2 — Sequential Burst takes that question up, and it is less obvious than it sounds. A burst does not simply increment an address: it visits positions within a burst-local window, wrapping inside it, so a burst that starts partway through the window ends by visiting positions before its own starting point.
That window has a boundary, and the boundary is the reason a burst cannot wander into another row — which matters, because Module 9 established that a row is opened by an activate and a column command carries no row address at all.
Return to Burst Reads and Burst Writes for the transactions that consume this contract, DDR3 for burst chop's efficiency argument and the selection block, DDR4 and DDR5 for the prefetch evolution behind the numbers, and Read (RD / RDA) for the command that carries a per-command burst bit alongside its column.
Continue learning
Related tutorials
- Related topic
Burst Reads
One read command returns several transfers because the array moves more data per access than the interface is wide. That makes beat counting a correctness obligation, not bookkeeping.
- Related topic
DDR (DDR1)
Double data rate doubles transfer opportunities per clock cycle, not the clock. Two mechanisms make that survivable: a 2n prefetch so a slow array can feed a fast interface, and a source-synchronous DQS strobe so data carries its own timing.
- Related topic
DDR2
A clock cycle has only two edges, so DDR2 raised bandwidth by deepening prefetch to 4n — making prefetch depth the frequency ratio between interface and core. Raising the rate on a multi-load bus then forced termination onto the die.
- Related topic
DDR3
DDR3 took prefetch to 8n and paid twice: a fly-by command and clock bus that deliberately skews the clock at every device, and termination that had to calibrate itself against an external reference. Between them they made training mandatory.
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.
