Electronic – Curious state transitions in state machine RTL simulation

digital-logichdlstate-machinesverilog

I have a simple state machine as part of my Verilog module:

localparam State_IDLE = 3'b000,
           State_WRITETOLANE1 = 3'b001;

reg [2:0] currentState;
reg [2:0] nextState;

always @(posedge TxByteClk) begin
if( rst ) begin
    currentState <= State_IDLE;
end else begin
    currentState <= nextState;
end
end

always @(*) begin
nextState = currentState;
case( currentState )
    State_IDLE: begin
        if( TxSync ) begin
            nextState = State_WRITETOLANE1;
        end
    end
    State_WRITETOLANE1: begin
        nextState = State_IDLE;
    end
endcase
end

TxSync is an input signal. The bizarre behavior I'm seeing is that at the positive edge of the clock when TxSync is high, currentState is set to State_WRITETOLANE1 and as a result nextState is set to State_IDLE. But nextState was never set to State_WRITETOLANE1 in the first place! Why is currentState getting a value that was not even present in nextState? Doesn't the line currentState <= nextState; imply that currentState is the delayed version of nextState?

Best Answer

I went ahead and made a testbench to see the behavior of the circuit. Please right click the image to see it more clearly.

enter image description here

From my simulation, there is nothing unexpected or strange about the waveform. The state gets correctly initialized by the reset. When TxSync is high the state toggles once every clock cycle. When TxSync is deasserted the state holds a constant value.

I also used a lint checker and there are no major problems with the RTL. I must conclude that the circuit is simulating exactly as specified in the RTL. If you were expecting something different, you should make that more clear, and I will tell you how to change the model.

Some notes on the simulation:

  1. The currentState register latches the value of nextState at the rising edge of TxByteClk
  2. The nextState logic is updated after an infinitesimally small unit of time after the rising edge of TxByteClk (or TxSync), since this is a 0 delay model
  3. The currentState and nextState may appear to change simultaneously, but currentState is updated first

Pitfall: If your testbench updates the TxSync input exactly at the rising clock edge, You will just have a glitch in nextState. Your simulator may remove this glitch, making it look like nextState never entered State_WRITETOLANE1, when in fact it did, just for a very brief moment. This would make it look like currentState latched a value that nextState never had.

Remedy: Don't update the inputs exactly at the rising clock edge. Add some small delay so that the simulation can be more clearly understood. In my case, I updated the input at the falling clock edge. But the update time is arbitrary if you are doing a 0 delay simulation.

Related Topic