"Here's a block you've never seen. Four AXI masters, three slaves, an interconnect in the middle. You've got the whiteboard for forty-five minutes: how would you verify it?" Some candidates start drawing agents at once. Others list every feature they can think of. Both run out of time with no checking strategy on the board, and checking strategy is what the interviewer came to hear. The architecture round rewards a structure you can apply to any block, delivered at the right depth.
This lesson gives that structure, then three full scenarios with model answers: the two-DMA subsystem this course really built, an AXI interconnect with N masters and M slaves, and a cache controller. The first has real code and a real log behind it; the other two are the scenarios senior loops ask most often.
An architect presenting a house does not start with the paint. They show the plot, the load-bearing walls, the plumbing and the inspection plan, then take questions on any room. Your answer should do the same: clarify, draw the bench, then spend most of the time on checking, because that is where verification succeeds or fails. Agents are the easy part; anyone can draw them.
Step | What you say | Time (45 min) |
|---|---|---|
1. Clarify | Interfaces, configurations, what "done" means, what already exists (VIP, models, formal) | 3–5 min |
2. Draw the bench | Agents per interface (active, passive or reactive), env, config object | 5 min |
3. Checking | Where the reference model lives, what each checker owns, end-of-test rules | 12–15 min |
4. Stimulus and coverage | Features to plan items to coverage to tests; directed corners; error injection | 8 min |
5. Hard cases | Reset mid-test, errors, ordering, deadlock, performance | 5 min |
6. Reuse and other methods | What goes up to SoC level, what formal or emulation takes | 3 min |
7. Plan and risks | Effort estimate, milestones, the riskiest assumption | 3 min |
"Your DMA block bench is done. Now the design team chains two DMAs: dma0's output stream feeds dma1's input. Both have their own APB register port. How do you verify the subsystem?"
Model answer. "I reuse the block env twice, unchanged, because it already checks each DMA from its own monitors. The only interface that changes role is dma1's input: dma0 now drives it, so dma1's input agent goes passive, by a config_db setting from the subsystem env before it builds the block envs. Both block scoreboards keep running: dma1's still checks dma1 because its passive input monitor still sees every word. Then I add one new checker the block level could never have: end to end, every word out of dma1 equals the word into dma0 XOR both keys. It needs no knowledge of the middle stream, so it still works if that stream is buried inside the chip later. A subsystem config object holds the two decisions: whether the chain is internal, and whether the end-to-end checker exists."
// snippet: from the course DMA bench
// Two DMAs in a chain (dma0's output feeds dma1's input), verified by reusing the
// block env twice, unchanged, plus an end-to-end checker.
class dma_subsys_cfg extends uvm_object;
bit has_e2e_checker = 1; // the end-to-end scoreboard
bit chain_internal = 1; // dma1's input is driven by dma0, so its agent is passive
`uvm_object_utils(dma_subsys_cfg)
function new(string name = "dma_subsys_cfg");
super.new(name);
endfunction
endclass
class dma_subsys_env extends uvm_env;
`uvm_component_utils(dma_subsys_env)
dma_subsys_cfg cfg;
dma_env dma0, dma1; // the block env, reused as is
dma_e2e_scoreboard e2e;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(dma_subsys_cfg)::get(this, "", "cfg", cfg))
cfg = dma_subsys_cfg::type_id::create("cfg"); // defaults if the test set none
if (cfg.chain_internal)
uvm_config_db#(uvm_active_passive_enum)::set(this, "dma1.in_agt", "is_active", UVM_PASSIVE);
dma0 = dma_env::type_id::create("dma0", this);
dma1 = dma_env::type_id::create("dma1", this);
if (cfg.has_e2e_checker) e2e = dma_e2e_scoreboard::type_id::create("e2e", this);
endfunction
function void connect_phase(uvm_phase phase);
if (cfg.has_e2e_checker) begin
dma0.apb.ap.connect(e2e.key0_export);
dma1.apb.ap.connect(e2e.key1_export);
dma0.in_agt.ap.connect(e2e.first_export);
dma1.out_agt.ap.connect(e2e.last_export);
end
endfunction
endclass+UVM_TESTNAME=dma_subsys_test (run.sh dma_subsys_tb.sv)
UVM_INFO dma_tb.sv(411) @ 875000: uvm_test_top.env.dma0.sb [SB] 10 words matched, 0 mismatched
UVM_INFO dma_tb.sv(411) @ 875000: uvm_test_top.env.dma1.sb [SB] 10 words matched, 0 mismatched
UVM_INFO dma_tb.sv(1182) @ 875000: uvm_test_top.env.e2e [E2E] 10 words matched end to end, 0 mismatched
UVM_ERROR : 0Three checkers, three independent claims: dma0 is right, dma1 is right, and the chain is right. Follow-ups to expect:
Why keep the block scoreboards if the end-to-end one passes? They localise a failure to one DMA. The end-to-end checker only says the chain is wrong somewhere.
What if the middle stream is not observable at chip level? Then dma1's input monitor has nothing to watch, so its block scoreboard goes too; the end-to-end checker is the one that survives, which is why you design it not to need the middle.
What changes at SoC level? The APB agents go passive as well, since a CPU or a bus fabric now drives the registers; the register model's prediction moves to explicit prediction from the passive monitor. See Vertical Reuse.
This is the scenario most likely to be asked, and the one to draw. Start with the bench, then put most of the time into the scoreboard.
Model answer, the bench. "An active AXI master agent on each master port, and a reactive slave agent on each slave port: its monitor sees a request, a slave sequence decides the response (latency, OKAY or SLVERR), and a memory model returns read data. All monitors are passive observers feeding the checkers. A config object sets N, M, the address map, ID widths and the slaves' response behaviour."
Model answer, the checking. This is the part to spend time on:
Routing. For every request seen at a master port, the address map model predicts the destination slave, or a decode error for an unmapped address. The scoreboard then expects that request to appear at that slave port with the same address, length, size, burst and data.
IDs. The interconnect usually widens the ID on the slave side with the master's index, so the scoreboard keys transactions by (master, ID), and checks the response comes back to the right master with the original ID restored.
Ordering. AXI lets several outstanding transactions share one ID, and responses with the same ID must return in order; different IDs may return in any order. So the scoreboard keeps a queue per (master, ID) and compares at its head. A single slot per ID breaks on the second outstanding transaction.
Write data. In AXI4, write data has no ID and may arrive before its address. Hold write data per master in order and pair it with addresses in issue order.
Decode errors. An unmapped address must get DECERR from the interconnect's default slave, with the right number of read beats, and must never appear at any slave.
Deadlock and starvation. Every outstanding transaction carries an age; a checker flags any older than a bound. Under heavy cross traffic, a watchdog per master catches a master that never gets a grant.
QoS and arbitration. Check statistically, not cycle by cycle: under saturation, higher-QoS masters get lower average latency, and nobody exceeds the starvation bound. Cycle-exact arbitration checks make the bench brittle to legal design changes.
Bandwidth and latency budgets. A separate performance test saturates the fabric with legal traffic, timestamps every request and completion in the monitors, and reports per-master bandwidth and latency percentiles against the spec's budget at end of test. Coverage records which load levels and mixes were measured, so 'meets 80% of peak' is a measured claim, not an assumption.
Model answer, stimulus and coverage. Cover the cross of master × slave × burst type × outstanding depth, same-ID reordering attempts, narrow and unaligned transfers, 4 KB boundary handling, decode errors and back-pressure on every channel. Directed tests for the corners random rarely makes: all masters to one slave, one master to all slaves at once, exclusive accesses if supported. Formal fits well on the arbiter and on the per-channel handshake rules (VALID held until READY), using assertion VIP bound to every port.
Follow-ups: How do you know a read returned the right data? (the slave's memory model is shared with the scoreboard, or the scoreboard predicts read data from earlier writes it saw at that slave).
"Verify an L1 data cache controller: CPU side request port, memory side refill and write-back port, a snoop port for coherency."
Model answer. "The cache should be invisible to correctness: every CPU read returns the value of the most recent write to that address, whatever the cache did inside. So the main reference model is not a cache, it is a flat memory: I predict each CPU read from a sparse memory updated by the CPU writes I observe, plus writes from other agents seen on the snoop or memory side. That checker catches data corruption, lost writes and stale reads without modelling a single tag. Separately, I check the memory-side traffic for policy: misses cause refills of whole lines, dirty lines are written back on eviction in a write-back cache and never in a write-through one, and nothing is written back twice."
Coherency, at a high level. With two caches or a snoop agent, check the invariants rather than modelling the protocol cycle by cycle: at most one writer per line, a snoop to a modified line returns the latest data, and the line states seen in responses follow the protocol's legal transitions. Assertions on the snoop interface carry most of this.
Directed corner cases. A snoop arriving while the same line is being evicted dirty; two misses to the same line back to back (does the miss handling merge them?); a write to a line during its refill; thrashing one set past its associativity; a flush or invalidate during traffic; an error response on a refill.
Formal for the controller FSM. The control state machine is a small state space with nasty interleavings: prove no deadlock, every request eventually completes, and only legal state transitions happen. Simulation is poor at finding the one interleaving out of millions; formal is good at it.
Coverage. Hit and miss per way, eviction of clean and dirty lines, every legal line-state transition, snoops in each state, and the corner cases above as named plan items.
Follow-ups: Why not a cycle-accurate cache model? (it duplicates the design's decisions, so it shares its bugs and breaks on every legal change; the flat-memory model checks the only thing software cares about). How do you check performance? (separate counters for hit rate and miss latency against a spreadsheet budget, reported, not failed, on random traffic).
Most architecture rounds end here. Give a method, not a number pulled from the air: count the features, turn each into plan items, estimate per item with a complexity factor, and split the work into bring-up (bench, agents, first checker, smoke passing) and closure (coverage, corner cases, sign-off), which is usually the larger half. Then add the risk: a VIP you have to buy and integrate, a reference model that does not exist yet, a spec still changing. Say what you would cut first if the schedule shrank, and point to Closure and Sign-off Questions for how you would make that cut visible.
Agents for forty minutes. Drawing every port and sequencer in detail leaves no time for checking, which is what was being graded.
A reference model that copies the design. A cycle-accurate model of the interconnect's arbiter shares the design's assumptions and breaks on every legal change. Model the contract (routing, ordering, data), not the implementation.
No end-of-test and no hard cases. Not saying how the test knows all transactions completed, or what happens on reset mid-traffic, reads as someone who has never closed a bench.
Because its monitor is still useful. dma0 drives that stream, so dma1's input agent must not drive, but its monitor feeds dma1's block scoreboard, which localises any failure to dma1. Removing the agent would lose that checker.
Several outstanding transactions with the same ID: the second overwrites the first in a one-slot-per-ID map. AXI allows that, with same-ID responses returning in order, so the scoreboard needs a queue per (master, ID) compared at its head.
Correctness for the CPU means every read returns the most recent write, whatever the cache did. A flat memory checks exactly that, independently of tags and ways, so it cannot share the design's bugs. Policy (refills, write-backs) is checked separately on the memory side.
Next: the questions that follow an architecture answer are about evidence. Closure and Sign-off Questions works them. For a full timed rehearsal, take Mock 5: Full Env Whiteboard.