Pestillos de prevención Verilog

Creé un módulo Verilog simple que muestra números aleatorios. Este módulo tiene un contador que cuenta en cada borde del reloj y con solo presionar un botón, se muestra el número que esté en el contador en ese momento. Aquí está mi código:

module counter (input wire in, input wire clock, input wire reset, output reg [3:0] number)
reg [3:0] cur_state;
reg [3:0] next_state;

always @ (posedge clock) begin
    if (reset) cur_state <= 4'b0;
    else cur_state <= next_state;
end

// next state function
always @ (*) begin
    next_state = cur_state + 4'b1;
end

// output
always @ (*) begin
    if (in) number = cur_state;
end

number[3:0] se envía a un módulo de visualización para mostrar el número correspondiente. (Las entradas se eliminan correctamente).

Todo funciona bien en la FPGA, pero el programa me notifica que he usado un pestillo en la salida. ¿Hay alguna forma de prevenir esto e implementar el mismo comportamiento usando un flip-flop?

Gracias.

always @ (*) begin if (in) number = cur_state;¿ Qué pasa cuando inestá bajo?
El valor anterior almacenado en número se conserva.
¿Y qué implica eso en un circuito combinacional?

Respuestas (3)

Su problema radica en que está describiendo un circuito asíncrono que requiere su estado anterior.

// output
always @ (*) begin
    if (in) number = cur_state;
end

Cuando ines alto, todo está bien - numberse le asigna el valor de cur_state. Sin embargo, ¿qué sucede cuando ines bajo?

Cuando ines bajo, numberno tiene un nuevo valor especificado (es decir, a través de else), lo que significa que está infiriendo que debe mantener su valor. Cada vez que se le pide a un circuito combinacional que mantenga su valor, obtiene un pestillo.

Entonces, la forma de evitar bloqueos es asegurarse de que en cada lógica inferida de forma combinada, defina completamente el valor asignado para que nunca se requiera. Puede hacer esto de una de dos maneras.

 

Primero, si no le importa el valor cuando ines bajo, puede asignar alguna constante:

  // output
  always @ (*) begin
      if (in) begin
          number = cur_state; //If in is high, output the current state 
      end else begin
          number = 4'b0000; //If in is low, output is don't care so avoid latch by assigning value
      end
  end

 

En segundo lugar, si necesita que la salida mantenga su estado, debe convertirlo en un proceso cronometrado:

// output
always @ (posedge clock) begin
    if (in) number = cur_state;
end

Ahora que es síncrono, puede hacer que la salida mantenga su estado porque ahora está infiriendo un flip-flop. La desventaja de esto es que tiene una latencia de 1 ciclo desde que cambia la inseñal hasta que numberse actualiza el valor.

@toolic no hace ninguna diferencia en el código anterior. No hay nada que decir que el no bloqueo debe usarse para procesos cronometrados. Se debe evitar mezclar los dos.

Las respuestas de la combinación de @Tom Carpenter y @toolic son casi correctas. Necesita las asignaciones sin bloqueo si desea evitar problemas de simulación de eventos combinacionales que ocurren después de eventos secuenciales. La asignación sin bloqueo se asegura de eso.

Otra cosa que es importante agregar es que la entrada de entrada es una señal asíncrona y, por lo tanto, si se muestrea, debe muestrearse a través de un sincronizador de doble flop como mínimo.

He reescrito su código con un solo registro para mantener el estado de su FSM.

module counter (input wire in, input wire clock, input wire reset, output reg  number);
reg  cur_state;
reg  next_state;

always @ (posedge clock) begin
    if (reset) cur_state <= 4'b0;
    else cur_state <= next_state;
end

// next state function
always @ (*) begin
    next_state = cur_state + 1'b1;
end

// output
always @ (*) begin
    if (in) number = cur_state;
end
endmodule

Así se sintetiza tu código usando Yosys

ingrese la descripción de la imagen aquí

Esto tiene sentido, ya que estamos describiendo un FSM con un solo registro [un solo flip flop 1] para almacenar el estado y en cada flanco positivo del reloj, la entrada en este flip flop es una salida de circuito combinacional [que agrega 1 al estado actual] .

Esto se describe con estos dos bloques funcionales.

always @ (posedge clock) begin
    if (reset) cur_state <= 4'b0;
    else cur_state <= next_state;
end

// next state function
always @ (*) begin
    next_state = cur_state + 1'b1;
end

El problema principal ahora está en el DLatch final que retiene su salida

always @ (*) begin
    if (in) number = cur_state;
end

Esto se debe a que el bloque de código anterior describe un DLatch. Siempre que IN == 1 deje que la salida = el estado actual, y siempre que IN == 0 no cambie la salida.

Esto se traduce en un Dlatch en el que la puerta del pestillo [El EN] está conectada a la entrada IN y la entrada de este DLatch está conectada al estado actual.

Como se mencionó anteriormente, puede corregir esta mala interpretación usando una declaración else en el último bloque siempre.

always @ (*) begin
    if (in) number = cur_state;
    else number=0;
end 

Ahora tu así es como se sintetiza el código. Puede ver que la salida ahora es impulsada por una puerta AND, no por un pestillo.

ingrese la descripción de la imagen aquí

Estoy bastante seguro de que las respuestas anteriores ya mencionaron esto, pero creo que observar cómo se sintetiza su circuito hace una gran diferencia.

(Noob de FPGA aquí) Si no me importa el valor de numbercuando ines bajo, ¿puedo asignarlo 4'bxcomo valor constante, para que quede explícito que no me importa? Si es así, aunque no me importa, ¿cuál podría ser el valor de salida "real"? ¿Habría alguna diferencia si uso 4'bzpara configurarlo en alta impedancia, o no se recomienda aquí?
EDITAR : según stackoverflow.com/q/29451175/586382 , parece que su compatibilidad depende del sintetizador y, en general, es mejor optimizar los mapas de Karnaugh a mano para tener un control total de la salida en lugar de tener un comportamiento indefinido.