¿Por qué no deberíamos cambiar las entradas a un circuito secuencial (máquina de Moore) en el borde del reloj?

El código incluido aquí toma un flujo de bits de dígitos binarios, el bit menos significativo (LSB) primero, y genera el complemento a dos del flujo completo, también LSB primero. Se adjunta un diagrama de estado de Moore.

Ahora, cuando trato de probar el código en un banco de pruebas, los estados no se actualizan según lo previsto.

El MEV:

MEV

El diseño:

module seqDetector( input in,
                input clk,
                input rst,
                output reg out);
                
//Moore Machine

parameter   SX = 3'd4,
            S0 = 3'd0,
            S1 = 3'd1,
            S2 = 3'd2,
            S3 = 3'd3;
            
reg [2:0] cur_state,next_state;

//next state assignment
always @(posedge clk,negedge rst) begin
    if( rst == 1'b0)
        cur_state <= SX;
    else
        cur_state <= next_state;
end

//next state calculation
always @(cur_state,in) begin
    case(cur_state)
        SX: if(in == 1'b0) next_state = S0; else next_state = S1;           
        S0: if(in == 1'b0) next_state = S0; else next_state = S1;       
        S1: if(in == 1'b0) next_state = S3; else next_state = S2;       
        S2: if(in == 1'b0) next_state = S3; else next_state = S2;       
        S3: if(in == 1'b0) next_state = S3; else next_state = S2;
    endcase
end

//output calculation
always @(cur_state) begin
    case(cur_state)
        SX: out = 1'bx; 
        S0: out = 1'b0;
        S1: out = 1'b1;
        S2: out = 1'b0;
        S3: out = 1'b1;
    endcase
end

endmodule

El banco de pruebas:

`timescale 1ns/1ns
module tb();
initial begin
    $dumpfile("2's.vcd");
    $dumpvars(0,tb);
end

reg clk;
reg in;
reg rst;
wire out;

initial begin
    clk = 1'b1;
    forever #5 clk = ~clk;
end

seqDetector s0(in,clk,rst,out);

initial begin
    fork
    #0 rst = 1'b1;
    #10 rst =  1'b0;
    #20 rst = 1'b1;
    #10  in = 1'b0;
    #20  in = 1'b1;
    #30  in = 1'b0;
    #40  in = 1'b1;
    #50  in = 1'b1;
    #60  in = 1'b0;
    #70  in = 1'b0;
    #80  in = 1'b1;
    #90  in = 1'b1;
    #100 in = 1'b1;
    #110 in = 1'bx;
    #120 $finish;
    join
end
endmodule

El problema se representa en el siguiente gráfico:

La gráfica

Pero cuando cambiamos el banco de pruebas de modo que las entradas se retrasen 1 ns más allá del borde del reloj, el problema existente se resuelve y se logra la funcionalidad. Pero hay algunas fallas cuyo origen no puedo descifrar, como se muestra aquí:

seqDetector s0(in,clk,rst,out);

initial begin
    fork
    #0 rst = 1'b1;
    #10 rst =  1'b0;
    #20 rst = 1'b1;
    #11  in = 1'b0;
    #21  in = 1'b1;
    #31  in = 1'b0;
    #41  in = 1'b1;
    #51  in = 1'b1;
    #61  in = 1'b0;
    #71  in = 1'b0;
    #81  in = 1'b1;
    #91  in = 1'b1;
    #101 in = 1'b1;
    #111 in = 1'bx;
    #120 $finish;
    join
end

Resuelto

Entonces, la primera pregunta es: ¿por qué hay un problema cuando cambio la entrada en el borde del reloj, desde la perspectiva de escribir un código Verilog?

Y la segunda pregunta es: ¿cuál es la causa de las fallas en la variable next_state?

Creo que es obvio que next_state cambia porque las entradas al cálculo de next_state cambian, ¿no crees?
Sí, da una pista, pero ¿por qué el estado actual no sigue al siguiente estado en el borde del reloj como se muestra en el primer diagrama de tiempo? Consulte el bloque comentado como // próxima asignación de estado.
Cuando 2 entradas cambian a la vez, una de ellas cambia primero. ¿Puedes decir cuál cambia primero? No en realidad no. Así que podría ser el equivocado. Si realmente construyó ese circuito donde esas dos entradas cambiaron al mismo tiempo, sería una violación de tiempo e incluso podría obtener metaestabilidad. (Está bien cambiar dos entradas al mismo tiempo, pero no cuando una de ellas es una señal de reloj)

Respuestas (1)

Investigación

Usando Mentor Questa 2020.1y Cadence Xcelium 20.09no tenemos ningún problema técnico.

Usando Synopsys VCS 2020.03y Icarus Verilog 14obtenemos fallas.

Usé EDA Playground para simular, si está interesado, visite este enlace.

¿Por qué tienes un fallo?

La razón por la que tiene fallas son las condiciones de carrera/ peligros de carrera debido al orden no determinista de las declaraciones de bloqueo de Verilog concurrentes en la simulación.

Como lo arreglas?

Aquí hay 2 técnicas para evitar fallas en la simulación lógica secuencial:

  1. Aplique el estímulo un poco después del borde activo del reloj, usando un retraso, como lo hizo en su pregunta.

  2. Usando asignaciones sin bloqueo, puede controlar las entradas en el borde activo del reloj sin problemas técnicos.

Este banco de pruebas utiliza asignaciones sin bloqueo y simula sin fallas en estos simuladores: Mentor Questa 2020.1, Cadence Xcelium 20.09, Icarus Verilog 14 y Synopsys VCS 2020.03.

module tb();
initial begin
  $dumpfile("dump.vcd");
  $dumpvars(0,tb);
end

reg clk;
reg in;
reg rst;
wire out;

initial begin
    clk = 1'b1;
    forever #5 clk = ~clk;
end

seqDetector s0(in,clk,rst,out);

initial begin
    fork
    #0 rst <= 1'b1;
    #10 rst <=  1'b0;
    #20 rst <= 1'b1;
    #10  in <= 1'b0;
    #20  in <= 1'b1;
    #30  in <= 1'b0;
    #40  in <= 1'b1;
    #50  in <= 1'b1;
    #60  in <= 1'b0;
    #70  in <= 1'b0;
    #80  in <= 1'b1;
    #90  in <= 1'b1;
    #100 in <= 1'b1;
    #110 in <= 1'bx;
    #120 $finish;
    join
end
endmodule

¿Cómo evitar fallos en State Machine Designs?

Registrar las salidas . Para saber más, puedes leer esta respuesta.