A C test can decide whether the DUT behaved correctly, or it can just act and let something else decide. Those are two genuinely different designs, and mixing them up - assuming a test checks itself when it actually doesn't, or vice versa - is a common way for a bug to slip through a passing regression.

7 min read | Intermediate Level

What You'll Learn

  • What "directed" actually describes about a test, and what it doesn't
  • The two different ways a directed test's result can actually get checked
  • What a self-checking test looks like, including how it reports pass or fail
  • Why the strongest tests usually use both kinds of checking at once

What "Directed" Actually Means

A directed test runs a fixed, hand-written sequence of operations - write these registers, trigger this transfer, read that location - with no randomization involved. Every time it runs, it does exactly the same thing.

"Directed" describes the stimulus, not who's responsible for judging whether the result was correct. That second question - who checks it, and how - is a completely separate design decision, and it's the one this article is actually about.

Two Ways a Directed Test's Result Gets Checked

A directed C test's correctness can be judged from either side of the CPU-to-DUT boundary, and the two approaches see very different things.

External checking puts the judgment in the SV environment. The C test just performs the operations; a scoreboard or monitor on the SV side independently observes the bus transactions the DUT actually produced and compares them against an expected result it computed itself. For a DMA transfer, that might mean the SV scoreboard watches the actual write transactions land on the destination bus and compares the bytes against a golden copy - regardless of what the C test believes happened.

Self-checking puts the judgment in the C test itself. The test computes (or already knows) the expected result, compares it against what it actually got, and writes a plain pass/fail verdict somewhere the testbench can read - no SV-side reference model required.

These aren't mutually exclusive. A single directed test can be self-checking from the CPU's point of view while an SV scoreboard is simultaneously checking the same operation from the bus's point of view. Each one can only see - and therefore only catch bugs in - its own side of that boundary.

What a Self-Checking Test Actually Looks Like

The pattern is almost always the same shape: do the operation, compute or look up what the answer should have been, compare, and write one of two fixed values to a status location the environment already knows to watch.


volatile unsigned int * const TEST_STATUS = (unsigned int *) 0x80002000;
#define TEST_PASS 0x1
#define TEST_FAIL 0x0

void _start(void) {
    unsigned int result = run_dma_and_read_checksum();
    unsigned int expected = 0xA53C1F00;

    *TEST_STATUS = (result == expected) ? TEST_PASS : TEST_FAIL;
    while (1) { }
}

The SV side no longer needs to know anything about what a correct checksum looks like - it just polls TEST_STATUS for one of two defined values. All the domain knowledge about correctness lives entirely in the C test.

Reserve a third value - or a separate "done" flag - to distinguish "still running" from "failed." If 0x0 means both "hasn't finished yet" and "failed," a test the environment gives up polling on too early looks identical to one that actually failed, and the two get debugged very differently.

The Trade-off Nobody States Out Loud

Self-checking is fast to write and, because it doesn't depend on anything in the SV environment, the exact same C test can run unmodified on an emulator, an FPGA prototype, or real silicon bring-up. That portability is a real advantage external checking can't offer - a scoreboard only exists in simulation.

But a self-checking test can only ever be as strong as the check its author thought to write. It has no visibility into anything the CPU can't observe by reading memory: it can't tell you whether the DUT's internal bus protocol was followed correctly, whether an unrelated address got corrupted along the way, or whether the timing of the transaction was anywhere close to right. A self-checking test that compares a final checksum will happily report TEST_PASS even if the DMA engine took a completely wrong path to get there, as long as the bytes that end up in memory happen to be correct.

External checking sees exactly the things self-checking is blind to - because it watches the bus directly, not the CPU's summary of what it believes happened. Its cost is that someone has to build and maintain a reference model or scoreboard for every scenario worth checking that way, and none of that checking exists outside simulation.

Self-checking (C test)External checking (SV scoreboard)
SeesOnly what the CPU can read backActual bus transactions and timing
Runs on real siliconYes, unmodifiedNo - simulation only
Cost to buildLow - just C codeHigher - needs a reference model
Catches protocol bugsNoYes

Neither one is strictly better - they catch different classes of bugs, using different data. A test that matters is usually worth checking both ways at once.

Common Mistakes

Mistake: Treating a passing self-check as proof the DUT behaved correctly at the bus level.

A self-check only proves the final, CPU-visible result matched expectations. It says nothing about how the DUT got there, which is exactly the gap an SV scoreboard is meant to close.

Mistake: Overloading a status value to mean two different things.

Using the same value for "not finished yet" and "failed" makes a hung test and a failed test look identical to anything polling for the result, which turns an easy diagnosis into a confusing one.

Mistake: Writing a self-check whose expected value is computed the same buggy way the DUT computes it.

If the "expected" side of the comparison is derived from the same flawed assumption as the DUT's actual behavior, the check passes even though both sides are wrong - the expected value needs to come from an independent source of truth.

Summary

In this article, you learned:

  • "Directed" describes fixed, non-randomized stimulus - it says nothing about who checks the result
  • A directed test's result can be checked externally by an SV scoreboard, internally by the C test itself, or both at once
  • A self-checking test computes its own expected result and writes a plain pass/fail verdict the environment can poll for, with no SV-side reference model required
  • Self-checking is portable to real silicon but blind to anything below the CPU's own view; external checking sees the bus directly but only exists in simulation - the strongest tests use both
Did you find what you were looking for?

Quiz

No quiz questions available for this article.