I'm attempting to use Cooctb to verify a simple Verilog counter with a reset:
`default_nettype none
`timescale 1ns / 1ps
module counter (
input wire clk,
input wire rst,
output reg [7:0] count
);
always @(posedge clk) begin
if (rst) begin
count <= 0;
end else begin
count <= count + 1;
end
end
endmodule
When I first wrote a test that verifies count goes back to 0 when rst is asserted, I used RisingEdge to set and assert values. However, I found that I had to wait for two rising edges for count to go back to 0:
await RisingEdge(dut.clk)
assert dut.count.value.integer == 3
dut.rst.value = 1
await RisingEdge(dut.clk)
# count == 4 here
await RisingEdge(dut.clk) # Why is this needed?
assert dut.count.value.integer == 0
Here's the signal trace for this RisingEdge test:
Even though it looks like rst is 1 at the 6ns rising edge, it must be getting set just after that edge, so it doesn't take effect until the 7ns rising edge?
If I use FallingEdge, then count goes back to 0 on the next edge, as I expect:
await FallingEdge(dut.clk)
assert dut.count.value.integer == 3
dut.rst.value = 1
await FallingEdge(dut.clk)
assert dut.count.value.integer == 0
Here's the signal trace for the FallingEdge test:
While this works, it seems a little odd to be using FallingEdge for all the test code, given the logic is synchronous to the rising edge. Is this how folks typically write Cocotb tests?
Here is the full Python code:
import cocotb
from cocotb.clock import Clock
from cocotb.triggers import RisingEdge, FallingEdge
@cocotb.test()
async def count_then_reset_test_rising(dut):
# Reset
dut.rst.value = 0
cocotb.start_soon(Clock(dut.clk, 1, units="ns").start())
await RisingEdge(dut.clk)
await RisingEdge(dut.clk)
dut.rst.value = 1
await RisingEdge(dut.clk)
dut.rst.value = 0
await RisingEdge(dut.clk)
# Running
assert dut.count.value.integer == 0
await RisingEdge(dut.clk)
assert dut.count.value.integer == 1
await RisingEdge(dut.clk)
assert dut.count.value.integer == 2
await RisingEdge(dut.clk)
assert dut.count.value.integer == 3
dut.rst.value = 1
await RisingEdge(dut.clk)
# count == 4 here
await RisingEdge(dut.clk) # Why is this needed?
assert dut.count.value.integer == 0
dut.rst.value = 0
await RisingEdge(dut.clk)
assert dut.count.value.integer == 0
await RisingEdge(dut.clk)
assert dut.count.value.integer == 1
@cocotb.test()
async def count_then_reset_test_falling(dut):
# Reset
dut.rst.value = 0
cocotb.start_soon(Clock(dut.clk, 1, units="ns").start())
await FallingEdge(dut.clk)
await FallingEdge(dut.clk)
dut.rst.value = 1
await FallingEdge(dut.clk)
dut.rst.value = 0
# Running
assert dut.count.value.integer == 0
await FallingEdge(dut.clk)
assert dut.count.value.integer == 1
await FallingEdge(dut.clk)
assert dut.count.value.integer == 2
await FallingEdge(dut.clk)
assert dut.count.value.integer == 3
dut.rst.value = 1
await FallingEdge(dut.clk)
assert dut.count.value.integer == 0
dut.rst.value = 0
await FallingEdge(dut.clk)
assert dut.count.value.integer == 1
await FallingEdge(dut.clk)
assert dut.count.value.integer == 2
await FallingEdge(dut.clk)
assert dut.count.value.integer == 3

