This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
testbenches [2020/03/17 08:52] nelson [Writing a Self-Checking Testbench] |
testbenches [2020/03/17 08:58] (current) nelson [Writing a Self-Checking Testbench] |
||
---|---|---|---|
Line 183: | Line 183: | ||
function void checkData(logic expected); | function void checkData(logic expected); | ||
if (expected != q) begin | if (expected != q) begin | ||
- | $display("ERROR %t: %d != %d", $time, expected, q); | + | $display("ERROR at time %t: got a %d but expected a %d", $time, q, expected); |
error_count++; | error_count++; | ||
end | end | ||
Line 210: | Line 210: | ||
Also, the list of inputs and expected values could be stored in an array or read from a file. | Also, the list of inputs and expected values could be stored in an array or read from a file. | ||
- | Then, why require the user to even compute the expected value? Maybe a python script or C program could be written to do that and create the list of inputs and expected output(s) for you. | + | A similar structure could be applied to the construction of a testbench for a sequential circuit. |
/* | /* | ||
Line 216: | Line 216: | ||
*/ | */ | ||
- | ==== Self-Checking Testbenches for Sequential Circuits ==== | ||
- | The above self-checking testbench is pretty straightforward: apply inputs, wait, check outputs. In the final version all 3 of these steps were wrapped into a single task. | ||
- | |||
- | For sequential circuits you often want to separate the wait piece out. So, you will need to a bit of a different organization. A recommendation is to have a function wait for a falling clock edge, check correctness based on what happened at the previous rising edge, and then apply new inputs. Then, the main initial block can advance the clock additional cycles as needed between calls to the function. | ||
- | |||
- | This is shown below. | ||
- | |||
- | module tb(); | ||
- | | ||
- | logic clk, clr; | ||
- | logic[7:0] q; | ||
- | | ||
- | cnt MYCNT(clk, clr, q); | ||
- | | ||
- | initial begin | ||
- | clk = 0; | ||
- | forever clk = #5ns ~clk; | ||
- | end | ||
- | | ||
- | task checkAndApply(logic[7:0] expected, newclr); | ||
- | // Wait for one cycle. If more needed, the calling function should advance clock. | ||
- | @(negedge clk); | ||
- | |||
- | // Check computed values from previous rising clock edge | ||
- | if (expected != q) begin | ||
- | $display("ERROR %t: %d != %d", $time, expected, q); | ||
- | error_count++; | ||
- | end | ||
- | | ||
- | // Apply new signals | ||
- | clr = newclr; | ||
- | |||
- | endfunction | ||
- | | ||
- | task step( | ||
- | initial begin | ||
- | clr = 1; | ||
- | repeat (2) @(negedge clk); // Do this to reset the circuit | ||
- | | ||
- | checkAndApply(0, 0); // Check cycle by cycle | ||
- | checkAndApply(1, 0); | ||
- | checkAndApply(2, 0); | ||
- | |||
- | repeat(13) @(negedge clk); // Wait for 13 clock cycles | ||
- | | ||
- | checkAndApply(16, 1); // Check circuit state now and then reset | ||
- | | ||
- | repeat(4) @(negedge clk); | ||
- | | ||
- | // and so on... | ||
- | | ||
- | end | ||
- | endmodule | ||
==== Using $finish ==== | ==== Using $finish ==== |