Search code examples
hdlchiselicaruscocotb

RisingEdge example doesn't work for module input signal in Chisel3


In Chisel documentation we have an example of rising edge detection method defined as following :

      def risingedge(x: Bool) = x && !RegNext(x)

All example code is available on my github project blp.

If I use it on an Input signal declared as following :

class RisingEdge extends Module {
  val io = IO(new Bundle{
    val sclk = Input(Bool())
    val redge = Output(Bool())
    val fedge = Output(Bool())
  })

  // seems to not work with icarus + cocotb
  def risingedge(x: Bool) = x && !RegNext(x)
  def fallingedge(x: Bool) = !x && RegNext(x)
  // works with icarus + cocotb
  //def risingedge(x: Bool) = x && !RegNext(RegNext(x))
  //def fallingedge(x: Bool) = !x && RegNext(RegNext(x))

  io.redge :=  risingedge(io.sclk)
  io.fedge := fallingedge(io.sclk)
}

With this icarus/cocotb testbench :

class RisingEdge(object):
    def __init__(self, dut, clock):
        self._dut = dut
        self._clock_thread = cocotb.fork(clock.start())

    @cocotb.coroutine
    def reset(self):
        short_per = Timer(100, units="ns")
        self._dut.reset <= 1
        self._dut.io_sclk <= 0
        yield short_per
        self._dut.reset <= 0
        yield short_per

@cocotb.test()
def test_rising_edge(dut):
    dut._log.info("Launching RisingEdge test")
    redge = RisingEdge(dut, Clock(dut.clock, 1, "ns")) 
    yield redge.reset()
    cwait = Timer(10, "ns")
    for i in range(100):
        dut.io_sclk <= 1
        yield cwait
        dut.io_sclk <= 0
        yield cwait

I will never get rising pulses on io.redge and io.fedge. To get the pulse I have to change the definition of risingedge as following :

  def risingedge(x: Bool) = x && !RegNext(RegNext(x))

With dual RegNext() : Dual RegNext screenshot

With simple RegNext() : Simple RegNext screenshot

Is it a normal behavior ?

[Edit: I modified source example with the github example given above]


Solution

  • Do not change module input value on rising edge of clock.

    Ok I found my bug. In the cocotb testbench I toggled input values on the same edge of synchronous clock. If we do that, the input is modified exactly under the setup time of D-Latch, then the behavior is undefined !

    Then, the problem was a cocotb testbench bug and not Chisel bug. To solve it we just have to change the clock edge for toggling values like it :

    @cocotb.test()
    def test_rising_edge(dut):
        dut._log.info("Launching RisingEdge test")
        redge = RisingEdge(dut, Clock(dut.clock, 1, "ns")) 
        yield redge.reset()
        cwait = Timer(4, "ns")
        yield FallingEdge(dut.clock) #   <--- 'synchronize' on falling edge
        for i in range(5):
            dut.io_sclk <= 1
            yield cwait
            dut.io_sclk <= 0
            yield cwait