UVM
Environment Reuse
How an environment, built once, is reused across tests, integration levels, and projects — the test/environment separation that lets one environment serve every test, the configuration and conventions that make an environment plug-and-play, and packaging a whole environment as a unit.
Environments · Module 15 · Page 15.4
The Engineering Problem
Layered environments showed vertical reuse — nesting an env up the design hierarchy (Module 15.3). But that's one face of a larger picture. An environment, built once, should be reusable across three axes: across tests (one env, many tests — the primary axis), across integration levels (nesting, Module 15.3), and across projects (a subsystem env reused on a derivative chip). The most important and most-often-exercised is across tests: a single environment serves every test in a project — dozens or hundreds of them — each running different stimulus. This only works if the environment is test-agnostic: it owns the reusable structure (agents, scoreboard, coverage, coordination), while each test owns the variable scenario (which stimulus, which config). The moment a test-specific assumption is baked into the environment, the env welds to that test and breaks (or mis-checks) every other. The problem this chapter solves is making an environment a true plug-and-play verification component — reusable across tests, levels, and projects — through the test/environment separation and the reuse disciplines.
Environment reuse is the discipline that makes an environment, built once, reusable across tests, integration levels, and projects. Its central enabler is the test/environment separation: the environment owns the reusable structure (the agents, scoreboard, coverage, virtual sequencer, and their connections); each test owns the variable scenario (which virtual sequence to run, which configuration to set). Because the env holds structure and the test holds scenario, one env serves all tests — provided no test-specific (scenario) logic leaks into the env. The same general reuse disciplines apply (Module 14.4): self-containment (the env depends only on its DUT and config, not on a specific test or outer context), configuration over modification (an env config object, set by whatever instantiates it), a clean boundary (a virtual sequencer to run sequences on, analysis access to subscribe to), and packaging (the env travels with its config, virtual sequences, and scoreboards as a verification component). This chapter is environment reuse: the three axes, the test/environment separation, the conventions that make an env plug-and-play, and the test-specific assumption that breaks reuse across tests.
How is an environment, built once, reused across tests, integration levels, and projects — and why is the test/environment separation (the env owns reusable structure, the test owns variable scenario) the discipline that lets one environment serve every test?
Motivation — why environment reuse, and why test-agnostic
An environment is expensive to build and immensely reusable — if kept test-agnostic. The reasons:
- One environment serves many tests. A project has dozens or hundreds of tests, all running against the same DUT through the same structure. Building the env once and reusing it across all tests is the primary economy — every test inherits the agents, scoreboard, coverage, and coordination for free.
- The test/environment separation is what enables across-test reuse. The env owns reusable structure; the test owns variable scenario. Because they're separate, one env serves all tests. Mixing them — baking a test's scenario into the env — welds the env to that test and breaks the reuse.
- Environments reuse across integration levels. Nesting (Module 15.3) reuses an env up the hierarchy — a block env inside a subsystem env. The same env, reconfigured, serves standalone and nested.
- Environments reuse across projects. A subsystem env built for one chip is reused on a derivative (a new product with the same subsystem). The env, built reusably, drops into the new project.
- Reuse is fragile — keep the env general. As with agents (Module 14.4), one test-specific assumption, one hardcoded expectation, one baked-in scenario destroys the reuse. The env must stay general — structure, not scenario.
The motivation, in one line: an environment is built once and reused across dozens of tests, every integration level, and derivative projects — an enormous economy — but the reuse depends on the env staying test-agnostic: the test/environment separation (env = reusable structure, test = variable scenario) is what lets one env serve all tests, and a single test-specific assumption baked into the env forfeits it.
Mental Model
Hold environment reuse as one stable test rig that every experiment runs through:
An environment is the stable test rig every experiment runs through: built once as reusable infrastructure, it serves every test, nests into bigger rigs, and moves to new labs — but a rig welded to one experiment can't be reused. The rig is the structure; the experiment is what varies. Picture a permanent lab rig set up around a device — instruments, analyzers, wiring (the environment: agents, scoreboard, coverage, connections). The rig is infrastructure: built once, carefully, and left in place. The experiments (the tests) are what vary — experiment A applies one set of conditions, experiment B another — but they all run through the same rig. You don't rebuild the rig per experiment; you run a different experiment through it (run a different virtual sequence, set different config). This is the whole economy: the rig is reused, the experiments are cheap to write (each is just the variable part). And the rig travels: it nests into a bigger rig (a subsystem rig containing this one), and it moves to a new lab (a derivative project). The fatal mistake is welding an experiment into the rig — building in a specific experiment's expected reading, or hardwiring one experiment's conditions into the infrastructure. Then the rig only works for that experiment: run a different one and the baked-in reading is wrong (mis-checks) or the hardwired conditions fight the new ones. A reusable rig is general infrastructure — it holds no experiment — so any experiment runs through it correctly. The rig provides the structure (drive here, measure there, analyze thus); the experiment provides the scenario (what to drive, what to expect).
So environment reuse is one stable rig, many experiments: the environment is reusable infrastructure (structure — agents, scoreboard, coverage), built once and run through by every test, nested into bigger rigs, moved to new projects. The test is the variable experiment (scenario — which sequence, which config). The discipline is the test/environment separation: keep the rig general (no experiment baked in) so every test runs through it — weld an experiment in, and the rig serves only that one.
Visual Explanation — the three axes of environment reuse
The defining picture is the three axes: one environment, built once, reused across tests, across integration levels, and across projects.
The figure shows the three axes of environment reuse — the range of contexts one env serves. A single environment, built once (the structure), fans out to three kinds of reuse. Across tests: dozens or hundreds of tests run against the same environment, each supplying different stimulus and config — the primary axis, exercised constantly (every test reuses the env). Across integration levels: the environment nests as a sub-environment up the design hierarchy — block into subsystem into SoC (Module 15.3). Across projects: the environment drops into a derivative chip that reuses the same DUT or subsystem. The crucial reading is that all three reuse the same environment code — they differ only in test scenario (which sequences) and configuration (which settings). The brand-colored env is built once; the success-colored contexts are all served without rewriting it. The across-tests axis is distinctive to environments (and the most exercised): unlike an agent (whose primary reuse is across instances and projects), an environment's dominant reuse is across the many tests of one project — the env is the stable substrate, tests are the variable layer. This is why the test/environment separation matters so much for environments: the across-tests axis only works if the env is test-agnostic. The diagram is the scope of env reuse: one env, built once, amortized across every test, every level, every derivative — provided it stays general structure, not baked-in scenario.
RTL / Simulation Perspective — the test/environment separation
In code, the test/environment separation is concrete: the environment builds the structure (agents, scoreboard, vseqr) and nothing test-specific; the test supplies the scenario (config, which virtual sequence). The code shows the split.
// === ENVIRONMENT: reusable STRUCTURE only — no test-specific scenario baked in ===
class my_env extends uvm_env;
`uvm_component_utils(my_env)
my_agent in_agt, out_agt; my_scoreboard sb; my_coverage cov; my_vseqr vseqr;
my_env_config cfg; // configured, not modified
function void build_phase(uvm_phase phase);
if (!uvm_config_db#(my_env_config)::get(this, "", "cfg", cfg)) `uvm_fatal("NOCFG","")
in_agt = my_agent::type_id::create("in_agt", this); // structure...
out_agt = my_agent::type_id::create("out_agt", this);
sb = my_scoreboard::type_id::create("sb", this);
cov = my_coverage ::type_id::create("cov", this);
vseqr = my_vseqr ::type_id::create("vseqr", this);
// ✗ NEVER start a specific sequence here, or hardcode a test's expected value in sb
endfunction
function void connect_phase(uvm_phase phase);
in_agt.ap.connect(sb.expected_imp); out_agt.ap.connect(sb.actual_imp);
vseqr.in_seqr = in_agt.seqr; vseqr.out_seqr = out_agt.seqr;
endfunction
endclass
// === TEST: the variable SCENARIO — config + which virtual sequence to run ===
class my_test extends uvm_test;
my_env env;
function void build_phase(uvm_phase phase);
my_env_config c = my_env_config::type_id::create("c");
c.in_active = UVM_ACTIVE; /* this test's choices */
uvm_config_db#(my_env_config)::set(this, "env", "cfg", c);
env = my_env::type_id::create("env", this); // reuse the SAME env class
endfunction
task run_phase(uvm_phase phase);
my_scenario_vseq vseq = my_scenario_vseq::type_id::create("vseq"); // THIS test's scenario
phase.raise_objection(this);
vseq.start(env.vseqr); // run the scenario on the env's vseqr
phase.drop_objection(this);
endtask
endclass
// A DIFFERENT test reuses the SAME env, sets different config, runs a different virtual sequence.The code shows the test/environment separation that enables across-test reuse. The environment builds only the reusable structure — the agents, scoreboard, coverage, and virtual sequencer — and connects them; it is configured (gets my_env_config), not modified. The ✗ comment marks the discipline: the env never starts a specific sequence or hardcodes a test's expected value — that would be test-specific scenario leaking into reusable structure. The test supplies the variable scenario: it creates and sets the env's config (this test's choices), instantiates the same env class, and runs this test's virtual sequence on the env's virtual sequencer (vseq.start(env.vseqr)). A different test reuses the same env class, sets different config, and runs a different virtual sequence. The shape to carry: the environment is reusable structure — built and configured, test-agnostic; the test is variable scenario — config plus a virtual sequence. One env class serves all tests because all the variation lives in the tests (config + sequences), and none of it is baked into the env. The virtual sequencer (Module 15.2) is the clean boundary through which tests inject their scenarios — the env exposes it, tests run sequences on it. This separation is the env-reuse mechanism: keep the env general, put the scenario in the test, and every test reuses the one env.
Verification Perspective — what makes an environment plug-and-play
Beyond the test/environment separation, the general reuse disciplines (Module 14.4) make an environment a plug-and-play verification component. Seeing them applied to the env is seeing what packaging an env for reuse requires.
The figure shows the disciplines that turn an environment into a plug-and-play verification component. Test/environment separation (the env-specific one): the env owns reusable structure; each test owns its variable scenario — so one env serves all tests. Self-containment: the env depends only on its DUT and its config object — never on a specific test, outer env, or hierarchy (so it nests and moves freely, Modules 15.3, 14.4). Configuration over modification: an env config object carries all per-use settings (agent modes, parameters, sub-env config) — change config, never code (the cascade of Modules 14.6, 15.3). Clean boundary + packaging: the env exposes a virtual sequencer (run sequences) and analysis access (subscribe), and ships with its config, virtual sequences, and scoreboards as a unit. The verification insight is that these are the same reuse disciplines as for agents (Module 14.4) — applied to the environment — plus the env-specific test/environment separation at the top. Together, they let an environment drop into any test, level, or project by configuration alone — the definition of plug-and-play. The brand-colored separation is highlighted because it's the distinctive env-reuse enabler (agents don't have a "many tests" axis the way envs do): an env can be self-contained, configured, and packaged, but if it bakes in a test's scenario, it still fails the across-tests axis. So env reuse is the general disciplines (self-contained, configured, packaged) and the test/environment separation — all required for an env to be a true reusable verification component. The figure is the checklist for packaging an environment for reuse: separate test from env, depend only on DUT and config, configure don't modify, expose a clean boundary, ship it whole.
Runtime / Execution Flow — reusing an environment
Reusing an environment is a lightweight flow: instantiate it, configure it for this context, run a test's scenario through it — no modification. The flow shows the reuse.
The flow is the lightweight reuse that env reusability enables. Instantiate (step 1): create the same env class — for a new test, a nesting level, or a new project — code untouched. Configure (step 2): set the env config object for this context — this test's settings, this level's reconfiguration (Module 15.3), this project's parameters. Connect the boundary (step 3): target the virtual sequencer to run sequences on, and subscribe to analysis access as needed (for a test, just target the vseqr; for nesting, the outer env wires it). Run (step 4): a test runs its virtual sequence on the env's vseqr — no environment modification. The runtime insight is that all four steps are instantiate, configure, connect, run — none is modification. Adapting an environment to a new test, level, or project is entirely configuration and the test's own scenario; the env's code is never opened. This is why reuse is cheap: a new test is just a config + a virtual sequence (minutes), not a re-built environment (weeks). And it's why a single test-specific assumption baked into the env is so costly: it breaks the drop-in property — the env no longer works as-is for a different test, forcing modification (a fork). The flow shows the reuse promise for environments: a well-built env is adopted by configuration and a test's scenario alone — instantiate it, configure it, run your test through it — across every test, level, and project, without ever editing it. The entire value of the env-reuse discipline is keeping it that way — general structure, adapted by configuration, exercised by tests.
Waveform Perspective — the environment is invariant across tests
The deepest sign of env reuse is invariance across tests: the same environment structure processes every test's stimulus identically — only the test's scenario varies. The waveform shows the env's stable structure under a test's variable stimulus.
The environment's structure is invariant across tests — only the test's stimulus varies
12 cyclesThe waveform shows the environment invariant across tests. A test supplies stimulus through the environment — test_stim drives the DUT with this test's value (stim_data = 6B). The environment's structure processes it the same way for any test: the agent observes (env_observe), the scoreboard checks (env_check). The crucial reading is the division: the variable part is the test's stimulus (test_stim, stim_data — this test's value and scenario), while the stable part is the environment's structure (env_observe, env_check — the same observe-and-check for any test). A different test would drive a different value and a different scenario, but the same environment structure — observe, check — would handle it identically. The picture to carry is that the env_observe/env_check pattern is the environment (the stable rig), and the test_stim is the test (the variable experiment): swap the test, and test_stim changes while env_observe/env_check stay the same. This invariance of the environment's structure across tests is the visible signature of env reuse — the env carries its structure and checking with it, unchanged, regardless of which test runs through it. Reading a waveform for env reuse — does the env's structure (observe, check) operate the same way under different tests' stimulus? — confirms the test/environment separation holds. The stable env, variable test is the whole point: one environment, built once, runs every test through its unchanging structure — exactly what makes it a reusable verification component.
DebugLab — the environment that worked for one test and mis-checked the next
A reused environment mis-checking because a test-specific expectation was baked into its scoreboard
An environment was built and validated with its first test — which passed. When a second test reused the same environment (the intended reuse), the environment's scoreboard mis-checked: it flagged failures on the second test's correct behavior. The DUT was behaving correctly for the second test, but the environment's scoreboard expected the first test's behavior — so it reported mismatches that weren't real. The environment worked for one test and broke for every other.
A test-specific expectation — the first test's particular configuration/behavior — had been baked into the environment's scoreboard, instead of the scoreboard deriving expected from what it observes. So the env was welded to the first test's scenario:
✗ TEST-SPECIFIC expectation hardcoded in the ENV's scoreboard (welds it to one test):
class my_scoreboard ...;
function void check(txn t);
if (t.result != 8'hC0) `uvm_error(...) // ✗ hardcodes TEST 1's expected result (C0)
endfunction // test 2 produces a different (correct) result → mis-check
// the env only works for the test whose expectation was baked in → not reusable across tests
✓ SCENARIO-AGNOSTIC: derive expected from what is observed/configured (works for ANY test):
class my_scoreboard ...;
function void check_expected(txn in); expected_q.push_back(predict(in)); endfunction // from INPUT
function void check_actual(txn out); compare(out, expected_q.pop_front()); endfunction // vs OUTPUT
// expected is computed from the actual stimulus (via a reference model) → correct for EVERY testThis is the baked-in-scenario bug — putting test-specific (scenario) logic into the environment (reusable structure), which welds the env to one test and breaks the across-tests axis. The environment's scoreboard hardcoded the first test's expected result (C0) — a test-specific assumption. That worked for the first test (whose result was C0), so the env looked correct. But the second test produced a different result (correctly — it ran a different scenario), and the scoreboard still expected C0, so it mis-checked — flagging the second test's correct behavior as a failure. The env had test-specific scenario (a particular expected value) baked into what should be test-agnostic structure (a general checking mechanism). The test/environment separation was violated: the scenario (what to expect) leaked into the env (the structure). The fix is to make the scoreboard scenario-agnostic — derive expected from what it observes: compute the expected output from the actual input stimulus (via a reference model / predictor), so the expected is correct for whatever the test drives. Now the same env checks every test correctly, because its expectation is computed from the test's own stimulus, not hardcoded to one test. The general lesson, and the chapter's thesis: keep test-specific scenario logic out of the environment — the env owns reusable structure (a general checking mechanism that derives expected from observed), the test owns variable scenario (which stimulus); baking a test's expectation (or any test-specific behavior) into the env welds it to that test and mis-checks (or breaks) every other, forfeiting the across-tests reuse. The environment is the rig; the experiment is the test — never weld an experiment into the rig.
The tell is an environment that works for one test and mis-checks or breaks for others. Diagnose baked-in scenarios:
- Check whether a second test reuses the env correctly. If the env passes its first test but mis-checks a second, suspect a test-specific assumption baked in.
- Look for hardcoded expected values in the env. A scoreboard that hardcodes a specific result (rather than deriving it from inputs) is welded to one test.
- Look for the env starting specific sequences. An env that starts a particular scenario (rather than exposing a vseqr for tests to drive) embeds a test.
- Confirm checking derives from observation. A reusable scoreboard computes expected from the actual stimulus (a reference model), not from a fixed assumption.
Keep the environment test-agnostic:
- Derive expected from observed. The scoreboard computes expected from the actual input stimulus (a reference model/predictor), so it checks correctly for any test.
- Put scenarios in tests, not the env. The env exposes a virtual sequencer; tests run their scenarios on it — the env never starts a specific sequence.
- Never hardcode a test's values in the env. Test-specific config and expectations belong in the test; the env holds only general structure.
- Validate reuse with multiple tests. Run several different tests through the env to confirm it checks each correctly — one test proves nothing about reuse.
The one-sentence lesson: keep test-specific scenario logic out of the environment — the env owns reusable structure (a general checking mechanism that derives expected from observed stimulus), the test owns the variable scenario; baking a test's expectation or behavior into the env welds it to that test and mis-checks every other, forfeiting reuse across tests.
Common Mistakes
- Baking a test's scenario into the environment. A hardcoded expected value or a started-in-the-env sequence welds the env to one test; keep scenarios in tests and the env test-agnostic.
- Hardcoding expected values in the scoreboard. Derive expected from the observed stimulus (a reference model), so the env checks any test correctly.
- Coupling the env to a specific test or outer context. The env must depend only on its DUT and config; references to a particular test or hierarchy break reuse.
- Modifying the env per use. Reuse is configuration and a test's scenario, not modification; editing the env forks it.
- Validating reuse with a single test. One passing test proves nothing about across-test reuse; run several different tests through the env.
- Shipping the env without its package. A reusable env travels with its config, virtual sequences, and scoreboards; a bare env class forces consumers to rebuild the rest.
Senior Design Review Notes
Interview Insights
An environment, built once, is reused across three axes: across tests, across integration levels, and across projects — and across tests is the primary one. Across tests means a single environment serves every test in a project, dozens or hundreds of them, each running different stimulus and configuration. This is the dominant reuse for environments: the env is the stable substrate, and the tests are the variable layer on top, so every test reuses the agents, scoreboard, coverage, and coordination for free. Across integration levels means the environment nests as a sub-environment up the design hierarchy — a block env inside a subsystem env inside an SoC env — reconfigured for each level, which is layering. Across projects means a subsystem environment built for one chip is reused on a derivative chip that reuses the same subsystem. All three reuse the same environment code, differing only in test scenario and configuration. The across-tests axis is distinctive to environments and the most exercised — unlike an agent, whose primary reuse is across instances and projects, an environment's dominant reuse is across the many tests of one project. This is why the test/environment separation matters so much for environments: the across-tests axis only works if the env is test-agnostic, owning reusable structure while each test owns its variable scenario. The mental model is a stable test rig every experiment runs through: built once as reusable infrastructure, it serves every test, nests into bigger rigs, and moves to new projects. So while environments reuse across all three axes, the primary, constantly-exercised one is across tests, and keeping the env test-agnostic is what enables it.
The test/environment separation is the principle that the environment owns the reusable structure while each test owns the variable scenario, and it's essential because it's what lets one environment serve all the tests in a project. The environment holds the structure: the agents, the scoreboard, the coverage, the virtual sequencer, and the connections between them. It's built once, configured per context, and never modified per test. The test holds the scenario: which configuration to set and which virtual sequence to run. So when you write a new test, you don't touch the environment — you set the env's config and run your test's virtual sequence on the env's virtual sequencer. Because all the variation lives in the tests, and none of it is baked into the env, one env class serves all tests. This is essential for reuse because the across-tests axis is the primary one — a project has dozens or hundreds of tests, all against the same DUT through the same structure, and building the env once and reusing it across all of them is the main economy. If the separation is violated — if a test's scenario leaks into the env, like a hardcoded expected value or a sequence started inside the env — then the env welds to that one test and breaks or mis-checks every other, forfeiting the across-tests reuse. The discipline is to keep the env general: it provides the structure, drive here, observe there, check thus, while the test provides the scenario, what to drive and what to expect. The virtual sequencer is the clean boundary through which tests inject their scenarios — the env exposes it, tests run sequences on it. So the separation is the mechanism: keep scenarios in tests, keep the env test-agnostic, and every test reuses the one env. It's the single most important enabler of environment reuse.
A reusable scoreboard derives its expected values from what it observes — the actual input stimulus — rather than hardcoding a specific test's expected result. The trap is to bake a test-specific expectation into the scoreboard, like checking that the result equals a fixed value that happens to be what the first test produces. That works for the first test, so the env looks correct, but a second test runs a different scenario and produces a different, correct result, and the scoreboard still expects the first test's value, so it mis-checks — flagging correct behavior as a failure. The scenario, what to expect, leaked into the env, the structure. The fix is to make the scoreboard scenario-agnostic by computing expected from the actual stimulus. Concretely, the scoreboard observes the input transactions, runs them through a reference model or predictor to compute what the output should be, and compares that against the observed output. So the expected is derived from whatever the test actually drove, which means it's correct for any test — the scoreboard adapts to the scenario automatically because it computes expectation from inputs rather than assuming it. This is the general principle for reusable checking: the checking mechanism is part of the reusable structure, but the specific expectations must come from the actual stimulus, not from a fixed assumption. A reference model is the usual way to do this — it encodes the DUT's intended behavior generally, so given any input it produces the expected output, and the scoreboard compares. With that, the same env checks every test correctly, because its expectation tracks the test's own stimulus. The way to catch the welded-scoreboard problem is to run multiple different tests through the env and confirm each is checked correctly — one passing test proves nothing about reuse, because a baked-in expectation passes its own test by construction.
An environment is plug-and-play when it can drop into any test, integration level, or project by configuration alone, which requires the test/environment separation plus the general reuse disciplines applied to the env. The test/environment separation is the env-specific one: the env holds reusable structure and tests hold variable scenario, so one env serves all tests. Then the general disciplines, the same as for agents, apply to the env. Self-containment: the env depends only on its DUT and its config object, never on a specific test, outer env, or hierarchy, so it nests and moves freely. Configuration over modification: an env config object carries all per-use settings — agent modes, parameters, sub-env configuration — so you change configuration, never code; this is the config cascade that also handles nesting and projects. Clean boundary: the env exposes a virtual sequencer for tests to run sequences on and analysis access to subscribe to, so the interaction is standard everywhere. Packaging: the env travels with its config, its virtual sequences, and its scoreboards as a unit, so a consumer gets a working verification component, not a bare class that forces them to rebuild the rest. Together these let an environment be adopted by instantiate, configure, connect, and run — none of which is modification. The distinctive piece for environments is the test/environment separation, because envs have the many-tests axis that agents don't; an env can be self-contained, configured, and packaged but still fail the across-tests axis if it bakes in a test's scenario. So plug-and-play for an environment means: separate test from env, depend only on DUT and config, configure don't modify, expose a clean boundary through the virtual sequencer, and ship it whole. With all of those, the env reuses across tests, levels, and projects by configuration and the test's own scenario alone.
Environment reuse is the general picture; layering is one axis of it, and agent reuse is the same disciplines one level down. Layering — nesting an environment as a sub-environment up the design hierarchy — is the across-integration-levels axis of environment reuse. So layering is a special case: it's environment reuse applied vertically, where the consumer of the env is an outer env rather than a test, and the reconfiguration is for the integrated context. Environment reuse generalizes beyond that to the across-tests axis, which is the primary one and is horizontal within a project, and the across-projects axis. Agent reuse is the same set of reuse disciplines — self-containment, configuration over modification, clean boundary, packaging — but applied to the agent, the reusable unit for one interface, whereas environment reuse applies them to the environment, the reusable unit for a DUT. The environment adds one discipline that the agent doesn't emphasize as much: the test/environment separation, because environments have the many-tests axis. An agent's primary reuse is across instances and projects; an environment's primary reuse is across the many tests of one project, so keeping the env test-agnostic is central in a way it isn't for an agent. The configuration mechanisms connect them: the env's config cascades down to the agents (agent configuration), and when envs nest, the cascade extends through the nesting levels (layering). So they form a coherent hierarchy of reuse: agents are reused to build environments, environments are reused across tests and nested to build larger environments, all through the same disciplines of self-containment and configuration over modification, with the test/environment separation as the env-level addition that makes the dominant across-tests reuse work. Understanding one helps understand the others because they're the same principles at different scales.
Exercises
- Name the axes. List the three axes of environment reuse and which is primary, with an example of each.
- Apply the separation. For a given test, state what belongs in the environment and what belongs in the test, and why.
- Fix the welded scoreboard. Given a scoreboard that hardcodes one test's expected result, explain the reuse problem and the fix.
- Make it plug-and-play. List the disciplines that make an environment reusable, and what each prevents.
Summary
- Environment reuse makes an environment, built once, reusable across tests, integration levels, and projects — with across tests the primary, constantly-exercised axis (one env, many tests).
- Its central enabler is the test/environment separation: the env owns the reusable structure (agents, scoreboard, coverage, virtual sequencer, connections); each test owns the variable scenario (config + which virtual sequence) — so one env serves all tests, provided no test-specific logic leaks into the env.
- The general disciplines apply (Module 14.4): self-containment (depends only on its DUT and config), configuration over modification (an env config object), a clean boundary (a virtual sequencer to run on, analysis access), and packaging (ships with its config, sequences, and scoreboards).
- A reusable scoreboard derives expected from observed (a reference model), never hardcoding a test's expected value — so it checks any test correctly.
- The durable rule of thumb: keep the environment test-agnostic — it owns reusable structure (a general checking mechanism that derives expected from observed stimulus), tests own the variable scenario (config + virtual sequence run on the env's vseqr) — and reuse it across tests, levels, and projects by configuration alone; baking a test's expectation or behavior into the env welds it to that test and mis-checks every other, forfeiting the across-tests reuse the environment exists to provide.
Next — Scalable Verification Systems: the module's capstone — composing agents, environments, layering, and reuse into verification systems that scale to a full SoC: the architectural principles, the reuse and configuration strategies, and the patterns that keep a large verification system maintainable, comprehensible, and efficient as the design grows.