You now speak
The simplest fabric is the shared bus: one set of wires, everyone attached, one conversation at a time. It's small and easy — and it serialises the whole chip. While the DMA engine streams a megabyte to memory, the CPU's urgent little UART poke stands in line behind it. Shared buses ruled the microcontroller era; they suffocate anything bigger.
The opposite extreme is the crossbar: a private potential path from every
master to every slave —
Inside the fabric, your old friend address decode does the routing: the high bits of each transaction's address select the output port, exactly as in the memory-map lesson — just replicated per master and pipelined.
The last step is the important one: a crossbar removes structural conflicts (fights over wires) but cannot remove destination conflicts. Two masters, one slave — someone must choose. That someone is the arbiter.
An arbiter is a small combinational-plus-state machine at each contended port, answering one question per cycle: of the masters requesting this slave, who wins? The classic policies:
Which thumb, on which scale, is quality of service (QoS). The CPU's cache miss is latency-critical — a stalled pipeline bleeds performance every waiting cycle, but it only wants a cache line. The DMA engine is the opposite: bandwidth-hungry but patient — no one cares whether any single beat waits, only that megabytes per second keep flowing. A good fabric lets urgent-and-small overtake bulky-and-patient, and modern interconnects carry explicit QoS priority values on each transaction for exactly this purpose.
Two more realities of grown-up fabrics. Outstanding transactions: with AXI IDs, several masters' requests can be in flight through the fabric at once, and responses must find their way back — the fabric appends routing information to each ID so the R and B channels retrace their steps. And pipelining: a big fabric's paths are long, so designers drop register slices (a rank of flip-flops on each channel) into the middle. Each slice buys back clock frequency — shorter combinational paths, exactly the timing-closure trade you know from the RTL module — at the cost of a cycle of latency. The fabric is a pipeline you route messages through, not a wire.
Policies sound abstract until you count grants. Below, four masters share one slave for 300 cycles. Master 0 is greedy (requests 95% of cycles); the others request 40% of cycles. Same traffic, two arbiters — compare who actually gets served:
Under fixed priority, masters 1–3 only ever win in the rare cycles when master 0 is silent — and
master 3 needs masters 0, 1 and 2 all silent at once. Round-robin serves the same greedy
master less and everyone else steadily. Nudge master 0's 0.95 up to 1.0
and rerun: under fixed priority the other three masters flatline at exactly zero. That's
starvation — not slow service, no service.
A surprising QoS folk theorem from real chips: the humble display controller often carries the highest priority in the whole memory system — above the mighty CPU. Why? Because its deadline is physical. The panel's pixel clock drains the display FIFO at a fixed, non-negotiable rate; if a frame fetch arrives even slightly late, the FIFO underruns and the user sees the screen glitch. A CPU that waits an extra 100 ns merely runs imperceptibly slower — a display that waits too long fails visibly, every time. So QoS design starts not from "who is important?" but from "who has a hard real-time deadline, and what is the cost of missing it?" Bandwidth-hungry but deadline-free agents (DMA bulk copies) go last, latency-sensitive agents (CPU misses) go in the middle, and anything wired to physics goes first.
Fixed priority feels harmless in simulation, because testbenches politely take turns. Real traffic doesn't. A GPU or DMA engine in a busy phase can request every single cycle — and behind a fixed-priority arbiter, a lower-priority master then waits literally forever. The system-level symptom is horrible to debug: the chip doesn't crash; one subsystem just silently never makes progress — a watchdog fires seconds later, far from the cause, and nothing in any single block's verification ever looked wrong. Chips have shipped with exactly this bug. The rule of the experienced integrator: default to round-robin (or any policy with a service guarantee), and use bare fixed priority only where you can prove the high-priority master's traffic is bounded. An arbiter is a promise about worst-case behaviour, not average behaviour.