From Simple to Layered
Two testbenches already exist elsewhere on this site, for the same full adder. Both are worth looking at honestly, one more time, before building anything new — not to criticize them, since each did exactly its job at the scope it was written for, but to name precisely what's still missing before either deserves the word "layered."
Verilog's directed testbench
Simple Testbenches applied eight fixed input combinations from one initial block, spaced by #10, checked by eye via $monitor — later upgraded to a self-checking version comparing {cout, sum} against a + b + cin directly in an if/else. Two real limits, both structural rather than fixable with more code in the same shape:
- Stimulus is enumerated, not generated. Eight combinations were written out by hand because eight is small enough to write out by hand. A 32-bit adder, or a FIFO with a dozen interacting control signals, has a state space no one enumerates directively — it needs randomization, which needs somewhere to live other than a flat list of assignments.
- Driving and checking are the same block. The
if/elsesits directly inside the sameinitialblock driving stimulus. There's no way to reuse the checking logic against different stimulus, or the stimulus against a different check, without copying code.
SystemVerilog's class-based capstone
SystemVerilog's own capstone already went further — genuinely further than it might look at first glance. It has a driver class pulling add_transactions off a mailbox, a scoreboard class with an immediate assertion and a covergroup, and fork/join_none running both concurrently while the top-level initial block generates and randomizes stimulus. That's real architecture, not a toy — three of this topic's eventual components already exist there in embryonic form.
Read closely, though, three gaps are still there, and they're exactly the gaps a layered testbench closes:
- No monitor. The
scoreboardreadsvif.cb.sum/vif.cb.coutdirectly, every cycle, inside its ownrun()task. Checking is tightly coupled to observation — there's no independent component whose only job is watching the DUT's pins and reporting what it sees, which means nothing else in the testbench (a coverage collector, a protocol checker, a second scoreboard) can observe the same traffic without duplicating that same directvifaccess. - No generator class. Stimulus is created and randomized inline, in
tb_top's owninitialblock —repeat (20) begin add_transaction tr = new(); tr.randomize(); gen2drv.put(tr); end. That's fine for one fixed test; it means every different test scenario needs its own copy oftb_top, rather than reusing one environment with a different stimulus source dropped in. - No environment, no test.
driverandscoreboardare constructed and wired directly insidetb_top— there's no single class that owns "build and connect everything," and nothing separating "the environment's structure" from "this particular test's stimulus and pass/fail criteria." Every new test means editing the module that also happens to instantiate the DUT.
None of this is a flaw in that page — it was deliberately minimal, closing out a language topic, not an architecture one. But it's the precise, concrete list of what "layered" adds on top of "class-based": a monitor decoupled from checking, a generator decoupled from driving, and an environment/test split decoupled from both.
This isn't specific to one adder
It's worth naming explicitly that the directed-vs-layered gap above isn't a quirk of this particular full adder example — it's the same progression the whole industry describes as directed testing versus constrained-random verification. A directed test, by construction, only exercises the specific scenario its author thought to write, one feature at a time; growing coverage means writing more directed tests by hand, and that approach becomes unmanageable well before a real design's state space is covered — corner cases nobody thought to direct stimulus at simply never get exercised; the earlier eight-combination full adder example is a toy-scale version of a problem that gets qualitatively harder, not just bigger, as a design grows. Constrained-random verification instead generates large volumes of legal-but-varied stimulus from a smaller set of constraints, closing coverage faster and reliably surfacing the corner cases a human wouldn't have thought to write by hand — which is exactly why "stimulus is enumerated, not generated" was flagged as the first structural limit above, and why a generator (built a few pages from here) is one of the four missing pieces, not an optional nicety.
Naming the industry's own layer stack
The four components in the table below aren't an ad-hoc list — they map onto a formal five-layer model the verification industry already names (popularized by Janick Bergeron's Writing Testbenches and echoed across SystemVerilog verification methodology references): a signal layer (the interface/clocking-block wiring to the DUT's actual pins), a command layer (lower-level driver/monitor components plus assertions, translating between pins and transactions), a functional layer (higher-level driver/monitor components and the self-checking scoreboard structure), a scenario layer (generators producing streams of transactions under weights/constraints), and a test layer (where a specific test configures the scenario layer, or in directed cases drives the command layer directly). This site's generator/monitor/scoreboard/environment/test components correspond to the scenario, command+functional, functional, and test layers respectively — the same architecture, in this curriculum's own vocabulary rather than borrowing the five-layer names wholesale, since the goal here is building the pieces, not reciting a taxonomy.
What this topic builds instead
| Missing piece | What it becomes |
|---|---|
Stimulus generation, inline in tb_top | A standalone generator class, publishing to a mailbox |
Checking, reading vif directly | A monitor observing pins independently, feeding a second mailbox to a scoreboard |
| Driver + scoreboard wired directly in the top module | A single environment class owning construction and connection |
One initial block per test scenario | A test class, separated from the environment it configures |
Every row above is a page in this topic. The next page introduces the actual DUT and interface all of them build against, plus the block diagram tying the whole layered architecture together before any single piece gets built.