AMBA AXI · Module 16
Negative & Error-Injection Testing
Deliberately drive error and illegal scenarios to verify an AXI DUT's error handling — provoking SLVERR/DECERR with bad addresses and read-only writes, injecting errors from an error-capable slave model, and (carefully) probing illegal traffic — then checking the DUT responds and recovers correctly rather than hanging or corrupting state.
Constrained-random testing (16.6) deliberately stays inside the legal space. Negative and error-injection testing does the opposite: it intentionally drives the error scenarios — accesses that should return SLVERR/DECERR, slaves that fault, and (carefully) malformed traffic — to verify the DUT handles them correctly rather than hanging, corrupting state, or silently swallowing the fault. Error paths are the least-exercised and most-fragile part of any design (normal stimulus almost never triggers them), so they need deliberate provocation. This chapter shows how to provoke error responses, inject faults from an error-capable slave model, probe illegal traffic safely, and — crucially — check the recovery: that the DUT returns the right error code, stays consistent, and continues serving subsequent legal traffic.
1. Why Negative Testing Is Separate and Essential
Positive testing (random or directed) confirms the DUT does the right thing on good input. Negative testing confirms it does the right thing on bad input — a distinct, equally important question that positive testing structurally cannot answer, because legal traffic rarely produces errors. The error-handling logic (decode-error paths, SLVERR generation, recovery) is real RTL that must be exercised, and it's where bugs hide precisely because it's rarely hit.
2. Provoking Error Responses
The first class of negative test provokes the error responses the protocol defines — these are legal transactions that the slave is supposed to error on. Drive them deliberately and confirm the correct code comes back:
DECERR— access an unmapped address (no slave at that decode). The interconnect's default slave should returnDECERR.SLVERR— write to a read-only register, access a reserved offset, or hit a slave-defined error condition. The slave should returnSLVERRand not change state.EXOKAY/ exclusive failures — attempt an exclusive access that fails its monitor; confirmOKAY(notEXOKAY) is returned to signal failure.
// Directed negative sequence: provoke each error response
task provoke_errors();
axi_txn t = axi_txn::type_id::create("t");
// DECERR: write to an address with no slave mapped
start_item(t); assert(t.randomize() with { addr == UNMAPPED_ADDR; });
finish_item(t); // expect BRESP == DECERR
// SLVERR: write to a read-only register
start_item(t); assert(t.randomize() with { addr == RO_REG_ADDR; });
finish_item(t); // expect BRESP == SLVERR, reg unchanged
// Reserved offset read
start_item(t); assert(t.randomize() with { addr == RESERVED_ADDR; });
finish_item(t); // expect RRESP == SLVERR
endtaskThe scoreboard's reference model (16.4) must predict these error responses — it knows the address map, so it expects DECERR for unmapped and SLVERR for read-only — and verify the DUT both returns the right code and leaves state unchanged (a read-only write must not modify the register).
3. Error Injection from a Faulting Slave Model
The second class injects faults that don't originate from the manager's request but from the slave side — an error-capable slave model (or VIP) configured to return errors on demand, drop responses, stall indefinitely, or corrupt data, so you can verify how the manager and interconnect react. This tests the error-propagation and recovery paths the slave's own legal behavior wouldn't trigger.
The injected error then propagates, and the test verifies the response and the subsequent recovery on the bus:
Injected SLVERR then recovery
10 cycles4. Probing Illegal Traffic — Carefully
The third, most delicate class drives protocol-illegal traffic to test a checker, an interconnect's robustness, or a slave's defensive behavior. This is the deliberate inverse of 16.6's legality constraints: temporarily relax a constraint to emit, say, a 4 KB-crossing burst or a reserved encoding. It must be done carefully and in isolation, because the result is often undefined by the spec — the goal is usually to confirm a protocol checker fires (an illegal_bins hit or assertion) or that the DUT degrades gracefully, not to assert a particular functional outcome.
// Illegal-traffic sequence: relax a legality constraint deliberately.
// Used to confirm a CHECKER fires, not to assert functional behavior.
class axi_illegal_seq extends axi_rand_seq;
task body();
axi_txn t = axi_txn::type_id::create("t");
start_item(t);
// Disable the 4 KB constraint for this item, force a crossing burst
t.c_no_4k_cross.constraint_mode(0);
assert(t.randomize() with { burst == 1; // INCR
(addr % 4096) > 4000; // near page end
len > 8; }); // span crosses 4 KB
finish_item(t); // EXPECT: protocol checker/assertion fires
endtask
endclassThe key discipline: illegal-traffic tests verify the checker, not the DUT's functional response — since the spec doesn't define behavior for illegal input, asserting a specific functional result would be wrong. Keep them isolated so they can't pollute the legal-traffic scoreboard.
5. Common Misconceptions
6. Debugging Insight
7. Verification Insight
8. Interview Questions
9. Summary
Negative and error-injection testing deliberately exercises what positive testing structurally cannot: the DUT's behavior on bad input. It has three classes. Provoking error responses drives the protocol's defined errors — DECERR (unmapped addresses), SLVERR (read-only writes, reserved offsets, slave faults), exclusive-access failures — with the scoreboard's reference model predicting each and confirming both the right code and no wrongful state change. Slave-side error injection uses a configurable faulting slave model to return errors, drop/delay responses, or corrupt data, exercising the manager's and interconnect's error-propagation, timeout, and recovery logic. Illegal-traffic probing (relaxing a legality constraint) drives undefined scenarios in isolated sequences solely to confirm a checker fires — never to assert a functional outcome the spec doesn't define.
The decisive discipline is to check recovery, not just the response: the right error code is necessary but insufficient — the DUT must also suppress error side effects (a read-only write changes nothing), free resources (outstanding counters/FIFOs cleared), and keep serving subsequent legal traffic without hanging. The highest-severity, easiest-to-miss failures are hang-after-error (broken recovery/liveness) and state-change-despite-error (side effect not suppressed) — both pass a naive error-code check. Recovery is verified rigorously by following every error with legal traffic, checking resource accounting, using timeout watchdogs, and stressing errors under concurrency. With negative testing alongside assertions, monitors, scoreboards, coverage, and constrained-random, verification establishes not just "works when right" but "fails safely when wrong." Next, we package the whole environment into the reusable UVM AXI agent.
10. What Comes Next
You've completed the stimulus/checking toolkit; next we package it for reuse:
- 16.8 — UVM AXI Agent Overview (coming next) — assembling the driver, sequencer, and monitor into a reusable UVM agent, the standard packaging that bundles an interface's verification into one drop-in unit.
Previous: 16.6 — Constrained-Random AXI Traffic. Related: 6.8 — RRESP, BRESP & RLAST for the response codes provoked here, 12.3 — Decode & Address Map for the unmapped-address DECERR path, and 16.4 — AXI Scoreboards for the reference model that predicts errors.