¿Cómo elimino los latches en la implementación de FSM Verilog?

3

Estoy intentando crear un FSM que deja desplazamientos en un registro hasta que el MSB es 1 mientras se cuenta el número de turnos completados. Sin embargo, tengo un problema con los cierres porque no reasigno cada registro en un cambio de estado; a saber, los registros shift y ctr (ver las declaraciones "if (shift [3])"). Para deshacerme de los latches, sé que debo asignar estos registros, pero no quiero que sus valores cambien al final, quiero que se mantengan constantes.

¿Cómo logro esto?

'define RST 2'b00
'define LSH1 2'b01
'define LSH2 2'b10
'define DONE 2'b11

module leftshift(clk, rst, x, shift);
    input [3:0] x;
    input clk, rst;
    output [3:0] shift;

    reg [1:0] state, nextstate;

    reg [3:0] shift; // reg holding shifted bits
    reg [2:0] ctr; // ctr keeps track of how many shifts until completion

    always @ (posedge clk)
        if (rst) state <= 'RST;
        else state <= nextstate;

    always @ (state, x, shift[3])
        case (state)
            'RST: begin
                shift <= x;
                ctr <= 0;
                nextstate <= 'LSH1;
            end
            'LSH1: begin
                if (shift[3]) begin
                    //shift <= //don't change;
                    //ctr <= //don't change;
                    nextstate <= 'DONE;
                end
                else begin
                    shift <= {shift[2:0], 1'b0};
                    ctr <= ctr+1;
                    nextstate <= 'LSH2;
                end
            end
            'LSH2: begin
                if (shift[3]) begin
                    //ctr <= //don't change
                    //shift <= //don't change
                    nextstate <= 'DONE;
                end
                else begin
                    shift <= {shift[2:0], 1'b0};
                    ctr <= ctr+1;
                    nextstate <= 'LSH1;
                end
            end
            'DONE: begin
                //shift <= //don't change;
                //ctr <= //don't change;
                nextstate <= 'DONE;
            end
        endcase
endmodule
    
pregunta

3 respuestas

4

Lo que veo es el estilo de codificación en el que tienes una sección registrada y combinatoria. es un buen estilo de codificación, pero también funciona si eres 100% consistente en tu código:
Todo lo que registre (estado, contador, cambio) debe estar en la sección del reloj. y solo debe usar asignaciones "<=" no bloqueantes.
¡Todo código combinatorio debe estar completo! codificado y solo debe usar las asignaciones de bloqueo "=".
Comience agregando next_shift y next_cnt y en la sección de cronometraje use

always @ (posedge clk)
   if (rst) 
   begin
      state <= 'RST_STATE;
      shift <= 'RST_SHIFT;
      cnt   <= 'RST_CNT;
   end
   else
   begin
      state <= next_state;
      shift <= next_shift;
      cnt   <= next_cnt;
   end
 end

No voy a volver a codificar toda la sección combinatoria (lo siento), pero ahí usas:

always @ ( * ) // easiest!
begin
    case (state)
        'RST_STATE: begin
            next_shift = x;
            next_ctr   = 0;
            next_state = 'DONE;
        end
        'LSH1: begin
             if (shift[3]) begin
               next_shift = shift; // no change
               next_ctr   = cntr;  // no change
               next_state = 'DONE; // 
             end
             else begin 
               next_shift = {shift[2:0], 1'b0};
               next_ctr   = cntr+1;  
               next_state = 'LSH2;
             end
  etc...

end

Como dije, es un buen estilo de codificación porque puede manejar mejor algunos casos desagradables, pero el otro estilo de codificación (acabo de ver aparecer otra respuesta) es mucho más fácil.

Descargo de responsabilidad: el código no compilado puede haber errores tipográficos allí

    
respondido por el Oldfart
2

La división de partes secuenciales y combinatorias no es eficiente para su diseño. Además, el bloque combinational always tiene asignaciones no bloqueantes ( <= ), lo que puede causar resultados de síntesis inesperados.

Un solo bloque secuencial always debería resolver el problema.

always @ (posedge clk) begin
    if (rst) begin
        state <= 'RST;
        shift <= 0;
        ctr <= 0;
    end else begin
        case (state)
            'RST: begin
                shift <= x;
                ctr <= 0;
                state <= 'LSH1;
            end
            'LSH1: begin
                if (shift[3]) begin
                    //shift <= //don't change;
                    //ctr <= //don't change;
                    state <= 'DONE;
                end
                else begin
                    shift <= {shift[2:0], 1'b0};
                    ctr <= ctr+1;
                    state <= 'LSH2;
                end
            end
            'LSH2: begin
                if (shift[3]) begin
                    //ctr <= //don't change
                    //shift <= //don't change
                    state <= 'DONE;
                end
                else begin
                    shift <= {shift[2:0], 1'b0};
                    ctr <= ctr+1;
                    state <= 'LSH1;
                end
            end
            'DONE: begin
                //shift <= //don't change;
                //ctr <= //don't change;
                state <= 'DONE;
            end
        endcase
    end
end

Tanto ctr como shift son flip-flop aquí. Si no hay asignación en un caso, preservarán sus valores. La asignación ctr <= ctr también hace lo mismo.

    
respondido por el ahmedus
1

Dado que los demás ya han dado correctamente las respuestas específicas del código. Solo tengo una respuesta general a esta pregunta.

Para evitar trabas en un diseño HDL, hay que tener en cuenta dos puntos importantes.

1) Asegúrate de cubrir todas las condiciones posibles en el caso y si se construye, aunque sientas que no aparecerá en tu diseño o que consideres que es irrelavante. Debido a que el sintetizador siempre "pensará" que tal condición aparecerá en su diseño e inferirá un cierre para tales condiciones.

2) Una vez que lo hiciste correctamente, el siguiente paso es asegurarte de que todas las señales de salida / internas de tu diseño obtengan algún valor en cada ciclo de ejecución, independientemente de las condiciones. De lo contrario, el sintetizador inferirá nuevamente los latches para mantener los valores anteriores de las señales no cubiertas.

    
respondido por el MITU RAJ

Lea otras preguntas en las etiquetas