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