Every article so far in this chapter added a new piece to the SoC testbench: reused block-level UVCs, a virtual sequencer, a CPU running real software, formal tools proving structural properties. None of that effort means anything on its own.
Something has to say what each piece is actually responsible for proving, and how you'd know if it hadn't. That's the verification plan - and at SoC level it has a harder job than a block-level test plan: hold together requirements covered by simulation, software, and formal, without losing track of any of them.
10 min read | Intermediate Level
What You'll Learn
- Why SoC-level planning works with less visibility than block-level planning did
- What an actual plan entry looks like, not just the abstract idea of "traceability"
- How to plan a requirement without deciding in advance whether simulation, software, or formal closes it
- What "verification sign-off" actually requires beyond a coverage percentage
From White-Box to Black-Box: Why SoC Planning Is Different
Block-level verification is white-box. You wrote the RTL, or have full access to it, so the plan can name internal states directly: "cover the FIFO going full while a write is pending."
A sub-system built from a few blocks is usually gray-box - part new integration logic you can see into, part pre-verified IP you treat as a black box. By the time you reach the full SoC, planning is almost entirely black-box: you reason about observable behavior at chip-level interfaces and software-visible registers, not internal RTL state.
A requirement written as "internal arbiter state machine reaches state S3" doesn't survive that move - nothing at SoC level is supposed to look at arbiter internals directly. Rewritten for what's observable, it becomes: "a low-priority requester is starved for N cycles while high-priority traffic is continuous, and software reads back the expected data once it finally wins arbitration." Same underlying concern, expressed at the level of visibility the SoC testbench actually has.
This is exactly why block-level agents get reconfigured to passive and the CPU becomes an active stimulus source, as the previous two articles in this chapter covered - the testbench's visibility shrinks to what a real interface exposes, and the plan has to shrink with it.
What a Plan Entry Actually Looks Like
"Traceability" stays abstract until you see an actual entry. A real verification plan is a list of rows like this one, whatever tool or spreadsheet holds them:
ID: VP-DMA-014
Requirement: Every DMA transfer above 4KB switches to burst-mode
on the system bus.
Verified by: Simulation - SV virtual sequence (dma_large_xfer_vseq)
Check: dma_burst_mode_assertion, bound in dma_if
Coverage: dma_burst_cg.cross_burst_size.above_4k_burst_triggered
Priority: P1 - blocks sign-off
Status: Closed - 3/3 bins hit, 0 assertion failures
Every field earns its place. Requirement is what the spec actually promises. Verified by names the specific test, not just "simulation" in general. Check names the exact assertion or scoreboard comparison that would catch a violation. Coverage names the exact bin that proves the scenario was hit, not just that the test ran.
Two more entries, for requirements closed by different engines, so the shape of the pattern is clear:
ID: VP-RST-002
Requirement: A watchdog reset reaches every flip-flop in every
clock domain, in every chip mode.
Verified by: Formal - connectivity tool, reset fan-out check
Check: N/A - proof is exhaustive, not a runtime assertion
Coverage: 100% of reset fan-out paths proven (formal report)
Priority: P1 - blocks sign-off
Status: Closed - proof complete, 0 unreachable paths
ID: VP-DRV-031
Requirement: The DMA driver correctly builds a scatter-gather
descriptor chain for a 3-segment transfer.
Verified by: Software - self-checking C test (test_dma_sg_3seg.c)
Check: Test compares each segment's completion status
against the expected descriptor table
Coverage: sw_test_plan.dma.scatter_gather.3_segment
Priority: P2
Status: Closed - test passed on last 20 regression runs
Write the requirement as a comment directly above the coverage model or check that closes it, matching the plan entry's wording. When someone reads the code six months from now with no memory of the plan, the traceability shouldn't depend on them going and finding the document.
The covergroup behind VP-DMA-014 is ordinary SystemVerilog - the plan entry is what turns it from "a covergroup that exists" into "proof a specific promise was tested":
// VP-DMA-014: "Every DMA transfer above 4KB switches to burst-mode
// on the system bus."
covergroup dma_burst_cg @(posedge clk);
burst_mode_cp: coverpoint dma_if.burst_mode {
bins burst_triggered = {1};
}
size_cp: coverpoint dma_if.xfer_size {
bins above_4k = {[4097:$]};
}
// The cross is the actual evidence: burst mode specifically WHEN
// the transfer was large enough to require it, not independently.
cross_burst_size: cross burst_mode_cp, size_cp;
endgroup
The Plan Doesn't Care Which Engine Closes a Requirement
Look back at the three entries above. Same shape, three different engines: formal, software, simulation. That's deliberate.
A well-written SoC-level requirement doesn't presuppose how it gets covered. Planning means listing what must be true first, then assigning each requirement to whichever engine can actually prove it - not starting from "what can my UVM testbench already do" and shrinking the requirements to fit.
| Requirement | Closed by | Why this engine |
|---|---|---|
| Watchdog reset reaches every flip-flop | Formal | Structural, exhaustive proof beats sampled simulation |
| DMA driver builds a valid scatter-gather chain | Software | Only real driver code proves the programming model works as shipped |
| Low-priority requester eventually wins arbitration | Simulation | Needs constrained-random traffic no directed C test would generate |
A plan is not a wish list.
A requirement with no engine assigned, or assigned to "whichever test happens to hit it," isn't planned. It's hoped for. Every row needs an owner and a concrete piece of evidence.
Sign-Off: How You Know the Plan Is Done
"We hit 95% coverage" is not sign-off. It's one input to sign-off.
A defensible SoC sign-off requires several conditions to hold at once, not any single metric in isolation:
- Coverage closure against the plan - every requirement has either coverage evidence or a documented, approved waiver; no silent gaps.
- Regression passing on frozen RTL - the full sign-off regression passes against a specific, frozen RTL version, not a moving target.
- Zero unresolved critical bugs - blocking and critical-severity issues are fixed, not deferred; lower-severity issues may be waived with sign-off from both design and verification leads.
- Assertions clean across the suite - no assertion failures anywhere in the sign-off regression, since a failing assertion during sign-off means a known-bad state was reached.
Waivers are a normal, expected part of sign-off - not every corner case is worth chasing to closure. What distinguishes a healthy sign-off process is that a waiver is a documented decision with a named owner, not a coverage hole nobody noticed.
Common Mistakes
Mistake: Writing the plan around the testbench instead of the requirements.
Starting from "here's what our SV environment and C tests can already do" and calling that the plan inverts the actual goal. Some real requirements will quietly never get a row, because nothing convenient already covers them.
Mistake: Treating a coverage percentage as sign-off by itself.
A high number with unresolved critical bugs, a non-frozen RTL target, or undocumented waivers isn't sign-off. It's one passing metric next to open risk the number doesn't show.
Mistake: Letting the check and the coverage point drift apart.
A covergroup that gets updated when an interface changes, without updating the assertion or scoreboard check it was meant to accompany, ends up measuring that a scenario occurred without confirming it was handled correctly.
Summary
In this article, you learned:
- SoC-level planning is black-box: requirements have to be expressed in terms of observable, software- and interface-visible behavior, not internal RTL state
- A real plan entry names a specific requirement, check, coverage bin, and owning test - not just "simulation" or "covered" in general
- A well-written requirement doesn't presuppose which engine - simulation, software, or formal - closes it
- Sign-off requires coverage closure, a passing regression on frozen RTL, zero unresolved critical bugs, and a clean assertion record together, not any one of them alone