Debugging a Half-Adder Testbench: A Journey in Four Lessons
When I set out expecting a quick smoke test for my pure combinational half-adder in SystemVerilog, I expected it to be trivial. Instead, I learned more about assertions, build scripts, and wildcard patterns than I ever thought I would. What began as a syntax error turned into a four-step masterclass in testbench design.
Lesson 1: The SVA “Watch” Myth
I was sure I could “watch” my 2-bit input vector directly:
@(ab_combined) …But SystemVerilog properties demand explicit edges (posedge or negedge). The parser’s complaint—“expecting edge or negedge or posedge”—taught me that there’s no built‑in change‑detector syntax in SVA. You either:
Drive checks on a clock edge, or
Use immediate assertions in an
always_combblock
Lesson 2: Combinational Tests vs. Clocked Assertions
My half-adder doesn’t need a clock—but SVA properties do. I discovered two clean patterns:
Immediate assertions
always_comb begin assert(sum === a^b) else $error("…"); assert(carry === a&b) else $error("…"); endNo clock required; checks fire as soon as inputs or outputs change.
Dedicated TB clock
Declareinput logic clk;in the module header and drive it from the C++ harness. That clock only paces the assertions—your DUT remains purely combinational.
Along the way I learned that declaring logic clk; inside the module body doesn’t expose it to Verilator’s C++ driver—only ports become public members.
Lesson 3: Makefile Wildcard Woes
My Makefile used this pattern:
modules/*/tb/half_adder_tb.sv…but my testbench lived at:
modules/arithmetic/half_adder/tb/half_adder_tb.svA missing * cost me countless rebuilds. Changing to:
modules/*/*/tb/half_adder_tb.svinstantly fixed Verilator’s “Cannot find file containing module” error.
Lesson 4: Sequential vs. Combinational Dispatch
The build script classifies any TB containing the string posedge clk as sequential, pulling in sim_main.cpp that tries to toggle a clk port. Without that port, compilation fails. Removing the stray posedge clk (or properly making clk a port) let the combinational rule run with sim_main_minimal.cpp, and suddenly everything passed.
Takeaway:
SVA assertions aren’t magical “watches”—they need edges or immediate checks.
Combinational DUTs can use either
always_combasserts or a separate TB clock.Wildcard patterns in Makefiles must match directory depth exactly.
Build rules can hinge on simple text matches, so stray keywords change behavior.
With these insights, my half-adder now simulates cleanly every time.