Video 3 of 4 · ~9 minutes
Dr. Mike Borowczak · Electrical & Computer Engineering · CECS · UCF
Production testbenches at Apple, Qualcomm, or AMD contain hundreds of test cases. Every one is one-line call to a task with meaningful parameters. The top-level initial block reads like a test plan. When you look at a senior engineer's testbench, the test cases are scannable: you see the intent, not the boilerplate.
Today's pattern applies to every testbench you write from here forward. Video 4 combines tasks with file-driven test vectors — the combination that enables the thousand-case testbenches in Day 11 UART validation.
“task and function are the same thing. I'll use whichever.”
A function returns a value in zero time — no delays, no clock waits. A task can consume simulation time: #delay, @(posedge clk), wait(cond). Testbenches need both stimulus (takes time) and checks (zero time), so tasks are the right tool for test cases that combine them.
#10 inside a function — simulator rejects it. Try to drive stimulus with a function — same. Tasks are what the spec designed for this job.
initial begin
// Test case 1: 5 + 3
a = 4'd5; b = 4'd3;
#10;
if (sum !== 5'd8) begin tests_failed=tests_failed+1; $display("FAIL: 5+3"); end
else $display("PASS: 5+3 = %d", sum);
tests_run = tests_run + 1;
// Test case 2: 7 + 9
a = 4'd7; b = 4'd9;
#10;
if (sum !== 5'd16) begin tests_failed=tests_failed+1; $display("FAIL: 7+9"); end
else $display("PASS: 7+9 = %d", sum);
tests_run = tests_run + 1;
// Test case 3: 15 + 15 ...
// 20 more cases, 5 lines each = 100 lines of almost-identical code
end
task test_add;
input [3:0] a_in, b_in;
input [4:0] expected;
begin
a = a_in;
b = b_in;
#10; // wait for combinational settle
check_eq(sum, expected, $sformatf("%d + %d", a_in, b_in));
end
endtask
initial begin
test_add(4'd5, 4'd3, 5'd8);
test_add(4'd7, 4'd9, 5'd16);
test_add(4'd15, 4'd15, 5'd30);
test_add(4'd0, 4'd0, 5'd0);
test_add(4'd8, 4'd8, 5'd16);
$display("=== %0d passed, %0d failed ===", tests_run - tests_failed, tests_failed);
end
initial block now reads like a test plan — each case's intent visible at a glance.
// For a clocked DUT: drive inputs at posedge, sample after
task apply_and_check;
input [7:0] d_in;
input en_in;
input [7:0] q_expected;
begin
@(posedge clk);
d = d_in;
en = en_in;
@(posedge clk); // wait for next edge — DUT captures
#1; // slight delay to sample after edge
check_eq(q, q_expected, $sformatf("d=%h en=%b", d_in, en_in));
end
endtask
initial begin
rst = 1; #10; rst = 0;
en = 1; d = 8'hAA; @(posedge clk); #1;
if (q !== 8'hAA) $display("FAIL A"); else $display("PASS A");
en = 1; d = 8'h55; @(posedge clk); #1;
if (q !== 8'h55) $display("FAIL B"); else $display("PASS B");
en = 0; d = 8'hFF; @(posedge clk); #1;
if (q !== 8'h55) $display("FAIL C"); else $display("PASS C"); // hold
end
Define apply_and_check and rewrite the three cases as one-liners.
reset_dut();
apply_and_check(1, 8'hAA, 8'hAA); // load
apply_and_check(1, 8'h55, 8'h55); // update
apply_and_check(0, 8'hFF, 8'h55); // en=0: hold
~5 minutes — before/after
▸ COMMANDS
cd labs/week2_day06/ex3_tasks/
wc -l tb_before.v tb_after.v
diff tb_before.v tb_after.v | head -40
make sim # both produce identical results
▸ EXPECTED STDOUT
120 tb_before.v
45 tb_after.v
PASS: 5+3 = 8
PASS: 7+9 = 16
...
=== 20 passed, 0 failed ===
▸ KEY OBSERVATION
Same output. 63% fewer lines of code. More importantly: if a new check is needed, it's one edit in the task, not 20 edits across cases.
Ask AI: “Refactor this 40-line testbench using a task so each test case becomes a single line. Also use $sformatf for the labels.”
TASK
Ask AI to refactor to task-based.
BEFORE
Predict: one task with inputs + one-line cases in initial.
AFTER
Strong AI uses $sformatf for labels. Weak AI concatenates strings manually.
TAKEAWAY
AI is reliable for refactoring; easy productivity boost for any test-heavy file.
① Tasks can consume time; functions cannot.
② Encapsulate stimulus + check in one task per pattern.
③ One-line test cases make the initial block a test plan.
④ Use $sformatf for dynamic test labels.
🔗 Transfer
Video 4 of 4 · ~10 minutes
▸ WHY THIS MATTERS NEXT
Tasks handle repetition of structure. But what if you have thousands of test vectors? Video 4 introduces $readmemh — loading test vectors from external files so your testbench can run 100,000 cases without a 100,000-line initial block.