Directed Testing Vs Constraint Random Verification

In short: Directed testing writes one test per scenario you already know about. Constrained random verification (CRV) lets the simulator generate many legal, varied scenarios from a small set of rules, so it also finds bugs nobody thought to look for. Most real projects use both: CRV first to cover wide ground and catch corner cases, then a few directed tests to close the last gaps that random did not reach. This guide on directed testing vs constraint random verification shows when each one wins, with worked SystemVerilog examples.

Every verification plan has to answer one question: how do you know the design was tested enough? Two styles answer it in very different ways. Directed testing spells out each scenario by hand. Constrained random verification describes the rules of legal stimulus and lets the tool explore. Knowing when to reach for each is one of the most useful skills in SystemVerilog verification.

A simple way to picture it

Think about learning to drive. Directed testing is like a driving instructor giving you a fixed checklist: park here, turn left there, stop at this sign. You practise exactly those moves. It is reliable, but you only ever rehearse the situations the instructor imagined. Constrained random verification is like driving around a real city for a hundred hours inside sensible rules (stay on the road, obey signals, do not speed). You still keep the rules, but you meet junctions, weather, and traffic patterns the instructor never listed. That is how random stimulus surprises you with the odd corner case that a fixed checklist would miss.

What directed testing is

In directed testing, the engineer reads the specification, lists the scenarios that matter, and writes one test that drives exactly those stimuli and checks exactly those results. The test is explicit from start to finish. If the spec says a write to a full FIFO must be dropped and set the overflow flag, you write a test that fills the FIFO, issues one more write, and checks the flag.

// Directed test: fill a FIFO, then write once more and check overflow
task automatic test_fifo_overflow();
  // FIFO depth is 8; drive 8 legal writes
  for (int i = 0; i < 8; i++)
    drive_write(.data(i), .expect_accept(1));

  // 9th write must be rejected and raise the overflow flag
  drive_write(.data(99), .expect_accept(0));

  if (dut.overflow !== 1'b1)
    $error("Overflow flag did not assert on full FIFO");
  else
    $display("PASS: overflow behaved as specified");
endtask

The strength is clarity. Anyone can read the test and see the intent. The weakness shows up on large designs: you can only test what you thought of, and writing one test per scenario does not scale to millions of possible input combinations.

What constrained random verification is

Constrained random verification flips the effort. Instead of listing scenarios, you describe the shape of legal stimulus with constraints, and the solver produces many different legal transactions across a run. You pair this with functional coverage so you can measure which scenarios were actually hit, and with a scoreboard that checks results automatically for every generated case.

// A constrained-random transaction for the same FIFO interface
class fifo_txn extends uvm_sequence_item;
  rand bit        write_en;
  rand bit [7:0]  data;
  rand int        gap_cycles;   // idle cycles before this access

  `uvm_object_utils(fifo_txn)

  // Legal-but-varied stimulus rules
  constraint c_gap  { gap_cycles inside {[0:6]}; }
  constraint c_bias { write_en dist {1 := 70, 0 := 30}; } // lean toward writes

  function new(string name = "fifo_txn");
    super.new(name);
  endfunction
endclass

One short sequence of these transactions, run across many seeds, naturally creates back-to-back writes, bursts with gaps, and the exact full-then-write pattern that the directed test checked by hand, plus thousands of patterns you never listed. New or revised constraints then steer the run toward whatever functional coverage still shows uncovered.

Directed vs constrained random: side by side

AspectDirected testingConstrained random verification
Who picks the scenarioThe engineer, by handThe constraint solver, within your rules
Effort to add coverageOne new test per scenarioAdjust constraints, rerun with new seeds
Finds unexpected bugsRarely, only planned casesOften, explores unplanned combinations
Scales to big designsPoorlyWell
Debug of a failureVery easy, intent is explicitNeeds a good scoreboard and logging
Best first useClosing a specific known gapBroad coverage from the start

The practical workflow: use both

On real projects these two are not rivals. The winning order is random first, directed last:

  • Start with constrained random. Let it run across many seeds to sweep wide functional ground and shake out corner-case bugs early.
  • Analyse coverage. After regression, read functional and code functional coverage reports to see which specific scenarios random never reached.
  • Steer with constraints. Tighten or add constraints to push random toward the uncovered holes before writing any hand test.
  • Finish with directed tests. For the last few stubborn holes that are hard to reach randomly (a rare reset sequence, a fixed address corner), write a small number of directed tests aimed exactly at them.
  • Lock in good seeds. Keep the seeds that produced the best coverage as regression tests so those scenarios repeat every run.

Common mistakes to avoid

  • Writing hundreds of directed tests first. This burns schedule and still misses unplanned corners. Reach for CRV to cover the wide middle.
  • Running random with no coverage model. Without functional coverage, you cannot tell whether all those cycles hit anything new or just repeated easy cases.
  • Over-constraining. Constraints that are too tight quietly block legal scenarios, so random never explores them. Review constraints against the spec.
  • Weak checking. Random stimulus is only useful if a scoreboard or set of assertions checks every result automatically. Eyeballing waveforms does not scale.
  • Not saving passing seeds. A great run you cannot reproduce is a run you did not really get credit for.

Expected output, in plain words

For the directed FIFO test, after eight accepted writes the ninth is rejected and the overflow flag reads 1, so you see the PASS message. For the constrained-random class, each randomize() call produces a legal transaction where write_en is a write about seventy percent of the time and gap_cycles falls between 0 and 6; across many seeds the stream covers bursts, gaps, and the full-FIFO corner. Note: these describe the intended behaviour from a read of the code and the IEEE 1800 rules, not captured runs from a specific simulator, and randomized values change with the seed. Confirm on your own simulator or on EDA Playground.

Once you are comfortable with both styles, the natural next steps are strengthening your checks with assertions, measuring progress with functional coverage, and practising the trade-offs through interview questions.

Frequently asked questions

What is the main difference between directed testing and constrained random verification?

Directed testing writes one explicit test per known scenario, so it only checks cases the engineer imagined. Constrained random verification describes the rules of legal stimulus and lets the solver generate many varied cases, so it also reaches scenarios nobody planned. Directed testing is precise but does not scale; CRV covers wide ground and finds corner-case bugs.

Is constrained random verification better than directed testing?

Neither is strictly better; they solve different problems. CRV is the better starting point for broad coverage on complex designs, while directed testing is better for closing a specific known gap that random rarely reaches. Most projects run CRV first, then add a few directed tests for the last holes.

When should I use directed tests in a random environment?

Use them after several regression cycles of constrained random, once coverage reports show which scenarios were never hit. Write small directed tests aimed exactly at those stubborn holes, such as a rare reset order or a fixed address boundary, rather than trying to force random to reach them.

Do I still need functional coverage with constrained random verification?

Yes. Without functional coverage you cannot tell whether millions of random cycles hit new scenarios or just repeated easy ones. Coverage tells you what was actually exercised and guides which constraints to change next.

Why can constrained random find bugs that directed tests miss?

Because the solver combines legal inputs in ways the engineer never listed, such as an unusual gap before a full-FIFO write. Directed tests only exercise planned sequences, so unplanned but legal combinations, which is where many silicon bugs hide, go untested.

Can I reproduce a random failure for debugging?

Yes, if you record the seed. Rerunning with the same seed and the same testbench reproduces the exact stimulus, so you can debug it like a directed test. This is why teams save the seeds that produced good coverage or failures.

Similar Posts