Transiciones de estado curiosas en simulación RTL de máquina de estado

Tengo una máquina de estado simple como parte de mi módulo Verilog:

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 es una señal de entrada. El comportamiento extraño que estoy viendo es que en el borde positivo del reloj cuando TxSync es alto, currentState se establece en State_WRITETOLANE1 y, como resultado, nextState se establece en State_IDLE. ¡Pero nextState nunca se estableció en State_WRITETOLANE1 en primer lugar! ¿Por qué currentState obtiene un valor que ni siquiera estaba presente en nextState? ¿La línea currentState <= nextState; implica que currentState es la versión retrasada de nextState?

El FSM parece correcto. ¿Podrías editar el código para que sea compilable? Solo necesita las definiciones de parámetros y la declaración del módulo, pero si lo compilo, quiero compilar el mismo código en el que tiene el problema.
¿NextState o currentState están siendo impulsados ​​por algún otro código que no se encuentre en la lista aquí?
Podría ayudar cargar una forma de onda que muestre las señales relevantes.
Agregué los parámetros. currentState y nextState están siendo impulsados ​​por nada más que lo que se muestra aquí. Agregaré una forma de onda mañana cuando tenga acceso, pero no es ambigua, al final, currentState cambia a un valor que nextState nunca ha tenido.
nextState debería haberse establecido en State_WRITETOLANE1 tan pronto como TxSync subió. ¿Tienes un diagrama de tiempo que muestre que esto no sucedió? ¿Qué valor está viendo en nextState antes del borde del reloj?

Respuestas (1)

Seguí adelante e hice un banco de pruebas para ver el comportamiento del circuito. Por favor, haga clic derecho en la imagen para verla más claramente.

ingrese la descripción de la imagen aquí

Según mi simulación, no hay nada inesperado o extraño en la forma de onda. El estado se inicializa correctamente con el reinicio. Cuando TxSync es alto, el estado cambia una vez cada ciclo de reloj. Cuando se desactiva TxSync, el estado mantiene un valor constante.

También usé un verificador de pelusas y no hay mayores problemas con el RTL. Debo concluir que el circuito está simulando exactamente como se especifica en el RTL. Si esperabas algo diferente, deberías dejarlo más claro, y te diré cómo cambiar el modelo.

Algunas notas sobre la simulación:

  1. El registro currentState bloquea el valor de nextState en el flanco ascendente de TxByteClk
  2. La lógica nextState se actualiza después de una unidad de tiempo infinitesimalmente pequeña después del flanco ascendente de TxByteClk (o TxSync), ya que este es un modelo de retraso 0
  3. Puede parecer que currentState y nextState cambian simultáneamente, pero currentState se actualiza primero

Trampa: si su banco de pruebas actualiza la entrada TxSync exactamente en el borde ascendente del reloj, solo tendrá una falla en nextState. Su simulador puede eliminar esta falla, haciendo que parezca que nextState nunca ingresó State_WRITETOLANE1, cuando de hecho lo hizo, solo por un breve momento. Esto haría que pareciera que currentState enganchó un valor que nextState nunca tuvo.

Remedio: No actualice las entradas exactamente en el flanco ascendente del reloj. Agregue un pequeño retraso para que la simulación se pueda entender más claramente. En mi caso, actualicé la entrada en el borde descendente del reloj. Pero el tiempo de actualización es arbitrario si está haciendo una simulación de 0 retrasos.

Esa es de hecho la respuesta. ¡Gracias!. pero ¿por qué el simulador está haciendo esto? ¿Funcionó con máquinas de estado idénticas? ¿No deberían los simuladores reaccionar como si todo sucediera en el borde ascendente y no permitir fallas?
El registro de estado se actualiza solo en el flanco ascendente del reloj. Pero recuerde que no solo está modelando el estado, que es secuencial, sino que también está modelando la lógica de recarga del estado, que es combinatoria. Estos dos componentes son los bloques primero y segundo siempre, respectivamente. Entonces, para la parte secuencial, no importa en qué fase del ciclo del reloj cambie la entrada. Pero la parte combinatoria depende de cuándo cambie la entrada, ya que se actualizará inmediatamente. Si la entrada cambia en el reloj posedge, la parte combinada se actualizará dos veces demasiado rápido para verla en el simulador.