always_ff siempre se ejecuta antes que always_comb en ModelSim

0

Tengo un ejercicio que separa la lógica de peine de la lógica secuencial en el bloque always_ff.

Sin embargo, encontré que el orden de las ejecuciones always_comb y always_ff es diferente entre los diferentes simuladores.

Por lo que sé, el bloque always_comb se ejecuta siempre que la variable en la lista de sensibilidad cambia. (Corríjame si me equivoco) y always_ff se ejecuta en el borde de la señal (por ejemplo, clk).

Desde la prospectiva de programación, el orden de ejecución de always_comb y always_ff es importante, ¿verdad? Siempre asignamos una variable de forma no bloqueable dentro de un flip flop desde una variable que es controlada por always_comb?

out_pkt_size <= pkt_size; // pkt_size is drive by always_comb

Por favor avise.

Puede verificar el código en edaplayground

always_comb begin
    $display("%2g, always_comb       : in_vld       = %b", $time, in_vld);
    if (in_vld) begin
        pkt[HIBIT-:WIDTH] = in_data;
        pkt_size = {<<8{pkt[HIBIT-:16]}};
    end else begin
        pkt = {WIDTH{1'hx}};
        pkt_size = 'd0;
    end
end

always_ff @(posedge clk or negedge reset_n) begin
    $strobe( "%2g, always_ff(strobe) : out_pkt_size = %1d", $time, out_pkt_size);
    $display("%2g, always_ff         : in_vld       = %b", $time, in_vld);
    if (!reset_n) begin
        out_pkt_size <= 1'd0;
    end else begin          
        out_pkt_size <= pkt_size;
    end
end

Ejecute el resultado desde edaplayground (usando Synopsys VCS como simulador):

Puedes ver que always_comb se ejecuta antes que always_ff.

initial ...
 0, always_ff         : in_vld       = x
 0, always_comb       : in_vld       = x
 0, always_ff(strobe) : out_pkt_size = 0
10, always_comb       : in_vld       = 0
10, always_ff         : in_vld       = 0
10, always_ff(strobe) : out_pkt_size = 0
20, always_comb       : in_vld       = 1
20, always_ff         : in_vld       = 1
20, always_ff(strobe) : out_pkt_size = 140
30, always_comb       : in_vld       = 0
30, always_ff         : in_vld       = 0
30, always_ff(strobe) : out_pkt_size = 0
40, always_ff         : in_vld       = 0
40, always_ff(strobe) : out_pkt_size = 0
50, always_ff         : in_vld       = 0
50, always_ff(strobe) : out_pkt_size = 0
$finish called from file "testbench.sv", line 96.
$finish at simulation time                   55

Resultado de ModelSim:

Puede ver que, sin embargo, always_ff siempre se ejecuta antes que always_comb. El resultado es que out_pkt_size está 1 ciclo atrás.

run
# initial ...
#  0, always_comb       : in_vld       = x
#  0, always_ff         : in_vld       = x
#  0, always_ff(strobe) : out_pkt_size = 0
# 10, always_ff         : in_vld       = 0
# 10, always_comb       : in_vld       = 0
# 10, always_ff(strobe) : out_pkt_size = 0
# 20, always_ff         : in_vld       = 1
# 20, always_comb       : in_vld       = 1
# 20, always_ff(strobe) : out_pkt_size = 0
# 30, always_ff         : in_vld       = 0
# 30, always_comb       : in_vld       = 0
# 30, always_ff(strobe) : out_pkt_size = 140
# 40, always_ff         : in_vld       = 0
# 40, always_ff(strobe) : out_pkt_size = 0
# 50, always_ff         : in_vld       = 0

run
    
pregunta Ken Tsang

1 respuesta

1

La diferencia en el orden entre tus always_ff y always_comb es el resultado de un orden de asignación diferente entre tus bloques initial y always .

Su bloque initial usa un retraso #10 para hacer asignaciones a entradas sensibles en el bloque always_comb . Eso coincide con la asignación clk=~clk en el bloque always . Así que tienes una condición de carrera con la sincronización del borde positivo de always_ff de clk y las entradas al always_comb

Es necesario modificar el tiempo de la asignación de bloque initial para no estar en una carrera mediante el uso de asignaciones no bloqueantes, o el uso de demoras que hacen que las asignaciones salgan de la posición del reloj.

    
respondido por el dave_59

Lea otras preguntas en las etiquetas