Every module you build in SystemVerilog needs something to drive its inputs and check its outputs. That something is a testbench. A testbench is itself a SystemVerilog module, but it has no ports. It exists only in simulation and is never synthesised into hardware. Its entire job is to create the environment that your design under test, the DUT, would normally operate in.
On previous pages you wrote modules that describe hardware. This page flips the perspective. You are now the engineer sitting outside the chip, deciding what inputs to apply and what outputs to observe. That mental shift is one of the most important in verification.
To test a module, you first declare signals in the testbench to connect to the DUT ports, then instantiate the DUT by name. Named port connections, written as .port_name(signal_name), are the professional style because they survive port order changes without silent bugs.
Inside an initial block you drive inputs by assignment. Stimulus is procedural: assignments happen one after another in time order. This is different from the continuous assignment you used in your design modules. The initial block runs exactly once from simulation time zero and is the place to set up your scenario, wait for responses, and observe results.
One caution: a combinational gate like the and_gate you will test does not update its output at time zero. You assign inputs at time zero, but the gate event is scheduled and settles a moment later. Adding a small time delay with #1; advances the simulator by one time unit so propagation completes before you read the output. Skip that delay and you will see an unknown value on the output even though your inputs look correct.
SystemVerilog provides a family of system tasks for observing simulation. $display is the workhorse: it prints a formatted string to the simulation log, much like printf in C. The format string uses %b for binary, %d for decimal, and %h for hexadecimal. A call like $display("y = %b", y); prints the current binary value of y with a label, which is exactly the output the grader is looking for.
$finish tells the simulator the test is done and it should exit cleanly. Without it, an event driven simulator keeps running, waiting for something that never arrives. In a professional environment a runaway simulation wastes compute hours. The habit of finishing explicitly is one worth building from your very first testbench.
Verilator, the engine behind this lab, compiles SystemVerilog to C++ and runs it natively. That means it enforces strict two-file conventions: it expects the DUT module and the testbench module to both be present in the compilation. The grader handles that automatically here, concatenating the provided design with your testbench before compiling.
What matters to you as the learner is that Verilator output is exactly what appears in the engine panel. The line y = 1 must be present in stdout for the grader to mark you as passing. No label, no pass. Wrong label, no pass. This precision mirrors a real CI regression: the check script looks for an exact string, and everything else is a failure. Write the display statement to produce that exact string.
The and_gate module is provided below and is already included in the grader compilation. Your job is to complete the testbench: instantiate and_gate, drive a=1 and b=1, wait one time unit so the gate propagates, print y using $display("y = %b", y), then call $finish. The grader matches stdout against the string "y = 1".
Weekly intelligence on VLSI, AI for EDA, and chip careers. 20,000+ engineers already inside.