5.5.8 · D5Embedded Systems & Real-Time Software
Question bank — DMA — memory-to-memory, peripheral-to-memory without CPU
Before we start, a few symbols and words earn their place here so nothing surprises you:


True or false — justify
TRUE / FALSE: DMA transfers happen without using any clock cycles at all.
False. DMA frees the CPU's compute pipeline, but each beat still burns bus cycles and bus bandwidth — it's free for the CPU, not for the memory subsystem.
TRUE / FALSE: Because the CPU isn't executing the copy loop, it can never stall during a DMA transfer.
False. CPU and DMA share one bus; in burst mode DMA can hold the bus so long that a CPU memory access has to wait. Compute from cache is fine, but a cache miss can stall.
TRUE / FALSE: Doubling the item width roughly halves the transfer time for the same number of bytes.
True. Same bytes with wider items means fewer beats. Since and halves when doubles, time halves — this is exactly why you pick the widest aligned width.
TRUE / FALSE: Throughput tells you that transferring more data gives you more bytes/second.
False. cancels in ; throughput is a rate, fixed by width, clock, and cycles-per-beat. More data takes proportionally more time, so the rate is unchanged.
TRUE / FALSE: For a peripheral-to-memory transfer you should increment both the source and destination address each beat.
False. The peripheral's data register is one fixed hardware address, so ; only the RAM buffer walks forward (). Incrementing the source wanders into neighbouring registers.
TRUE / FALSE: After calling the "start DMA" function, the destination buffer holds valid data on the very next line.
False. DMA is asynchronous; you must wait for the transfer-complete flag or interrupt. Reading early gives partial or stale bytes.
TRUE / FALSE: On a microcontroller with a data cache, writing DMA input straight to RAM guarantees the CPU reads the fresh bytes.
False. The CPU may return a cached stale copy. You must invalidate the cache region before reading DMA-filled RAM — see Cache Coherency.
TRUE / FALSE: Cycle-stealing arbitration is always faster than burst arbitration.
False. Cycle-stealing releases the bus every beat, protecting CPU latency but adding re-arbitration overhead. Burst holds the bus and finishes sooner — it's a latency-vs-throughput trade, not a clear winner.
TRUE / FALSE: Mem-to-mem DMA and a hand-written memcpy produce identical bytes in the destination.
True (for the byte contents), but the cost profile differs: DMA offloads the work while memcpy keeps the CPU busy the whole time.
TRUE / FALSE: A hardware-triggered peripheral transfer moves the whole block the instant you enable it.
False. It performs one beat per DMA request; the peripheral raises a request each time it has/needs an item, so the transfer is naturally paced by the peripheral's data rate.
Spot the error
ERROR? "I set up a UART receive DMA, so I incremented the source address by 1 each beat to walk through the incoming bytes."
Error. The UART data register is a single fixed address; new bytes appear there repeatedly. Keep and increment only the RAM destination.
ERROR? "To copy 4096 bytes fastest, I used so I'd have the most beats and finish quickest."
Error. More beats is slower. Use the widest aligned width (): 1024 beats instead of 4096, roughly 4× faster.
ERROR? "My ADC-capture buffer looked corrupt, so I added a while(!done_flag); before enabling the transfer."
Error. The flag can never be set before the transfer runs — that spins forever. The wait belongs after enable, before you read the buffer.
ERROR? "DMA didn't need the interrupt, so I disabled the transfer-complete IRQ and just used the buffer after a short delay."
Error. A fixed delay is a race: bus contention or wait-states can stretch the transfer past your guess. Use the flag/interrupt to know it's truly done.
ERROR? "I flushed the cache after the DMA finished writing input into RAM to make the data valid."
Error. For DMA input you invalidate (drop stale cached copies) before reading; you clean/flush for DMA output (CPU wrote, DMA reads) before the DMA starts. The direction and timing were both wrong.
ERROR? "Throughput is 96 MB/s, so a 96 MB transfer takes 1 second no matter the item width."
Error. Throughput itself depends on width (). Change and the 96 MB/s figure changes; it isn't a fixed budget independent of setup.
ERROR? "I programmed source, count and mode, hit enable, and expected mem-to-mem to run — but nothing moved."
Error. You forgot the destination address (and for mem-to-mem, a software trigger). Recall the setup checklist S.C.A.M. — Source, Count, Address-increment mode, Mode/trigger — the four fields every DMA needs before you enable it; here the destination (part of address setup) was missing.
Why questions
WHY does the peripheral's source address stay fixed while the destination walks forward?
The peripheral exposes one hardware register (memory-mapped) where each new item appears at the same spot; the RAM buffer must scatter those items into successive slots, so only it increments.
WHY does cancel out of the throughput formula but not out of the transfer-time formula?
Time scales with the amount moved, but throughput = bytes/time = — the in bytes and time cancel, leaving a rate that depends only on how fast each beat is.
WHY is a hardware-triggered transfer "naturally rate-matched" to the peripheral?
The peripheral raises exactly one DMA request per item it has/needs, and DMA runs one beat per request — so it never overruns or underruns the peripheral's pace.
WHY can DMA capture fast peripheral data the CPU might otherwise miss?
A dedicated engine responds to each request in hardware with deterministic, tiny latency, whereas the CPU might be mid-instruction or servicing another ISR and arrive too late for a microsecond-fast ADC sample.
WHY does cache coherency only bite on some chips and not the simplest MCUs?
Simple MCUs have no data cache, so CPU and DMA both see the same RAM directly. Add a cache and the CPU may hold a private stale copy, so RAM and cache can disagree — that's the coherency bug.
WHY choose cycle-stealing over burst even though burst finishes the block sooner?
Cycle-stealing releases the bus after each beat so the CPU never waits long — you trade a slower overall transfer for guaranteed low CPU latency, which real-time deadlines demand.
WHY does DMA pair so well with double/circular buffering?
DMA fills one buffer while the CPU processes the other; with a circular buffer the address auto-wraps, letting a continuous stream run forever without the CPU intervening every block.
Edge cases
EDGE: What if you set the item count ?
No beats are scheduled, so nothing transfers and (on many controllers) the complete flag may fire immediately — a common source of "it says done but the buffer is empty" confusion.
EDGE: What if source and destination memory regions overlap in a mem-to-mem copy?
DMA generally has no overlap protection like a safe
memmove; forward-incrementing addresses can overwrite bytes you haven't copied yet, corrupting the result. Avoid overlap or copy in a safe direction.EDGE: What if the item width is 4 but the buffer address is not 4-byte aligned?
Many buses fault or silently split the access; you lose the width advantage or get a bus error. Widest-width only helps when the addresses are aligned to that width.
EDGE: What if a peripheral produces data faster than DMA can service the requests?
An overrun occurs — new data lands in the register before the previous beat consumed it, and bytes are lost. The peripheral usually raises an overrun error flag.
EDGE: What if the CPU and DMA both want the bus in the very same cycle?
The arbiter grants exactly one; the other waits one arbitration slot. Only one driver ever holds the shared bus, so neither corrupts the other — one just stalls briefly.
EDGE: What happens to the transfer if the enable bit is cleared mid-way?
The transfer aborts at the current beat, leaving the destination partially filled — the completed beats are valid, the rest are untouched, and no complete-interrupt fires.
EDGE: Your controller supports a negative (decrementing) address increment (). When is that the right choice?
When you must traverse a buffer backwards — e.g. reversing a byte array in place, or an overlap where copying high-to-low avoids clobbering unread bytes. The controller then walks instead of ; the time and throughput are identical (same , , , ) — only the direction of address walking changes.
Recall One-line takeaways
DMA is free for compute, not for the bus ::: it always costs bus cycles and can stall the CPU on memory access. Peripheral address is fixed ::: increment only the RAM side. Always wait for the done flag ::: DMA is asynchronous. Cache + DMA needs manual invalidate/clean ::: RAM and cache can disagree. Address increment can be positive, zero, or negative ::: forward walk, fixed register, or backward walk.