transferencia de estado en FSM, escrito en verilog

0

El código:

module demux(   input clk, rst_n, NewPacket,
    input [7:0] DataIn,
    output reg[7:0] Dataout0,Dataout1,Dataout2,Dataout3,Dataout4,Dataout5,Dataout6,Dataout7);

reg[1:0] state, state_n;
reg[7:0] Data_temp1;
reg[3:0] packet_size;
reg[2:0] outport;

parameter   S0 = 2'd0,
            S1 = 2'd1,
            S2 = 2'd2;

always @(posedge clk)
if(!rst_n)
    state <= S0;
else begin
    state <= state_n;
    Data_temp1 <= DataIn;
    if(packet_size > 0)begin
        packet_size <= packet_size - 1;
        outport <= outport + 1;
    end
end

always @(state, NewPacket, packet_size) begin
state_n = 2'bx;
case(state)
    S0: if(NewPacket)
            state_n = S1;
        else
            state_n = S0;
    S1: begin
            packet_size = Data_temp1[3:0];
            outport = Data_temp1[6:4] - 1;
            state_n = S2;
        end
    S2: if(packet_size > 0)
            state_n = S2;
        else
            state_n = S0;
endcase
end

always @(posedge clk)
if(!rst_n)
    {Dataout0,Dataout1,Dataout2,Dataout3,Dataout4,Dataout5,Dataout6,Dataout7} <= 64'b0;
else begin
    {Dataout0,Dataout1,Dataout2,Dataout3,Dataout4,Dataout5,Dataout6,Dataout7} <= 64'b0;
    if(state == S2)
        case(outport)
        0:  Dataout0 <= Data_temp1;
        1:  Dataout1 <= Data_temp1;
        2:  Dataout2 <= Data_temp1;
        3:  Dataout3 <= Data_temp1;
        4:  Dataout4 <= Data_temp1;
        5:  Dataout5 <= Data_temp1;
        6:  Dataout6 <= Data_temp1;
        7:  Dataout7 <= Data_temp1;
        endcase
    end
endmodule

El banco de pruebas:

    'timescale 1ns / 1ps

module demux_tb;

// Inputs
reg clk;
reg rst_n;
reg NewPacket;
reg [7:0] DataIn;

// Outputs
wire [7:0] Dataout0;
wire [7:0] Dataout1;
wire [7:0] Dataout2;
wire [7:0] Dataout3;
wire [7:0] Dataout4;
wire [7:0] Dataout5;
wire [7:0] Dataout6;
wire [7:0] Dataout7;

// Instantiate the Unit Under Test (UUT)
demux uut (
    .clk(clk), 
    .rst_n(rst_n), 
    .NewPacket(NewPacket), 
    .DataIn(DataIn), 
    .Dataout0(Dataout0), 
    .Dataout1(Dataout1), 
    .Dataout2(Dataout2), 
    .Dataout3(Dataout3), 
    .Dataout4(Dataout4), 
    .Dataout5(Dataout5), 
    .Dataout6(Dataout6), 
    .Dataout7(Dataout7)
);

initial
    forever #10 clk <= ~clk;

initial
begin
    clk = 1;
    rst_n = 1;
    NewPacket = 0;
    DataIn = 0;

    #20 rst_n = 0;
        #60 rst_n = 1;

    #20 DataIn = 8'h73;
    NewPacket = 1;

    #20 DataIn = 8'h11;
    NewPacket = 0;

    #20 DataIn = 8'h22;

    #20 DataIn = 8'h33;

    #20 DataIn = 8'h44;

    #20 DataIn = 8'b0;

    #20 DataIn = 8'h02;
    NewPacket = 1;

    #20 DataIn = 8'h88;
    NewPacket = 0;

    #20 DataIn = 8'h99; 
end

endmodule

La forma de onda:

En mi opinión, state_n debería cambiar a 1 primero y el siguiente clk state cambiar a 1 y state_n cambiar a 2. Pero en forma de onda, state_n cambiar a 2 desde 0 directamente, ¿Por qué?

    
pregunta fgg1991

2 respuestas

2

Hay una condición de carrera en el banco de pruebas. NewPacket se actualiza y desencadena una actualización a state_n antes de que se actualice el reloj. Todo esto sucede en el mismo paso de tiempo. Para ayudar a visualizar esta carrera, cambia la hora del primer #20 a #19 o #21 .

El banco de pruebas debe usar asignaciones no bloqueantes para todas las señales que van al uut ; excepto clk . Los relojes deben tener asignaciones de bloqueo.

initial
    forever #10 clk = ~clk; // Blocking

initial
begin
    // blocking is okay here for initialization
    clk = 1;
    rst_n = 1;
    NewPacket = 0;
    DataIn = 0;

    // From here on, use non-blocking assignments to signals going to uut
    #20 rst_n <= 0;
    #60 rst_n <= 1;

    #20 DataIn <= 8'h73;
    NewPacket <= 1;

    ...

    #20 $finish; // tell the simulator to end
end

Ejemplo de trabajo aquí

Sugerencias para mejorar demux :

  • Data_temp1 , packet_size y outport deberían tener un valor de restablecimiento en el bloque if(!rst_n) .
  • packet_size y outport también se asignan en dos bloques siempre, lo que es ilegal para la síntesis. Para el código sintetizable, una variable solo debe asignarse en un solo bloque always . Creando dos nuevas variables para calcular los siguientes valores; como la relación state_n - state . FYI: *_n es a menudo un estilo de codificación para bajo activo. *_next , next_* o *_ns son prefijos / sufijos más comunes para los próximos cálculos.
  • Se recomienda usar @* o @(*) para bloques lógicos combinacionales en lugar de especificar señales en la lista de sensibilidad. Por lo tanto, reemplaza @(state, NewPacket, packet_size) con @*
respondido por el Greg
0

He estado jugando con TL-Verilog ( enlace ), y estaba buscando un ejemplo para codificar. Casualmente agarré este y corrí con él hoy. Los resultados se ven muy bien. Pensé que lo compartiría.

Hay una curva de aprendizaje con sintaxis de TL-Verilog, pero todos los problemas con bloqueo / no bloqueo, listas de sensibilidad y similares que fueron errores en esta publicación han desaparecido. Así que mi respuesta a tu publicación es "usar TL-Verilog".

Lo codifiqué aquí: enlace . Debería poder compilarlo y simularlo usted mismo, jugar con él y depurar con formas de onda. ¡Creo que es una locura que haya llegado a ser la mitad del tamaño!

Aquí está mi versión del código "demux", traducido del código anterior:

    \TLV_version 0.1: http://TBD
    \SV
    module demux_TLV(   input clk, rst_n, NewPacket,
        input [7:0] DataIn,
        output reg[7:0] Dataout[7:0]);

    \TLV
       |pipe
          @0
             // Inputs
    !        $new_packet = *NewPacket;
    !        $data_in[7:0] = *DataIn;

          @1
             // Logic
             $done_packet = $packet_size == 0;
    !        {$outport[2:0], $packet_size[3:0]} =
                !*rst_n          ? '0 :
                !$done_packet#+1 ? {$outport#+1 + 3'b1,   $packet_size#+1 - 4'b1} :
                $new_packet      ? {$data_in[6:4] - 3'b1, $data_in[3:0]} :
                                   {$outport#+1,          $packet_size#+1};

          @2
             // Output
             >port[7:0]
    !           $data_out[7:0] = (*rst_n &&
                                  !|pipe$done_packet#+1 &&
                                  (port == |pipe$outport)) ? |pipe$data_in
                                                           : '0;
                *Dataout[port] = $data_out;

    \SV

    endmodule

No es una traducción directa. Más de una reescritura lógica.

El código de EDA Playground tiene el original y mi versión, ambos instanciados desde el banco de pruebas. Producen el mismo resultado en la simulación.

Me doy cuenta de que estoy corriendo el riesgo de comenzar una discusión en este hilo. Por favor, no vayas allí; esa no es mi intención Solo quería publicar otra solución potencial.

¡Gracias por el divertido ejemplo con el que jugar!

    
respondido por el DigitalHead

Lea otras preguntas en las etiquetas