0
\$\begingroup\$

I am trying to implement a non-synthesizable dual-clock FIFO in Verilog (solely for testbench purposes). Since the FIFO has to operate correctly even when both clocks toggle precisely at the same moment, I want to arrange its logic into the single always block with both clocks in its sensitivity list. Remembering that VHDL has 'event syntax, I wrote something like this:

integer memoryOccupiedBits = 0;

always @(resetAsync, posedge readClock, posedge writeClock) begin
    if (resetAsync) begin
        ...
        memoryOccupiedBits = 0;
        ...
    end else begin
        if ((posedge readClock) && readClockEnable) begin
            if (readEnable) begin
                ...
                memoryOccupiedBits = memoryOccupiedBits - ReadPortWidth;
                `Assert(memoryOccupiedBits >= 0);
                ...
            end
        end

        if ((posedge writeClock) && writeClockEnable) begin
            if (writeEnable) begin
                ...
                memoryOccupiedBits = memoryOccupiedBits + WritePortWidth;
                `Assert(memoryOccupiedBits <= TotalDepthBits);
                ...
            end
        end
    end
end

However, this usage of posedge clock is not allowed in Verilog (at least my simulator says it is a syntax error). Is there any language construct to achieve the result of VHDL's 'event? Or some other way to rewrite the above code without sacrificing its clarity and structure?

UPD:
I added some more details to my code snippet to clarify the intent. Please note the blocking assignments to the memoryOccupiedBits signal.

\$\endgroup\$

3 Answers 3

2
\$\begingroup\$

Since this does not need to be synthesizable, you could separate this logic into separate always blocks

always @(resetAsync)
    if (resetAsync) begin
        ...
    end
always @(posedge readClock iff !resetAsync && readClockEnable)
    if (readEnable) begin
                ...
    end
always @(posedge writeClock iff !resetAsync && writeClockEnable)
    if (writeEnable) begin
                ...
    end

Updated

Based on the new information provided, you can use nonblocking event triggers and the triggered() method to see if one or both clocks were triggered.

  event r_ev,w_ev
  always @(posedge readClock) ->>r_ev;
  always @(posedge writeClock) ->>w_ev;
  
  always @(r_ev or w_ev) 
    if (r_ev.triggered && w_ev.triggered)
      $display($time,,"both r_ev and w_ev");
    else if (r_ev.triggered)
      $display($time,,"r_ev triggered");
    else if (w_ev.triggered)
      $display($time,,"w_ev triggered");
    else
      $display($time,,"unreachable");
```
\$\endgroup\$
2
  • \$\begingroup\$ Unfortunately, if you separate the logic into different always blocks, you cannot reason about the order they are to be executed in if both clocks triggered at the same moment. \$\endgroup\$ Commented Jul 27, 2023 at 9:50
  • \$\begingroup\$ You added new requirements to your original question. In discrete event simulation, no two events ever happen at the same moment. You can only know that the two clocks triggered when the scheduled event region is over. \$\endgroup\$
    – dave_59
    Commented Jul 27, 2023 at 19:15
0
\$\begingroup\$

posedge is legal in the sensitivity list @(...), but it is illegal inside the body of the always block. This code compiles without syntax errors:

always @(resetAsync, posedge readClock, posedge writeClock) begin
    if (resetAsync) begin
    end else begin
        if (readClock && readClockEnable) begin
            if (readEnable) begin
            end
        end

        if (writeClock && writeClockEnable) begin
            if (writeEnable) begin
            end
        end
    end
end
\$\endgroup\$
1
  • \$\begingroup\$ Unfortunately, that code does a different thing (actually, I wrote my code like this at first). Imagine one clock being exactly twice as fast as the other. In this case one of the ifs will false-positively execute at the middle of its clock cycle. \$\endgroup\$ Commented Jul 24, 2023 at 14:02
0
\$\begingroup\$

It seems I have found a workaround, though it does not look very clean or elegant. The point is to save clock values on each always block invocation and manually detect clock edges:

reg readClockPrev = 1'b0;
reg writeClockPrev = 1'b0;

wire readClockPosedge = ({readClockPrev, readClock} === 2'b01);
wire writeClockPosedge = ({writeClockPrev, writeClock} === 2'b01);

always @(resetAsync, readClock, writeClock) begin
    if (resetAsync) begin
        ...
    end else begin
        if (readClockPosedge && readClockEnable) begin
            if (readEnable) begin
                ...
            end
        end

        if (writeClockPosedge && writeClockEnable) begin
            if (writeEnable) begin
                ...
            end
        end
    end

    readClockPrev = readClock;
    writeClockPrev = writeClock;
end
```
\$\endgroup\$
1
  • \$\begingroup\$ There is no guarantee the always block executes only once if both clocks have their posedge in the same timestep. \$\endgroup\$
    – dave_59
    Commented Jul 27, 2023 at 19:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.