Exercises — DMA — memory-to-memory, peripheral-to-memory without CPU
Before we start, let's restate the only three formulas you need, in plain words, so nothing here is used before it's earned.
Level 1 — Recognition
Exercise 1.1
Which of these three transfers keeps the source address fixed (does not increment it each beat)?
(a) Mem-to-mem memcpy (b) ADC → RAM buffer (c) RAM buffer → DAC
Recall Solution 1.1
Answer: (b) ADC → RAM buffer (peripheral-to-memory).
Why. The ADC's data register is a single fixed hardware address — new samples appear at the same spot each time. So we read that one address repeatedly () and scatter the results into successive RAM slots ().
- (a) Mem-to-mem: both increment ().
- (c) Mem-to-periph: the destination (DAC register) is fixed (), source increments.
Rule of thumb: the peripheral side is always the fixed address. See Memory-Mapped IO for why a peripheral register lives at one address.
Exercise 1.2
Match each quantity to its symbol: item count, bus clock, bytes per item, cycles per beat.
Recall Solution 1.2
- Item count
- Bus clock (Hz)
- Bytes per item
- Cycles per beat
Memory aid: only and set the rate (); only sets how long it runs.
Exercise 1.3
True or false: "Right after I call dma_start(), the destination buffer holds all the data."
Recall Solution 1.3
False. DMA is asynchronous — dma_start() only launches the mover. The bytes arrive over the next seconds. You must wait for the transfer-complete flag or interrupt (see Interrupts and ISRs) before reading. Reading early gives stale or partial data.
Level 2 — Application
Exercise 2.1
A UART logs bytes to RAM (periph-to-mem), , , bus MHz, . Find and throughput.
Recall Solution 2.1
Time: plug into . Why this step: each beat is 1 cycle, we have 512 beats, and tells us cycles per second — dividing gives seconds.
Throughput: (Sanity check: bytes, MB/s. ✓)
Exercise 2.2
Mem-to-mem copy of a 300-word array, , MHz, . Find bytes moved and time .
Recall Solution 2.2
Bytes: bytes. Time: . Address increments: both walk forward, bytes per beat (this is what makes it a copy — see memcpy and Block Copy).
Exercise 2.3
You must move exactly 8192 bytes. Compare vs at MHz, . Which is faster and by how much?
Recall Solution 2.3
- : .
- : .
- Ratio . The transfer is 4× faster.
Why: same total bytes, but wider beats mean fewer beats, and each beat costs the same cycles. Fewer cycles → less time. Always use the widest width your source/destination alignment permits.
Level 3 — Analysis
Exercise 3.1
An ADC fires a DMA request every (one sample per request, hardware-triggered). Each beat itself takes cycle at MHz. Is the DMA the bottleneck, or the ADC? What is the effective throughput, ?
Recall Solution 3.1
Two rates compete:
- How fast DMA could move a beat: .
- How often data arrives: one request every .
Since , the DMA finishes each beat in a blink and then waits for the next request. The ADC is the bottleneck — the transfer is rate-matched to the peripheral.
Effective throughput = bytes per sample ÷ sample period: Not the raw MB/s! The formula is the DMA's ceiling; the real rate is throttled by how often the peripheral has data. See ADC and DAC.
Exercise 3.2
In burst mode the DMA holds the bus for the whole 2048-beat block; in cycle-stealing mode it takes one beat, releases, repeats. The block takes (). During this window the CPU needs a memory access every ns on average. Roughly how long is the CPU stalled in burst mode vs cycle-stealing?
Recall Solution 3.2
Burst mode: DMA owns the bus for the entire . Every CPU memory access in that window stalls until DMA finishes. Worst case, the CPU is blocked for nearly the full (all its memory accesses queue up).
Cycle-stealing: DMA takes the bus for one beat () then releases it. A CPU access arriving during a beat waits at most one beat . Over the whole block the CPU loses only of bus occupancy spread out — but any single CPU access is delayed by at most ns, so the CPU keeps running, just slightly slowed.
The trade-off: burst = highest DMA throughput, worst CPU latency. Cycle-stealing = bounded CPU latency, slightly lower DMA throughput (arbitration overhead between beats). This is exactly the Bus Arbitration decision.
Exercise 3.3
A CPU copy loop costs cycles/word. You DMA-copy words instead. How many CPU cycles does the offload save, and (at MHz) how much CPU time is freed?
Recall Solution 3.3
Cycles saved: cycles. Time freed: .
Why this matters: the CPU spends only cycles (setup + one interrupt handler) instead of babysitting the copy. Those go to real computation while DMA plumbs the bytes.
Level 4 — Synthesis
Exercise 4.1
You're streaming audio out to a DAC at 48 kHz sample rate, 2 bytes/sample, and you cannot afford a gap while the CPU refills the buffer. Design the buffer scheme and compute how much time you have per half-buffer if each half holds 512 samples.
Recall Solution 4.1
Scheme: double buffering (ping-pong). Split one buffer into two halves. DMA streams half A to the DAC while the CPU fills half B; when DMA finishes A it fires a half-transfer / transfer-complete interrupt and switches to B while the CPU refills A. No gaps. This is Circular and Double Buffering.
Refill deadline. One half = 512 samples, each played every s: So the CPU has ~10.67 ms to refill one half before DMA drains the other. Miss this deadline → audible glitch. This is the essence of deterministic real-time DMA. (Mem-to-periph: , at the DAC register.)
Exercise 4.2
Same DAC stream, but the SoC has a data cache. The CPU computes new samples into RAM, then DMA reads them out. What cache operation is required and when, and what happens if you skip it?
Recall Solution 4.2
Required: after the CPU writes new samples, clean (flush) the cache for that buffer region before enabling the DMA read. Cleaning pushes the cached values down into actual RAM, which is where DMA reads from.
If skipped: the fresh samples may still sit only in the CPU's write-back cache, while RAM holds stale bytes. DMA reads RAM → plays old/garbage audio. This is the classic Cache Coherency bug.
Mirror case (periph→mem input): before the CPU reads a DMA-filled buffer, invalidate the cache for that region so the CPU fetches fresh RAM instead of a stale cached copy.
Level 5 — Mastery
Exercise 5.1
Full design. A sensor node captures a 12-bit ADC at 100 kHz continuously, stores into 16-bit RAM slots, and the CPU processes blocks of 1000 samples. Design the complete DMA setup (all four SCAM parameters + increment modes + trigger + interrupt strategy), then verify the CPU has enough time to process each block, given processing costs 50,000 cycles/block at MHz.
Recall Solution 5.1
SCAM setup:
- Source ADC data register, fixed ().
- Count (one block).
- Address mode: destination increments by (); source fixed.
- Mode/trigger: hardware-triggered by the ADC "conversion ready" DMA request — one beat per sample, naturally rate-matched. Use circular / double buffering so capture never stops: DMA fires a half-transfer interrupt at 500 samples and a full-transfer interrupt at 1000, ping-ponging halves.
Timing check — does the CPU keep up?
- Time to fill one 1000-sample block at 100 kHz:
- Time for the CPU to process one block:
- Since , the CPU finishes with ~8.96 ms to spare per block. ✓ The system is comfortably real-time.
CPU utilisation for processing — the other ~90% is free for other tasks, exactly the DMA parallelism win.

Exercise 5.2
Failure analysis. In the design above, an engineer sets the ADC to 800 kHz to get finer resolution but leaves everything else the same. Processing still costs 50,000 cycles/block. Does the system still meet its deadline? If not, give two concrete fixes.
Recall Solution 5.2
New fill time at 800 kHz: Processing still needs . Now — it still fits, but barely: only ~0.21 ms of slack. Any interrupt jitter, cache miss, or added work can blow the deadline.
Two fixes:
- Bigger blocks — process 4000 samples per block. Fill time ; processing , still fits, but the fixed per-block overhead (interrupt entry, cache invalidate) is amortised over more samples, lowering effective per-sample cost.
- Optimise processing — cut cycles/block (better algorithm, use of hardware accelerators) so drops well below , restoring margin.
Verify the CPU never touches the ADC register itself — DMA does all capture; the CPU only wakes on the transfer-complete interrupt. Also invalidate the cache on the DMA input buffer before processing (Cache Coherency).
Recall Self-test summary
:::: transfer time from beats, cycles/beat, clock :::: rate ceiling; cancels Peripheral side address increment :::: always (fixed register) Real bottleneck of a periph transfer :::: the peripheral's request rate, not Before CPU reads DMA input (with cache) :::: invalidate cache Before DMA reads CPU output (with cache) :::: clean/flush cache