DDR · Module 12
Data-Transfer Efficiency
Bursting buys command efficiency and can spend payload efficiency to get it. Those are different quantities that trade against each other, and collapsing them into a single percentage is how architectural arguments go wrong.
Three chapters have established what a burst is: how many transfers, which positions, in what order.
This chapter asks what it is all for — and the honest answer is that the question has more than one answer:
When does bursting actually improve efficiency, what overhead remains, and which efficiency are you talking about?
Bursting buys command efficiency, and it can spend payload efficiency to get it. Those are different quantities, they trade against each other, and collapsing them into a single percentage is how architectural arguments go wrong — because a design can improve one while degrading another and a single number will report it as an improvement.
1. What Bursting Actually Buys
Start with the quantity bursting was invented to improve. Chapter 12.1 §1 derived it: if each column command moved one transfer, the command interface would have to sustain the data interface's rate.
So the first measure is about commands:
command efficiency = useful payload delivered
────────────────────────
number of column commandsBursting improves this directly and by construction. Eight transfers per command instead of one is eight times the payload per command, and that is not a claim requiring measurement — it follows from the contract.
Which is exactly why it is the wrong number to stop at. A measure that a design improves by definition cannot tell you whether the design is good. It tells you the mechanism is working, not that it is helping.
2. Four Measures, Kept Apart
Here are the four quantities that get called "efficiency," with what each divides by. The denominators are the whole distinction.
| Measure | Numerator | Denominator | Answers |
|---|---|---|---|
| A — Command | useful payload | column commands | how much data per command? |
| B — Slot utilisation | transfer slots carrying data | transfer slots available | how busy is the data bus? |
| C — Payload | bytes the requester wanted | bytes transferred | how much of what moved was wanted? |
| D — System | useful bytes delivered | everything that could have been | what did the memory system achieve? |
They are independent in both directions, and the two cases worth internalising are:
High A, low C. A long burst delivers a large payload per command — excellent command efficiency — and if the requester wanted a quarter of it, three quarters of the transferred bytes were waste. The command interface is being used beautifully to move data nobody asked for.
High B, low C. The data bus is busy on almost every available slot, so utilisation looks superb. If most of those slots carry unwanted bytes, useful throughput is poor. §8's debugging case is exactly this, and it is the most common way a performance investigation starts in the wrong place.
And D is not the sum of the others. System efficiency includes row-state work (Module 9), bus turnaround, refresh, bank conflicts and scheduling — each owned by a later module — and it can be dominated by any one of them while A, B and C all look healthy. §4 names them and quantifies none.
3. The Arithmetic
Payload efficiency is the measure with the most direct connection to the burst contract:
payload_efficiency = useful_bytes / transferred_bytes
and for one burst:
transferred_bytes = (W / 8) × BLWorked, with Chapter 12.1 §3's verified figures and a hypothetical request size:
EDUCATIONAL EXAMPLE — the request size is chosen to illustrate,
not measured from any system.
DDR4-shaped: 64-bit interface, BL8
transferred = (64/8) × 8 = 64 bytes
requester wanted 64 bytes → 64/64 = 100%
requester wanted 32 bytes → 32/64 = 50%
requester wanted 8 bytes → 8/64 = 12.5%The third line is the one to sit with. Nothing is broken. The command was efficient, the bus was busy, the burst was well-formed — and seven eighths of what moved was discarded.
Slot utilisation divides differently:
slot_utilisation = slots_carrying_data / slots_available
where a "slot" is one transfer position on the data interface
in the observation window, whether or not anything used it.Stating what the denominator includes is mandatory, because it is where these numbers are quietly made to look good. A window that excludes refresh intervals, or excludes cycles where the bus was turning around, measures a different thing from one that does not — and neither is wrong, but comparing them is.
4. The Overhead That Remains
Bursting removes command-rate pressure. It does not remove everything, and naming what is left prevents optimising the wrong layer.
Row-state work. Module 9 established that a miss costs one row-state transition and a conflict costs two, serialised — and no burst length changes that. A workload dominated by conflicts is bounded by row transitions, and lengthening bursts addresses nothing.
Bus turnaround. Module 10 and Module 11 established that the data bus changes ownership between directions, and the handover is scheduled rather than signalled. Alternating reads and writes spends slots on direction changes.
Refresh. Chapter 7.5 established that refresh occupies the device. Those slots are unavailable regardless of how efficiently everything else behaves.
Bank conflicts and scheduling. Module 16 and Module 17 own these.
5. RTL — Counting the Measures Separately
The engineering problem
Count, over an observation window, the quantities the four measures need — separately, so that no ratio can be formed from mismatched denominators.
Why hardware needs it
These quantities cannot be reconstructed after the fact from a trace of useful length. And the whole argument of this chapter is that they must not be pre-combined, because a block that reports one percentage has already made the mistake.
Classification
SYNTHESIZABLE EDUCATIONAL CONTROLLER RTL — instrumentation.
What it models
Window-based accumulation of: transfer slots available, slots carrying data, bytes transferred, bytes the requester wanted, and bursts completed — with saturation and an explicit validity flag.
What it does NOT model
Ratios. Timing, refresh, turnaround or row state (Modules 9, 13, 14, 16, 17). The burst contract's origin (Chapter 12.1). Order (Chapters 12.2, 12.3). Data values. And it cannot tell a wanted byte from an unwanted one — that is an input, supplied by whoever knows the request.
Interface and parameter contract
// ─────────────────────────────────────────────────────────────────────────
// burst_efficiency_monitor
//
// Classification: SYNTHESIZABLE EDUCATIONAL CONTROLLER RTL
// (instrumentation).
//
// MODELS: window-based accumulation of the quantities Chapter 12.4
// Section 2's measures need -- slots available, slots used, bytes
// transferred, bytes wanted, bursts completed -- kept SEPARATE so no ratio
// can be formed from mismatched denominators.
//
// COMPUTES NO RATIO, NO PERCENTAGE, NO SCORE. There is no divider. Counts
// are reported; ratios are formed by whoever knows what the denominators
// mean (Chapter 9.6 Section 5's argument).
//
// IS NOT Chapter 4.4's burst_length_select, which measures PER-REQUEST
// waste from a full-versus-chop SELECTION, counted in requests. This
// measures TRANSFER SLOTS over a window, including slots where nothing was
// transferred -- which that block cannot see because it never observes the
// bus.
//
// Counters SATURATE, never wrap: a wrapped counter reports a flattering
// ratio, and an instrument that errs toward what its reader hopes for is
// worse than no instrument.
//
// MODELS NO PHYSICAL OR ANALOG BEHAVIOUR.
// ─────────────────────────────────────────────────────────────────────────
module burst_efficiency_monitor #(
// Bytes moved by one transfer slot: the interface width in bytes.
parameter int BYTES_PER_SLOT = 8,
// Observation window, in transfer slots. A WINDOW is required because
// utilisation is meaningless without a stated denominator (Section 3).
parameter int WINDOW_SLOTS = 1024,
parameter int ACC_W = 24,
parameter int WIN_W = (WINDOW_SLOTS <= 1) ? 1 : $clog2(WINDOW_SLOTS + 1),
// Wide enough to hold one slot's byte count.
parameter int BYT_W = (BYTES_PER_SLOT <= 1) ? 1 : $clog2(BYTES_PER_SLOT + 1)
) (
input logic clk,
input logic rst_n,
// ── One transfer slot elapsed. Asserted for EVERY slot in the window,
// used or not -- this is the denominator of measure B, and gating it
// on activity is the most common way utilisation is inflated.
input logic slot_tick,
// A slot that actually carried data.
input logic slot_used,
// Bytes in this slot the requester actually wanted. Supplied by whoever
// knows the request -- typically derived from the byte mask (Chapter
// 6.11) and the request's size. Range 0..BYTES_PER_SLOT.
input logic [BYT_W-1:0] useful_bytes_this_slot,
// A burst completed, for measure A's denominator.
input logic burst_done,
// ── Raw counts. Section 2's four measures are formed from these by a
// consumer that states its denominators.
output logic [ACC_W-1:0] cnt_slots_available,
output logic [ACC_W-1:0] cnt_slots_used,
output logic [ACC_W-1:0] cnt_bytes_transferred,
output logic [ACC_W-1:0] cnt_bytes_useful,
output logic [ACC_W-1:0] cnt_bursts,
// The window has elapsed; counts are a complete observation.
output logic window_done,
// Sticky. Once any counter has clamped, every ratio from this set is
// wrong in a flattering direction.
output logic any_saturated,
output logic accounting_valid
);
if (BYTES_PER_SLOT < 1) begin : g_bps
initial $fatal(1, "burst_efficiency_monitor: BYTES_PER_SLOT must be >= 1");
end
if (WINDOW_SLOTS < 1) begin : g_win
initial $fatal(1, "burst_efficiency_monitor: WINDOW_SLOTS must be >= 1");
end
if (ACC_W < 2) begin : g_acc
initial $fatal(1, "burst_efficiency_monitor: ACC_W must be >= 2");
end
// The byte counters are incremented by ACC_W'(BYTES_PER_SLOT). If ACC_W
// cannot represent BYTES_PER_SLOT the cast TRUNCATES -- at ACC_W == 2
// and BYTES_PER_SLOT == 8 the increment becomes zero, so the transferred
// count never moves while every other counter does. Silent, and it makes
// payload efficiency read as zero for a perfectly healthy bus.
if ((1 << ACC_W) <= BYTES_PER_SLOT) begin : g_accfit
initial $fatal(1, "burst_efficiency_monitor: ACC_W too narrow to hold BYTES_PER_SLOT");
end
localparam logic [ACC_W-1:0] ACC_MAX = {ACC_W{1'b1}};
logic [WIN_W-1:0] win_q;
// ── Saturating add. Returns the clamped value with a clamp flag in the
// top bit, so the clamp is a value the design can act on rather than
// a condition inferred later.
function automatic logic [ACC_W:0] sat_add
(input logic [ACC_W-1:0] v, input logic [ACC_W-1:0] inc);
logic [ACC_W:0] sum;
begin
sum = {1'b0, v} + {1'b0, inc};
sat_add = sum[ACC_W] ? {1'b1, ACC_MAX} : {1'b0, sum[ACC_W-1:0]};
end
endfunction
logic [ACC_W:0] n_avail, n_used, n_xfer, n_useful, n_burst;
always_comb begin
n_avail = sat_add(cnt_slots_available, slot_tick ? ACC_W'(1) : ACC_W'(0));
n_used = sat_add(cnt_slots_used, (slot_tick && slot_used) ? ACC_W'(1) : ACC_W'(0));
// Bytes transferred counts the WHOLE slot when it is used: the
// interface moved a full width whether or not the requester wanted it.
// That is precisely what makes measure C meaningful.
n_xfer = sat_add(cnt_bytes_transferred,
(slot_tick && slot_used) ? ACC_W'(BYTES_PER_SLOT) : ACC_W'(0));
n_useful = sat_add(cnt_bytes_useful,
(slot_tick && slot_used) ? ACC_W'(useful_bytes_this_slot) : ACC_W'(0));
n_burst = sat_add(cnt_bursts, burst_done ? ACC_W'(1) : ACC_W'(0));
end
assign accounting_valid = !any_saturated;
assign window_done = slot_tick && (win_q == WIN_W'(WINDOW_SLOTS - 1));
always_ff @(posedge clk) begin
if (!rst_n) begin
cnt_slots_available <= '0;
cnt_slots_used <= '0;
cnt_bytes_transferred <= '0;
cnt_bytes_useful <= '0;
cnt_bursts <= '0;
win_q <= '0;
any_saturated <= 1'b0;
end else begin
cnt_slots_available <= n_avail [ACC_W-1:0];
cnt_slots_used <= n_used [ACC_W-1:0];
cnt_bytes_transferred <= n_xfer [ACC_W-1:0];
cnt_bytes_useful <= n_useful[ACC_W-1:0];
cnt_bursts <= n_burst [ACC_W-1:0];
if (n_avail[ACC_W] || n_used[ACC_W] || n_xfer[ACC_W]
|| n_useful[ACC_W] || n_burst[ACC_W])
any_saturated <= 1'b1;
if (slot_tick) begin
if (win_q == WIN_W'(WINDOW_SLOTS - 1)) win_q <= '0;
else win_q <= win_q + WIN_W'(1);
end
end
end
endmoduleState and sequential behaviour
Five counters, a window position, and a sticky saturation flag. Nonblocking throughout.
slot_tick is deliberately separate from slot_used. Counting only used slots would make utilisation unconditionally 100% — the denominator would equal the numerator — and that is the single most common way this measurement is made meaningless.
Reset behaviour
All counters and the window position clear; any_saturated clears. The flag is sticky within a run: once a counter has clamped, every ratio derived from the set is wrong, and clearing it would hide that the run's numbers are unusable.
Cycle-by-cycle trace
BYTES_PER_SLOT = 8, an eight-slot burst where the requester wanted 16 of the 64 bytes:
| Slot | slot_used | useful_bytes | bytes_transferred | bytes_useful |
|---|---|---|---|---|
| 0 | 1 | 8 | 8 | 8 |
| 1 | 1 | 8 | 16 | 16 |
| 2–7 | 1 | 0 | 24…64 | 16 |
| 8–15 | 0 | — | 64 | 16 |
After 16 slots: available 16, used 8, transferred 64 bytes, useful 16 bytes, bursts 1.
A consumer forms: slot utilisation 8/16 = 50%; payload efficiency 16/64 = 25%; command efficiency 16 useful bytes per command. Three different numbers from one observation, and none of them is "the efficiency."
How to simulate, and expected output
Drive the trace and check all five counters. Then:
slot_used high with slot_tick low must count nothing. The tick is the window's clock, and a used slot outside a tick is not a slot.
useful_bytes_this_slot equal to BYTES_PER_SLOT on every used slot gives payload efficiency of exactly 1 — the fully-used case, and the sanity check that the two byte counters can agree.
useful_bytes_this_slot of zero on every used slot gives a useful count of zero with a non-zero transferred count. This must not be treated as an error — it is a real and important measurement, and it is §8's symptom.
Drive past saturation with a small ACC_W — say 4 — and confirm any_saturated asserts, stays asserted, and accounting_valid goes low. This test is usually skipped and it is the one that matters, because a design never saturated in simulation will saturate in the lab.
WINDOW_SLOTS = 1 makes every tick a window boundary — the degenerate case, useful for checking window_done.
Synthesis implications
Five ACC_W registers with saturating adders plus a window counter — at ACC_W = 24, about 130 flops. Instrumentation that answers the first question of every bandwidth investigation, for a few hundred flops.
Corner cases
WINDOW_SLOTS == 1 is legal and degenerate. BYTES_PER_SLOT == 1 gives BYT_W = 1. useful_bytes_this_slot exceeding BYTES_PER_SLOT is not prevented by the port width when BYTES_PER_SLOT is not one less than a power of two — it is caught by §7's P1 instead, deliberately, because clamping it would silently repair a caller's bug. ACC_W = 4 is the right configuration for exercising saturation — and ACC_W narrower than BYTES_PER_SLOT needs does not elaborate, because the byte-increment cast would truncate to zero and the transferred count would silently never move.
Failure modes and debugging clues
Utilisation of exactly 100% across a long run almost always means slot_tick is gated on activity. cnt_bytes_useful exceeding cnt_bytes_transferred means the caller's useful-byte input is wrong — P1. any_saturated high invalidates everything and should be checked before any other number.
Extension ideas
Splitting the counters by direction turns this into a turnaround-aware monitor; splitting by bank connects it to Module 9's class distribution. Both are natural and both belong to later modules — the point of keeping this one narrow is that its denominators stay stateable.
Limitations
One window, one interface. It cannot see refresh, turnaround, row state or scheduling — §4's list — so it measures the data bus and not the memory system. And it depends entirely on the caller for the useful-byte count, which no property inside it can validate.
6. Useful, Wasted and Idle Slots
burst_efficiency_monitor — a busy bus and a poor payload
10 cyclesThree kinds of slot appear on that trace, and only two of them are visible to utilisation.
Wanted — the slot carried bytes the requester asked for. Wasted — the slot carried data, so utilisation counts it as used, and nobody wanted what it carried. Idle — no data at all.
Utilisation cannot distinguish the first two. It sees eight used slots out of sixteen available and reports 50%. Payload efficiency sees sixteen wanted bytes out of sixty-four transferred and reports 25%. Same observation, same window, two numbers that disagree about how well the interface is doing — and they are both correct, because they are answering different questions.
The slot class row is not a signal any hardware produces. It is the classification a reader applies, and it is drawn to make the point that the distinction between wanted and wasted exists only if something tells the monitor what was wanted — which is why §5's block takes the useful-byte count as an input it cannot validate.
And the idle slots are the ones §7's mechanism 5 is about. They are not a burst problem at all: something upstream was unable to issue, and no burst-length change addresses it.
EDUCATIONAL. One slot per cycle is a drawing convenience — a DDR interface moves two transfers per clock period — and no spacing here corresponds to any timing parameter.
7. Five Assertions Worth Writing
// P1 -- useful bytes can never exceed transferred bytes. The caller
// supplies the useful count, and a caller that over-reports makes payload
// efficiency exceed 100% -- a number that is obviously wrong and therefore
// gets explained away rather than investigated.
property p_useful_never_exceeds_transferred;
@(posedge clk) disable iff (!rst_n)
accounting_valid |-> (cnt_bytes_useful <= cnt_bytes_transferred);
endproperty
assert property (p_useful_never_exceeds_transferred);
// P2 -- used slots can never exceed available slots. The same guard on
// measure B, and it is what catches slot_tick being gated on activity:
// with that bug the two counters are equal, which this permits, so P2 is
// necessary and not sufficient -- Section 5's debugging note covers the
// rest.
property p_used_never_exceeds_available;
@(posedge clk) disable iff (!rst_n)
accounting_valid |-> (cnt_slots_used <= cnt_slots_available);
endproperty
assert property (p_used_never_exceeds_available);
// P3 -- transferred bytes and used slots agree exactly. They are two views
// of the same event, so a mismatch means one counter is being incremented
// on a different condition from the other -- which would make measures B
// and C silently incomparable.
property p_transferred_matches_used_slots;
@(posedge clk) disable iff (!rst_n)
accounting_valid
|-> ((ACC_W+4)'(cnt_bytes_transferred)
== (ACC_W+4)'(cnt_slots_used) * BYTES_PER_SLOT);
endproperty
assert property (p_transferred_matches_used_slots);
// P4 -- counters never decrease. A counter that falls has wrapped, which
// catches a saturating adder that does not saturate, independently of
// whether the flag was wired correctly.
property p_counters_are_monotonic;
@(posedge clk) disable iff (!rst_n)
(cnt_slots_available >= $past(cnt_slots_available))
and (cnt_bytes_transferred >= $past(cnt_bytes_transferred))
and (cnt_bursts >= $past(cnt_bursts));
endproperty
assert property (p_counters_are_monotonic);
// P5 -- saturation is sticky and honest. Once the numbers have stopped
// adding up they must never silently become trustworthy again.
property p_saturation_is_sticky;
@(posedge clk) disable iff (!rst_n)
$past(any_saturated) |-> any_saturated && !accounting_valid;
endproperty
assert property (p_saturation_is_sticky);What these prove. P1 and P2 bound the two ratios that matter most, catching a caller that over-reports. P3 is the one that keeps the measures comparable — two counters incremented on different conditions produce ratios that cannot be reasoned about together, and the failure is silent. P4 catches a non-saturating adder. P5 keeps the honesty sticky.
What these do not prove. Nothing here says the useful-byte count is correct — it is an input, and no property inside the block can validate the caller's notion of what the requester wanted. Nothing says the window's denominator is the right one: §3's point that a window excluding refresh measures a different thing, and that choice is outside this block. Nothing here is a performance claim — these are counts of data-bus activity, and §4 lists everything they cannot see. And nothing proves the measurement is representative: a perfectly accurate count over unrepresentative traffic tells you about the traffic.
8. Debugging — The Bus Looks Busy and Throughput Is Poor
Symptom. Slot utilisation is high — the data bus is carrying something on most available slots. Useful throughput measured at the requester is far lower than that suggests. No errors anywhere.
Candidate mechanisms.
- Payload efficiency is low: bursts are transferring far more than the requesters wanted. Measure C.
- The useful-byte count is wrong — the caller is reporting whole slots as useful when the request covered part of them.
slot_tickis gated on activity, so utilisation is inflated toward 100% and was never meaningful.- The bus is busy with retried or redundant traffic — the same bytes moving more than once.
- Utilisation and payload are both fine and the bottleneck is elsewhere: row conflicts, turnaround or refresh. §4.
Evidence to collect. All five counters as absolute values, not ratios. any_saturated. The window's definition — specifically whether the denominator includes refresh and turnaround slots. The requester's own view of bytes delivered, from outside this block. And the class distribution from Module 9, if available.
Discriminator.
- Check
accounting_validfirst. Low means a counter clamped and every ratio is unusable — one bit, and nothing else is worth examining until it is high. - Compute
cnt_bytes_useful / cnt_bytes_transferred. Low is mechanism 1, and it is the answer most of the time. The ratio's value is diagnostic: a quarter suggests requests a quarter of a burst's size, which points at the request size rather than at anything in the memory system. - Compare
cnt_slots_usedagainstcnt_slots_available. If they are equal over a long run, mechanism 3 — the tick is gated, and P2 does not catch this because equality is legal. - Compare
cnt_bytes_usefulagainst the requester's own delivered-byte count. A mismatch is mechanism 2, and the block's input is at fault rather than the memory system. - If payload and utilisation are both healthy, mechanism 5 — and the next measurement is Module 9's class distribution, not a burst-length change. §4's layer question.
Responsible layer. Mechanisms 2 and 3 are the instrumentation, not the design — and they are a large share of cases, which is why the counters are checked before the conclusions. Mechanism 1 is the request size against the burst contract, and the fix is granularity rather than anything in the data path. Mechanism 5 belongs to later modules.
Fix. Per mechanism — and report the measures separately on the dashboard from the start, because a single "efficiency" number could not have distinguished any two of these.
9. Common Misconceptions
"A 100%-busy bus means 100% useful efficiency."
Why it is tempting: a busy bus is doing work, and work feels like progress.
Concrete failure: §7's symptom. Utilisation near 100% with a quarter of the bytes wanted — the bus is fully occupied moving data nobody asked for, and an investigation that starts from utilisation concludes the memory system is saturated.
Correct model: utilisation and payload efficiency divide by different things. §2.
Prevention: never quote a utilisation figure without a payload figure beside it.
"Longer bursts always improve performance."
Why it is tempting: more data per command is more efficient by measure A, and measure A improves by construction.
Concrete failure: a design lengthens bursts, command efficiency doubles, and payload efficiency halves for a small-request workload. The single reported number improves and the requesters get slower.
Correct model: A improves by definition; C can degrade. They trade. §2.
Prevention: a measure that a change improves by construction cannot evaluate the change.
"Burst efficiency is the same as memory-system efficiency."
Why it is tempting: the burst is where the data moves, so it feels like where the performance is.
Concrete failure: a team optimises burst granularity on a workload bounded by row conflicts. Every burst-level measure improves and throughput does not move.
Correct model: measure D includes row-state work, turnaround, refresh and scheduling, each owned elsewhere. §4.
Prevention: §4's layer question — measure the class distribution before changing the granularity.
"Burst chop makes small requests efficient."
Why it is tempting: it transfers fewer bytes, so it looks like a saving.
Concrete failure: a performance model that assumes a chop halves the cost everywhere. It halves interface bytes and does not reduce the array's work — and for DDR5 the bank stays busy for the full burst duration regardless.
Correct model: a chop improves C, degrades A, and leaves the array's work alone. §3's callout, and Chapter 4.4 §5.
Prevention: separate array work from interface work in any efficiency argument.
"An efficiency counter is part of the design, so its numbers are right."
Why it is tempting: it is RTL, it was reviewed, it has assertions.
Concrete failure: the block is perfect and slot_tick is gated on activity, so utilisation is structurally 100%. Every number is wrong and every assertion passes, because the bug is in the contract, not the block.
Correct model: the counters depend on caller contracts they cannot verify. §5's limitations.
Prevention: reconcile against a measurement from outside — the requester's own delivered-byte count.
"There is a standard DDR efficiency percentage."
Why it is tempting: figures get quoted, and a number feels more useful than a method.
Concrete failure: a quoted percentage is used as a design target, and the workload, the window definition and the measure it referred to are all different from the one being designed.
Correct model: efficiency is a family of measures whose values depend entirely on the workload and the stated denominators. A percentage without both is not a fact.
Prevention: state the numerator, the denominator and the workload — or do not quote the number.
10. Interview Reasoning
"Why can a bus be highly utilised while useful throughput is poor?"
Because utilisation and payload efficiency divide by different things. Utilisation asks what fraction of available transfer slots carried data; payload efficiency asks what fraction of the transferred bytes anybody wanted. A burst transfers a fixed amount whether the requester wanted all of it or not — so a stream of small requests against a long burst keeps the bus almost fully occupied while most of what moves is discarded. The practical consequence is diagnostic: an investigation that starts from utilisation concludes the memory system is saturated, when the actual problem is request granularity.
"Why is command efficiency a poor measure of whether bursting helps?"
Because bursting improves it by construction. Eight transfers per command is eight times the payload per command, and that follows from the contract rather than from anything working well. A measure a change improves by definition cannot evaluate the change — it tells you the mechanism is operating, not that it is helping. The measure that can degrade is payload efficiency, and the useful statement is always about the pair: what did command efficiency buy, and what did payload efficiency pay for it?
"How do you calculate payload efficiency, and what do you need to state?"
Useful bytes over transferred bytes, where transferred is the interface width in bytes times the burst length. What must be stated is the workload — the request-size distribution is the whole input — and, for utilisation, what the window's denominator includes. A window that excludes refresh or turnaround slots measures something different from one that does not; neither is wrong and comparing them is. Most disagreements about memory efficiency turn out to be two people dividing by different denominators.
"Why can increasing burst granularity help one metric and hurt another?"
Because the same change moves the two in opposite directions. Lengthening a burst raises the payload delivered per command — measure A — and if the requester's size did not grow with it, the fraction of transferred bytes that were wanted falls. Reported as a single percentage, that appears as an improvement while the requesters get slower, which is the specific failure the single-number trap produces. The trade is real and the right resolution is workload-dependent, which is why this chapter derives the measures and defers the optimisation.
"Throughput is poor. When is burst granularity the wrong thing to change?"
When something else dominates. If the workload is bounded by row conflicts, every access is already paying two serialised row-state transitions and no burst length changes that — Module 9's result. The same holds if turnaround or refresh dominates. The discipline is to measure the class distribution and the payload efficiency before touching granularity, because burst-level measures can all improve while throughput does not move, and that outcome is expensive to reach by experiment.
11. Engineering Exercise
BYTES_PER_SLOT = 8, WINDOW_SLOTS = 64.
1. A DDR4-shaped configuration transfers BL8 on a 64-bit interface. How many bytes per burst? How many slots?
2. Over one window: 64 slots available, 32 used, 8 bursts completed, 96 useful bytes. Compute measures A, B and C, stating each denominator.
3. A design doubles the burst length; request sizes are unchanged. Say what happens to A, B and C, and which one the requester experiences.
4. A monitor reports utilisation of exactly 100.0% over a long run. What is the most likely cause, and which assertion does not catch it?
5. cnt_bytes_useful is 70 and cnt_bytes_transferred is 64. What has happened?
6. Payload efficiency is 95% and utilisation is 40%, and throughput is poor. Where do you look next, and why is it not burst length?
12. Summary
Bursting buys command efficiency by construction — and a measure that improves by definition cannot tell you whether a design is good.
Four measures, four denominators. Command efficiency divides by commands; slot utilisation by available slots; payload efficiency by transferred bytes; system efficiency by everything. They are independent, and the pair that matters most is high utilisation with low payload efficiency — a fully occupied bus moving data nobody wanted.
Always name the denominator. "Efficiency improved" is not a claim until it says which ratio, over what window, on what workload. A quoted percentage without those is not a fact.
Burst chop is a trade, not a saving. It improves payload efficiency, degrades command efficiency, leaves the array's work untouched — and on DDR5, vendor material describes the bank remaining busy for the full burst duration while only half the words transfer.
And bursting does not remove the rest of the overhead. Row-state work, turnaround, refresh and scheduling are each owned by a later module, and any one can dominate while every burst-level measure looks healthy. Measure the classes before changing the granularity.
The instrumentation follows from that: count the quantities separately, saturate rather than wrap, expose a validity flag, and compute no ratio in hardware — because a ratio hides its own denominator, which is the one thing this chapter says must always be stated.
13. What Comes Next
Module 12 is complete, and with it the data-transfer half of the curriculum.
Module 10 built the read transaction, Module 11 the write, and this module generalised the structure both of them used: a burst is a contract for a number of transfers, visiting a window of positions, in an order determined by a rule, bought for a command cost and paid for in transferred bytes.
Three things this module insisted on are worth carrying forward. Burst length counts transfer positions and nothing else. The burst-local window is what makes a burst structurally unable to reach a row nobody activated. And efficiency is a family of measures, not a percentage.
Module 13 — DDR Timing Fundamentals takes up the question every chapter of Modules 9 through 12 has deferred: when may any of this be issued, relative to everything else? Every educational interval in this module and the two before it — latency depths, recovery counts, representative spacings — has been labelled as not corresponding to any device figure, precisely because that question belongs there.
That module derives why timing parameters exist at all, from the device physics beneath them, before Module 14 catalogues them.
Return to Burst Length for the contract, Sequential Burst and Interleaved Burst for the order, DDR3 for burst chop's original efficiency argument, Performance Impact for the transition-count model this chapter's measure D depends on, and Channels for the saturating-telemetry discipline reused here.
Continue learning
Related tutorials
- Related topic
Performance Impact
Two request streams with identical addresses and counts can demand more than twice the row-state work, decided only by their order — and the instrumentation that measures it lies in specific, recognisable ways.
- Related topic
The Memory-Subsystem View
Six levels, six shared resources, six conflicts. Assembling them into one picture and tracing a single request through it produces the question that organises all memory debugging: which level is blocking this, and how often?
- Related topic
The Write Command
Accepting a read creates an obligation to recognise something. Accepting a write creates an obligation to produce something — and that single reversal explains almost every way writes differ from reads.
- Related topic
DDR Bandwidth
Chapter 12.4 named four efficiency measures and built three. This builds the fourth: every bus cycle charged to exactly one named cause, with the categories provably summing to the window.
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.
