Skip to content

AMBA APB · Module 14

APB Throughput

The throughput math of APB — transfers per second is f_clk/(2 + N_avg) with no burst bonus, so it is dominated by the fixed two-cycle floor and the average wait count, and it is deliberately modest because APB is a sparse control bus, not a bulk-data path.

Chapter 14.1 pinned the unit cost: one APB transfer is 2 + N cycles. This chapter turns that atom into a rate. Throughput is nothing more than the reciprocal of per-transfer latency: transfers/sec = f_clk / (2 + N_avg), and bytes/sec = bus_width_bytes × f_clk / (2 + N_avg). The single idea to carry: because APB cannot pipeline, there is no burst amortisation — back-to-back transfers each pay their own full 2 + N, so throughput is dominated by the fixed two-cycle floor and the average wait count, and nothing else. At 100 MHz the absolute ceiling is f_clk/2 = 50 M transfers/sec (200 MB/s on a 32-bit bus), and every wait state pushes it down a predictable amount. APB throughput is deliberately modest — it is a control bus, not a data mover — and once you can compute it, the whole utilisation and DMA-versus-CPU story becomes arithmetic.

1. Problem statement

The problem is computing how many transfers (and bytes) per second an APB link actually delivers — a hard number you can size a subsystem against, not a vague "it's slow."

A clock frequency alone tells you nothing useful. APB at 100 MHz is not 100 M transfers/sec; the two-phase structure and wait states cut it down, and the cut is exact:

  • Throughput is the reciprocal of latency. From 14.1, one transfer costs 2 + N cycles. A steady stream of transfers, each costing 2 + N_avg cycles, completes one every 2 + N_avg clocks — so the rate is f_clk / (2 + N_avg) transfers per second. That is the whole formula; everything else is plugging in numbers.
  • Bytes/sec adds one factor: the bus width. Each transfer moves bus_width_bytes (4 on a 32-bit bus). So bytes/sec = bus_width_bytes × f_clk / (2 + N_avg). Width scales the bytes per transfer, never the number of transfers per second.
  • The denominator is the whole story. f_clk and width are usually fixed by the SoC; the lever is 2 + N_avg. The fixed 2 is the floor you can never beat, and N_avg is the average wait count that pushes you below it. Throughput is therefore bounded above by f_clk/2 and degrades monotonically as N_avg grows.

So the job is: given f_clk, bus width, and the average wait count N_avg, compute transfers/sec and bytes/sec — and know that the answer is f_clk / (2 + N_avg), with no pipelining bonus hiding anywhere.

2. Why previous knowledge is insufficient

You can already cost a single APB transfer at 2 + N cycles — that is exactly what Chapter 14.1, APB latency anatomy established, and what the multi-wait chapter taught you about driving PREADY low for several cycles. What you have not done is aggregate that single cost into a rate:

  • Latency is a per-transfer number; throughput is the steady-state rate. Knowing one transfer costs 2 + N cycles does not, by itself, tell you transfers/sec — you need to multiply by f_clk and divide by the per-transfer cost, and decide what N to use across a stream (the average, N_avg). The shift is from "this transfer cost 3 cycles" to "this link sustains f_clk/(2+N_avg) transfers per second."
  • The tempting (wrong) instinct is to amortise the floor across a burst. On a pipelined bus, back-to-back transfers overlap and the fixed cost gets hidden. APB cannot do this — and why it can't is the subject of Chapter 14.3, why APB is not pipelined. For this chapter you take that as given: each transfer pays its own full 2 + N, so the per-transfer cost in a stream is the same as in isolation, and there is no burst bonus to divide out.
  • Peak throughput is not the same as achieved utilisation. This chapter computes the peak and steady-state rate the protocol can deliver for a given N_avg. What fraction of that a real workload actually uses — idle gaps, bursty arrival, contention — is Chapter 14.4, bus utilisation. Keep them separate: here we ask "how fast can it go for this N_avg?", not "how busy is it?"

So the model to add is the aggregate: take the 2 + N atom, average the wait count over a stream, and turn it into f_clk / (2 + N_avg) — the rate the rest of the performance module multiplies and budgets against.

3. Mental model

The model: APB is a metronome with a fixed two-beat minimum. Every transfer takes at least two beats (SETUP + ACCESS), and a slow slave adds N extra wait beats. The metronome never lets two transfers share beats — no overlap, ever — so the rate at which transfers complete is simply one transfer per (2 + N_avg) beats, and the beats tick at f_clk. Count the beats per transfer, divide the clock by it, and you have the throughput.

Three refinements make it exact:

  • Peak is f_clk / 2. With zero waits (N_avg = 0), every transfer is two beats, so one completes every two clocks: f_clk/2 transfers/sec. At 100 MHz that is 50 M transfers/sec — the absolute ceiling, hit only by an always-ready slave. There is no faster APB.
  • Waits push the denominator up, fast. N_avg = 1 makes it f_clk/3; N_avg = 2 makes it f_clk/4 — already half of peak. Because the floor is only 2, a small N_avg is a large relative cost: going from N_avg = 0 to 1 is a 33% throughput loss, not a rounding error. The reciprocal relationship makes early waits bite hard.
  • Width multiplies bytes, never transfers. A 32-bit bus moves 4 bytes/transfer, a 64-bit bus 8 — so bytes/sec doubles, but transfers/sec is identical (f_clk/(2+N_avg) regardless of width). Width buys you bandwidth per transfer, not more transfers. This is why APB stays modest: even a wide, fast, zero-wait APB tops out at a control-bus-grade data rate.
Two panels: left, a back-to-back zero-wait APB timeline at 100 MHz where each transfer is 2 non-overlapping cycles giving a 50 M transfers/sec peak; right, a degradation curve of transfers/sec versus N_avg showing 50.0, 33.3, 25.0, and 14.3 M/s at N_avg of 0, 1, 2, and 5.
Figure 1 — APB throughput as the reciprocal of per-transfer latency. Left: a zero-wait, back-to-back timeline at 100 MHz — each transfer is exactly 2 cycles (SETUP + ACCESS completion) and the transfers never overlap because APB cannot pipeline, so the peak rate is f_clk/2 = 50 M transfers/sec = 200 MB/s on a 32-bit bus; the 2-cycle period is bracketed and the no-overlap floor is called out. Right: the degradation curve, transfers/sec versus N_avg at the same 100 MHz / 32-bit config — 50.0 M/s at N_avg=0, 33.3 M/s at N_avg=1 (a 33% drop from a single average wait), 25.0 M/s at N_avg=2 (half of peak), and 14.3 M/s at N_avg=5 (under 30% of peak). The curve falls steeply because the denominator 2 + N_avg grows, and the figure stresses that bus width scales bytes/sec but not the transfer rate.

4. Real SoC implementation

In practice you do two things with this formula. In RTL or a monitor you count completed transfers per time window to measure achieved throughput; in a budgeting spreadsheet (or firmware sizing code) you compute the rate from f_clk, width, and N_avg. Here is the worked calculation — the arithmetic an architect runs to size an APB link, written as a small, commented C snippet.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Compute APB throughput from clock, bus width, and average wait count.
// Throughput is the reciprocal of per-transfer latency: f_clk / (2 + N_avg).
// There is NO burst bonus — APB cannot pipeline, so every transfer pays 2 + N.
 
double f_clk_hz   = 100e6;   // 100 MHz APB clock
double width_bits = 32;      // 32-bit data bus
double n_avg      = 0.0;     // average wait states per transfer (sweep this)
 
double width_bytes = width_bits / 8.0;          // 4 bytes per transfer (32-bit)
 
// transfers/sec = f_clk / (2 + N_avg)   -- the 2 is the fixed SETUP+ACCESS floor
double xfers_per_sec = f_clk_hz / (2.0 + n_avg);
 
// bytes/sec = bus_width_bytes * f_clk / (2 + N_avg)
double bytes_per_sec = width_bytes * xfers_per_sec;
double mb_per_sec    = bytes_per_sec / 1e6;     // decimal MB/s
 
// Worked results at 100 MHz, 32-bit (width_bytes = 4):
//   N_avg = 0  ->  100e6/2 = 50.0 M xfers/s  -> 200.0 MB/s   <- PEAK (f_clk/2)
//   N_avg = 1  ->  100e6/3 = 33.3 M xfers/s  -> 133.3 MB/s
//   N_avg = 2  ->  100e6/4 = 25.0 M xfers/s  -> 100.0 MB/s   <- already half of peak
//   N_avg = 5  ->  100e6/7 = 14.3 M xfers/s  ->  57.1 MB/s
//
// Same N_avg=0 on a 64-bit bus (width_bytes = 8):
//   transfers/sec is UNCHANGED at 50.0 M/s; only bytes/sec doubles to 400 MB/s.

Two facts make this the right way to size APB. First, N_avg is the only variable that moves the transfer ratef_clk and width are SoC-fixed, so the whole sizing exercise is estimating the average wait count of the addressed slaves and reading off f_clk/(2+N_avg). Second, width and transfer rate are orthogonal: doubling the bus doubles bytes/sec but leaves transfers/sec exactly where it was, which is precisely why a wide APB is still a control bus — it cannot out-transfer the 2 + N_avg floor. To measure achieved throughput rather than compute peak, the RTL counterpart is a counter that increments on each (penable && pready) completion and is sampled every fixed window (xfers_this_window / window_seconds), feeding the utilisation math downstream.

5. Engineering tradeoffs

The formula is fixed (f_clk / (2 + N_avg)), so the tradeoffs are entirely about what N_avg and width do to the delivered rate. The table below sweeps N_avg at a fixed 100 MHz / 32-bit config, then shows the width effect — making the degradation and the orthogonality concrete.

Config (100 MHz)N_avgDenominator 2 + N_avgTransfers/secBytes/sec (MB/s)vs peak
32-bit0250.0 M200.0100% (peak)
32-bit1333.3 M133.367%
32-bit2425.0 M100.050%
32-bit5714.3 M57.129%
64-bit0250.0 M (unchanged)400.0 (2×)100% transfers, 2× bytes
64-bit2425.0 M (unchanged)200.050% transfers, 2× bytes

Two throughlines. First, the transfer rate column collapses fast with N_avg — one average wait already costs a third, two waits halve it, five leave under 30% of peak; because the floor is only 2, every wait is a large fraction of the denominator. Second, width moves only the bytes column — the 64-bit rows have identical transfers/sec to their 32-bit counterparts; widening the bus buys bandwidth per transfer, not a faster cadence. So if you need more transfers per second, you must reduce N_avg (or raise f_clk); if you need more bytes, you can also widen — but neither buys APB out of being a modest control bus, because the 2 + N_avg floor caps the transfer cadence regardless.

6. Common RTL mistakes

7. Debugging scenario

The signature throughput bug is a bus-choice error: someone routed a high-bandwidth data stream through APB register reads and got a fraction of the rate they needed — the anatomy of APB throughput is exactly what diagnoses it.

  • Observed symptom: a driver draining a sensor sample FIFO (the stream produces 40 MB/s and must be emptied to avoid overflow) is losing samples — the FIFO periodically overflows and the OVERFLOW status bit sets. Functionally each read returns correct data; the system simply cannot keep up, so data is dropped.
  • Waveform clue: tracing the APB shows the firmware polling a 32-bit data register one transfer at a time, each costing 2 + N = 3 cycles (the FIFO read interface adds one wait, N = 1). The transfers are back-to-back but never overlap — there is no pipelining — so even fully saturated the link delivers only 100 MHz / 3 = 33.3 M reads/sec, and once the per-word software loop and poll overhead are counted the sustained drain falls well below the 40 MB/s the stream needs. The bus is busy yet under-delivering.
  • Root cause: bulk data was sized against a naive "bytes per clock" assumption that ignored the two-cycle floor and the lack of pipelining. APB throughput is f_clk/(2 + N_avg) per transfer, with no burst amortisation — so per-word register reads can never match a streaming source. The error is architectural: a data mover's traffic was placed on a control bus.
  • Correct RTL: keep the bulk stream off APB. Add a DMA engine that moves the FIFO contents in bursts over a pipelined bus (AHB/AXI), decoupled from APB and easily exceeding 40 MB/s; leave APB to carry only the sparse control/configuration registers (including the DMA setup) it was designed for. Where the per-transfer wait is spurious (an over-conservative FIFO PREADY), reducing N helps marginally — but it cannot close a streaming gap, so the real fix is the bulk path, not shaving a wait.
  • Verification assertion: assert the achieved transfer rate meets the drain requirement, and cover that the FIFO never overflows under the chosen path. A throughput coverpoint plus a deadline assertion turns the overflow from a silent data loss into a caught regression:
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Throughput / drain check for the FIFO-on-APB scenario.
// Count completed APB reads in a window; assert the rate clears the drain need.
int unsigned xfers_in_window;
always_ff @(posedge pclk or negedge presetn)
  if (!presetn)                 xfers_in_window <= 0;
  else if (window_tick)         xfers_in_window <= 0;            // reset each window
  else if (penable && pready)   xfers_in_window <= xfers_in_window + 1;
 
// Required: >= MIN_XFERS reads per window to drain 40 MB/s without overflow.
ap_drain_rate: assert property (@(posedge pclk) disable iff(!presetn)
  window_tick |-> (xfers_in_window >= MIN_XFERS_PER_WINDOW));
 
// And the FIFO must never overflow under the chosen path.
ap_no_overflow: assert property (@(posedge pclk) disable iff(!presetn)
  !fifo_overflow);
 
// Coverage: did we exercise the saturated (back-to-back) read stream at all?
cp_saturated: cover property (@(posedge pclk)
  (penable && pready) ##1 (psel && !penable) ##1 (penable && pready));
  • Debug habit: when an APB path is "correct but can't keep up," compute its peak f_clk/(2 + N_avg) and compare it to the required rate before touching anything. If peak is already below the requirement, no RTL tuning will save it — it is a bus-choice problem, and the answer is DMA + a pipelined bus for the stream, with APB left to its control traffic. Size against transfers × (2 + N_avg), never against bytes/clock.
Two-case debug diagram: the top wrong path drains a 40 MB/s sensor FIFO by polling a 32-bit APB register at 3 cycles per read, capped at 33.3 M reads/sec and overflowing, in red; the bottom correct path uses a DMA engine bursting over a pipelined bus above 40 MB/s with APB carrying only control, in green.
Figure 2 — a bulk-data-on-APB throughput bug, two cases. Top (wrong, red): a sensor FIFO needing 40 MB/s is drained by polling a 32-bit data register over APB; each read is 2 + N = 3 cycles and the reads never overlap (no pipelining), so the link tops out at 100 MHz/3 = 33.3 M reads/sec = 133 MB/s raw, and once per-word software loop and poll overhead are counted the sustained drain falls below 40 MB/s, so the FIFO overflows and samples are lost — root cause being the fixed 2-cycle floor and no pipelining, throughput capped at f_clk/(2+N), not bytes/clk. Bottom (correct, green): a DMA engine moves the FIFO in bursts over a pipelined bus (AXI/AHB), decoupled from APB and easily exceeding 40 MB/s, while APB carries only the sparse control/config registers it was designed for. The figure stresses APB is a control bus by design — size bulk data as transfers × (2 + N_avg) and move it to DMA.

8. Verification perspective

Throughput is a measured steady-state rate, so verification's job is to instrument it correctly, cover the wait-count regimes that move it, and regress peak-versus-achieved so a throughput drop trips before silicon.

  • Measure steady-state throughput, not a single transfer. Bind a counter that increments on each (penable && pready) completion and sample it over a fixed window: transfers/sec = xfers_in_window / window_seconds. Cross-check against the formula — a saturated zero-wait stream should measure f_clk/2; if it doesn't, the monitor (or the stimulus saturation) is wrong, and every downstream number is suspect.
  • Cover the N_avg range, not just one regime. Functional coverage should bin the per-window average wait count across zero-wait, light-wait, and heavy-wait regimes, because throughput is a strong function of N_avg. A suite that only ever runs the fast-register (N_avg ≈ 0) case reports the optimistic f_clk/2 and never exercises the slow-slave throughput that real traffic sees — the performance hole hides behind functional green.
  • Assert peak-vs-achieved and regress it. For throughput-critical paths, assert the measured rate clears the required minimum (xfers_in_window >= MIN_XFERS_PER_WINDOW for the workload's N_avg) and record the achieved rate as a regression metric. This catches a throughput regression — a new wait state, an over-conservative PREADY, a slave moved behind a CDC — that silently drops f_clk/(2+N_avg) below budget. Peak is the ceiling; the assertion guards the floor of acceptable achieved rate.

The point: instrument throughput as a windowed measurement, cover the N_avg distribution that drives it, and assert achieved-versus-required so a throughput drop is regressed, not discovered in the lab.

9. Interview discussion

"What's the throughput of APB?" is a precision filter exactly like the latency question. The weak answer is "it's slow" or "around the clock rate"; the strong answer is the formula, the peak, and why there's no burst bonus.

State it exactly: APB throughput is f_clk / (2 + N_avg) transfers per second, and bus_width_bytes × f_clk / (2 + N_avg) bytes per second — the reciprocal of the 2 + N per-transfer latency from the cost model. Then deliver the depth: peak is f_clk/2 (zero waits, e.g. 50 M transfers/sec = 200 MB/s on a 32-bit bus at 100 MHz), because the two-phase floor is mandatory; waits degrade it sharply (N_avg = 1 is a 33% loss, N_avg = 2 halves it) because the denominator starts at only 2; there is no burst bonus — APB can't pipeline, so each transfer pays its own 2 + N and back-to-back streams don't amortise the floor; and width scales bytes/sec but not transfers/sec. The senior flourish ties it to design intent: "APB's throughput is low by design — it's a control bus for sparse register traffic, where simplicity and timing closure beat bandwidth; bulk data goes on DMA + a pipelined bus, and the moment someone needs streaming throughput on APB it's a bus-choice error, not a tuning problem." Saying "throughput is just f_clk/(2+N_avg), dominated by the floor and the average wait, with no pipelining to hide behind" signals you have actually sized an APB subsystem.

10. Practice

  1. Compute the peak. At an 80 MHz APB clock with a 32-bit bus and zero waits, give peak transfers/sec and MB/s. State the formula you used.
  2. Apply a wait. For the same 80 MHz / 32-bit link with N_avg = 2, compute transfers/sec and MB/s, and the percentage of peak.
  3. Isolate the width effect. Take the N_avg = 1, 100 MHz case and give transfers/sec and bytes/sec for a 32-bit and a 64-bit bus. State which number changes and which doesn't, and why.
  4. Disprove the burst bonus. A burst of 10 back-to-back zero-wait transfers: give the total cycles and the effective per-transfer cost, and explain why it's not less than 2 per transfer.
  5. Size a stream. A peripheral must sustain 120 MB/s on a 100 MHz, 32-bit APB. Compute the required transfers/sec, the N_avg that would deliver it, and state whether APB can meet it — and what you'd do if it can't.

11. Q&A

12. Key takeaways

  • APB throughput is f_clk / (2 + N_avg) transfers per second, and bus_width_bytes × f_clk / (2 + N_avg) bytes per second — the plain reciprocal of the 2 + N per-transfer latency, with no other terms.
  • Peak is f_clk/2 (zero waits): 50 M transfers/sec = 200 MB/s on a 32-bit bus at 100 MHz. The two-cycle floor is mandatory, so there is no faster APB and "100 MHz ≠ 100 M transfers/sec."
  • Waits degrade it sharply. Because the denominator starts at 2, N_avg = 1 is a 33% loss and N_avg = 2 halves throughput. The fixed floor and N_avg dominate everything.
  • No burst bonus. APB cannot pipeline, so back-to-back transfers each pay their own 2 + N; throughput is never amortised across a stream. Bursty/streaming traffic belongs on a pipelined bus.
  • Width scales bytes, not transfers. Doubling the bus doubles bytes/sec but leaves transfers/sec = f_clk/(2+N_avg) unchanged — bandwidth per transfer, not a faster cadence.
  • APB is modest by design — and that's correct. It is a control bus for sparse register traffic; size data streams as transfers × (2 + N_avg) and move bulk to DMA, then verify achieved throughput against the requirement so a regression is caught, not discovered.