Wishbone · Module 25
DMA Master
Wishbone B3 contains no DMA, so every decision an engine makes is local policy. Three invariants make it correct — and the defect that breaks the worst of them passes every conformance check ever written.
Every master this curriculum has built so far did what it was told, one transfer at a time. Chapter 23.1's wb_master_fsm takes a request, issues it, reports the answer, and stops. A DMA engine is the first master that is handed a description of work and then goes away and does it.
A DMA engine converts a transfer description into a sequence of independently completed bus operations, while preserving source and destination progress, transfer count, data integrity and error state across stalls and arbitration.
Start with what it is not:
| it is called | why that is wrong |
|---|---|
| "memcpy in hardware" | memcpy handles overlap; this engine explicitly does not |
| "a burst generator" | a Wishbone BLOCK cycle has no length field — see §7 |
| "a second CPU" | it has no instructions, no branches, one job |
| "just a master FSM" | a master FSM has no descriptor, no count, no held data |
| "one big bus transaction" | it is 2N ordinary transactions for N items |
1. Wishbone B3 Contains No DMA
Search B3 for DMA and you will find nothing. There is no DMA rule, no DMA chapter, no descriptor, no transfer count. That is not an omission — B3 specifies an interface, and a DMA engine is a client of that interface like any other master.
So the honest framing for the whole module:
Everything this engine decides is LOCAL DMA POLICY — NOT A WISHBONE REQUIREMENT. What B3 supplies is the set of obligations the engine must meet while it decides.
Three of those obligations shape the architecture:
RULE 3.25 — "MASTER interfaces MUST assert
[CYC_O]for the duration of SINGLE READ / WRITE, BLOCK and RMW cycles.[CYC_O]MUST be asserted no later than the rising[CLK_I]edge that qualifies the assertion of[STB_O]."RULE 3.35 — "The cycle termination signals
[ACK_O],[ERR_O], and[RTY_O]must be generated in response to the logical AND of[CYC_I]and[STB_I]."RULE 3.60 — "MASTER interfaces MUST qualify the following signals with
[STB_O]:[ADR_O],[DAT_O()],[SEL_O()],[WE_O], and[TAGN_O]."
RULE 3.35 is why this engine has exactly one bus operation in flight, ever. One outstanding phase, no tags, no reordering. A deeper DMA pipeline is not a design option in Classic Wishbone — the protocol forbids the thing it would need.
RULE 3.60 is why a DMA can stall for an arbitrary number of clocks and stay correct. The whole request is held still while unanswered, so the engine can wait out a slow endpoint, a full FIFO, or a lost arbitration round and return to a question that has not moved.
2. Two Planes, Two Wishbone Ports, Opposite Directions
A DMA engine has two Wishbone interfaces, and confusing them is the most common misunderstanding about DMA.
// CONTROL PLANE this module. A SLAVE. The CPU writes SRC, DST,
// LENGTH and CTRL here. Small, register-mapped,
// Module 24's subject.
// DATA PLANE wb_dma. A MASTER. It moves the actual bytes and the
// CPU never touches it."The DMA is a peripheral" and "the DMA is a bus master" are both true, of different ports. The control plane is a Module 24 slave — a register map with an access policy and a termination scheme. The data plane is a master that the CPU never addresses at all.
3. The Three Invariants
// 1. SOURCE PROGRESS ONLY ON SOURCE READ SUCCESS
// 2. DESTINATION PROGRESS ONLY ON DESTINATION WRITE SUCCESS
// 3. THE ITEM COUNT ADVANCES EXACTLY ONCE PER COMMITTED ITEM -
// not on presentation, not on grant, not on RTY.Each has a named defect in the RTL below, and five of the six defects in this module are PERFECTLY LEGAL WISHBONE. That is the module's central point, and Chapter 25.4 publishes the matrix that proves it.
4. The State Machine
Two states drive the bus and they differ only in direction:
assign cyc_o = (st_q == S_RD) || (st_q == S_WR);
assign stb_o = (st_q == S_RD) || (st_q == S_WR);
assign we_o = (st_q == S_WR);
// RULE 3.60: the context is driven from registers and cannot move while
// the phase is unanswered, because nothing but a successful termination
// changes src_q / dst_q / hold_q.
assign adr_o = (st_q == S_WR) ? eff_dst : eff_src;Which clock owns which change is the whole of the design, so it is worth tabulating:
| what changes | on which clock | never on |
|---|---|---|
| held data | a successful source read | presentation, a write ACK |
| source address | a successful source read | presentation, RTY |
| destination address | a successful destination write | presentation, RTY |
| remaining count | a successful destination write | a source read, RTY, ERR |
| done | the final successful destination write | issuing the final write |
[CYC_O], [STB_O] | state entry and exit | — |
5. The Holding Register
A source read and a destination write are two separate bus operations. Whatever the source returned has to survive the gap — and the gap can be arbitrarily long.
// A source read and a destination write are TWO SEPARATE BUS
// OPERATIONS. Whatever the source returned must survive the gap, which
// may be arbitrarily long: destination wait states, arbitration loss,
// retries. RULE 3.65 makes the slave's [DAT_O()] valid only on ITS
// termination clock, so by the time the destination write happens the
// source's data is long gone from the wire.
assign dat_o = LIVE_READ_DATA ? dat_i : hold_q;Chapter 24.2's RULE 3.65 is what makes the holding register mandatory rather than convenient. A slave's [DAT_O()] is qualified by its termination. Once that phase ends the data is not merely stale — it is not required to be anything at all.
LIVE_READ_DATA is the defect that ignores this, and Chapter 25.2 measures what it costs.
6. Progress Is Commit Accounting
logic term, ok, present;
assign present = cyc_o && stb_o;
assign term = ack_i || err_i || rty_i; // RULE 3.45 makes OR safe
assign ok = present && ack_i;present is a request on the wire. ok is an answer. Every progress decision keys off ok:
logic advance_rd, advance_wr;
assign advance_rd = ADVANCE_ON_PRESENT ? (present && (st_q == S_RD))
: (ok && (st_q == S_RD));
assign advance_wr = ADVANCE_ON_PRESENT ? (present && (st_q == S_WR))
: (ok && (st_q == S_WR));Against a zero-wait slave the two coincide and the defect is invisible — the recurring shape of every commit-timing bug in this curriculum, from Chapter 23.3's FIRE_ON_STB to Chapter 24.2's SIDE_ON_STB.
ADVANCE_ON_PRESENT is the one defect in this module that also breaks a Wishbone rule, and it is worth being precise about why: advancing on presentation moves [ADR_O] while the phase is still open, which is RULE 3.60. The protocol does not know what a transfer count is; it happens to constrain a signal this particular bug disturbs.
7. RTY Is Not "Keep Waiting"
end else if (present && rty_i) begin
// RTY TERMINATES THIS ATTEMPT. The retry is a NEW transfer,
// later. Nothing about the descriptor moves.
nrty_q <= nrty_q + 16'd1;B3's own description:
[RTY_I]— "indicates that the interface is not ready to accept or send data, and that the cycle should be retried. When and how the cycle is retried is defined by the IP core supplier."
And for errors:
[ERR_I]— "indicates an abnormal cycle termination. The source of the error, and the response generated by the MASTER is defined by the IP core supplier."
"Defined by the IP core supplier" is B3 naming us as the authority and declining to be one. So this engine's policy, stated in its datasheet header:
// 4 response to [ERR_I] : TERMINATE the descriptor, set ERROR, stop.
// 5 response to [RTY_I] : RETRY the same bus operation, up to
// MAX_RETRIES, ADVANCING NOTHING. On
// exhaustion, terminate with ERROR.Chapter 25.3 measures both, and the COUNT_ON_RTY defect that treats "ask again" as "done".
8. Length Is In Items, And Zero Is Defined
// ── LENGTH IS MEASURED IN ITEMS, NOT BYTES ──────────────────────────────
// One convention, stated once, at the register boundary and here. LENGTH
// counts 32-bit items. A 4-item transfer moves 16 bytes and performs 4
// source reads and 4 destination writes. Mixing the two conventions is
// the classic DMA off-by-four and this module refuses to offer the
// choice.The addresses are byte addresses and the count is an item count. That asymmetry is deliberate and documented rather than discovered: a 4-item transfer from 0x100 touches 0x100, 0x104, 0x108, 0x10C.
And the case nobody decides until it bites:
// ── ZERO LENGTH IS DEFINED, NOT ACCIDENTAL ──────────────────────────────
// LENGTH = 0 completes immediately: no bus operation, no data moved, DONE
// asserted, ERROR clear. The alternative - underflowing the counter and
// transferring 2^N items - is what happens if nobody decides. LOCAL POLICY.Measured — both deltas zero, and one completion:
N=0 src_ok delta=0 dst_ok delta=0 remaining=0 done delta=1
-> N=0 MOVED NOTHING AND COMPLETED. Both deltas are
zero - not one bus operation was issued - and
done was asserted. LOCAL POLICY: the engine
checks for zero at START rather than entering
the loop and underflowing to 65536 items.And the off-by-one case, N=1:
N=1 src_ok=1 dst_ok=1 remaining=0 done=1
0x4800 = 0xc0de0000 (expected 0xc0de0000)
0x4804 = 0x22222222 (expected 0x22222222, untouched)
final src=0x0704 dst=0x4804
-> ONE source read, ONE destination write, ONE done.
Addresses advanced by exactly one ITEM_BYTES and
the neighbouring word was not touched.9. Configuration State Is Not Active Transfer State
The register block copies the programmed fields into a separate active descriptor when START is written:
// ── THE SNAPSHOT, WHICH IS THE POINT OF THIS MODULE ─────────────────────
// CONFIGURATION STATE IS NOT ACTIVE TRANSFER STATE.
//
// START copies SRC/DST/LENGTH/INC into a separate ACTIVE descriptor. From
// that clock on, the engine reads only the active copy, and software may
// write the programming registers freely - preparing the next descriptor
// while the current one runs - without touching the transfer in flight.The experiment: program a 4-item transfer, start it, then rewrite SRC, DST and LENGTH while it runs.
rig dst_ok remaining done 0x4400 holds
correct 4 0 1 0xa0000000
NO_SNAPSHOT 99 0 1 0xa0000000
-> THE CORRECT RIG IGNORED THE REPROGRAMMING.The defective engine moved 99 items instead of 4, because software's mid-flight LENGTH write extended the transfer that was already running.
EVERY BUS TRANSACTION IN BOTH RIGS WAS LEGAL:
protocol violations correct 0 NO_SNAPSHOT 0A defect that could not arm
The first version of this experiment reported both rigs identical. The register block's snapshot had been removed — and the engine latched the descriptor at START anyway, so nothing changed:
// NO_SNAPSHOT is the architecture in which there is NO active
// descriptor at all: the engine keeps only an item index and derives
// every address from the LIVE programming inputs. Removing the
// snapshot from the register block alone is not enough - this engine
// would still have latched the values at START - and SIM A caught
// exactly that: the defect was configured, compiled, and inert.A defect parameter that cannot arm is not evidence of a correct design. Chapter 24.4 lost a gate to the same shape, and it is now a standing check: every negative control in this module must be shown firing.
START while BUSY is the other policy this raises, and it is stated rather than discovered:
START WHILE BUSY - LOCAL POLICY: IGNORED
starts accepted 3 starts ignored 110. What This Engine Does Not Do
// no scatter-gather, no descriptor rings, no linked lists
// no overlapping source/destination support - NOT memmove, and 25.2
// states it rather than leaving it to be discovered
// no burst or [CTI_O()] - Classic phases only
// no coherence, no IOMMU, no translation
// no channel arbitration - ONE channelAnd one more, which belongs to the bus rather than the engine: a DMA does not make anything atomic. RMW is B3's named indivisible construct — "used for indivisible semaphore operations" — and [CYC_O] merely stays asserted across it. Holding [CYC_O] across a descriptor locks nothing, which Chapter 25.4 takes up.
Next: Chapter 25.2 — Memory Transfers puts the engine to work moving a block of memory, gives each endpoint its own latency, and measures what happens to the data when the holding register is taken away.
Continue learning
Related tutorials
- Related topic
Why Multiple Masters Exist
The same four-word copy performed twice — by the CPU and by a second initiator — with the bus traffic counted both times, and the specification's own definition of a master.
- Related topic
CPU + DMA Systems
Two initiators in contention, with the clocks counted in four columns: holding work, asking for the bus, owning it, and being answered. Contention cost two clocks out of twenty-one.
- Related topic
Memory Transfers
A source read and a destination write are two bus operations with an arbitrary gap between them. Remove the holding register and six of six items corrupt, with zero protocol and zero invariant violations.
- Related topic
Peripheral Transfers
A stream has one address and returns something different each time. Backpressure is wait states, not a ready line — and a peripheral address that increments delivers one item of six, acknowledged every time.
Standards & specifications
- Governing standard
- Wishbone SoC Interconnection Architecture (OpenCores)(opens OpenCores in a new tab)
Defines the Wishbone signal set, the bus cycles built from it and the interface rules a portable IP core must follow. It deliberately leaves interconnect topology, address map and arbitration policy to the integrator, so those are system decisions rather than requirements of the specification.
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 Wishbone curriculum.
