The problem with AyncFIFO with two clock sources written in Chisel 5.0

72 views Asked by At

I wrote a AsyncFIFO in Chisel. Since Chisel-tester doesn't support multiple clock source, I wrote another shell to encapsulate the fifo. However, I encountered a problem in testing the empty condition.

My circuit has a decoupled input "read": io.read.ready is provided by the tester, and io.read.valid is the inverted empty signal. If the cache is empty, then io.read.valid is low to prevent more reading. At the same time, io.read.valid and io.read ready are connected to an incrementor to find the correct address: only when both are high, will the incrementor increment.

Now the strange thing is below: The waveform

After io.read.valid falls, the incrementor still increments additional period until it stops. (Which is not expected)

Chisel code of this part is below:

    val (read_addr, read_wrap) = (withClock(io.read_clock)) {
        Counter(0 until depth << 1, enable = (io.read.valid && io.read.ready))
    }

and the compiled Verilog is below:

  assign _full_T = write_addr - read_addr;  // src/main/scala/3_5_object_oriented_progrmming.scala:73:26, src/main/scala/chisel3/util/Counter.scala:61:40

......

  always @(posedge io_read_clock) begin // src/main/scala/3_5_object_oriented_progrmming.scala:30:16
    if (reset)  // src/main/scala/3_5_object_oriented_progrmming.scala:30:16
      read_addr <= 5'h0;    // <stdin>:45:3, src/main/scala/chisel3/util/Counter.scala:61:40
    else if ((|_full_T) & io_read_ready)    // src/main/scala/3_5_object_oriented_progrmming.scala:57:61, :73:{26,39}
      read_addr <= read_addr + 5'h1;    // <stdin>:45:3, src/main/scala/chisel3/util/Counter.scala:61:40, :77:24
  end // always @(posedge)

Update: I had upload the full waveform, code, testbench (Since ChiselTesters doesn't support multiple clock sources at the top, I encapsulate it inside another module so that there is only one clock source), and the compiled Verilog: OneDrive Link

1

There are 1 answers

1
SpaceCowboy max On

According to your sources, this code

always @(posedge io_read_clock) begin
  // ...
  end else if (_T_1) begin 
    read_addr <= _wrap_value_T_3; 
  end
end

It is equivalent to

wire [4:0] _full_T_1 = write_addr - read_addr;
wire  _T_1 = io_read_valid & io_read_ready;
wire  empty = _full_T_1 == 5'h0;
assign io_read_valid = ~empty;

always @(posedge io_read_clock) begin
  // ...
  end else if (((write_addr - read_addr) != 5'h0) & io_read_ready) begin
    read_addr <= read_addr + 5'h1; 
  end
end

Along with the fact that write_addr is clocked not by a io_read_clock, it leads to a race condition in between pointers. You should recheck your design on the topic of what you are trying to synchronise with the Gray encoder.