Me ha costado mucho entender la diferencia entre bloquear y no bloquear las asignaciones en Verilog. Quiero decir, entiendo la diferencia conceptual entre los dos, pero estoy realmente perdido cuando se trata de la implementación.
Me referí a varias fuentes, incluida esta pregunta , pero todas las explicaciones parecen explicar la diferencia en términos de código (lo que sucede con la secuencia de ejecución de las líneas cuando se usa el bloqueo frente al no bloqueo). Mi pregunta es un poco diferente.
Al escribir código verilog (ya que lo estoy escribiendo para sintetizarlo en un FPGA), siempre trato de visualizar cómo se verá el circuito sintetizado, y ahí es donde comienza el problema:
1) No puedo entender cómo el cambio de las asignaciones de bloqueo a no bloqueo podría alterar mi circuito sintetizado. Por ejemplo:
always @* begin
number_of_incoming_data_bytes_next <= number_of_incoming_data_bytes_reg;
generate_input_fifo_push_pulse_next <= generate_input_fifo_push_pulse;
if(state_reg == idle) begin
// mealey outputs
count_next = 8'b0;
if((rx_done_tick) && (rx_data_out == START_BYTE)) begin
state_next = read_incoming_data_length;
end else begin
state_next = idle;
end
end else if(state_reg == read_incoming_data_length) begin
// mealey outputs
count_next = 8'b0;
if(rx_done_tick) begin
number_of_incoming_data_bytes_reg <= rx_data_out;
state_next = reading;
end else begin
state_next = read_incoming_data_length;
end
end else if(state_reg == reading) begin
if(count_reg == number_of_incoming_data_bytes_reg) begin
state_next = idle;
// do something to indicate that all the reading is done
// and to send all the data in the fifo
end else begin
if(rx_done_tick) begin
generate_input_fifo_push_pulse_next = ~ generate_input_fifo_push_pulse;
count_next = count_reg + 1;
end else begin
count_next = count_reg;
end
end
end else begin
count_next = 8'b0;
state_next = idle;
end
end
En el código anterior, ¿cómo cambiaría el circuito sintetizado si reemplazara todas las asignaciones de bloqueo por no bloqueado?
2) Comprender la diferencia entre las instrucciones de bloqueo y no bloqueo cuando se escriben de forma secuencial es un poco más simple (y la mayoría de las respuestas a esta pregunta se centran en esta parte), pero ¿cómo afectan los comportamientos de bloqueo cuando se declaran en comportamientos condicionales separados? . Por ejemplo:
¿Haría una diferencia si escribiera esto?
if(rx_done_tick) begin
a = 10;
end else begin
a = 8;
end
o si escribí esto:
if(rx_done_tick) begin
a <= 10;
end else begin
a <= 8;
end
Sé que las sentencias condicionales se sintetizan para convertirse en multiplexores o estructuras de prioridad, por lo que creo que usar sentencias de bloqueo o no de bloqueo no debería hacer una diferencia, pero no estoy seguro.
3) Al escribir bancos de pruebas, el resultado de la simulación es muy diferente cuando se usan las instrucciones de bloqueo de v / s no bloqueantes. El comportamiento es muy diferente si escribo:
initial begin
#31 rx_data_out = 255;
rx_done_tick = 1;
#2 rx_done_tick = 0;
#30 rx_data_out = 3;
rx_done_tick = 1;
#2 rx_done_tick = 0;
#30 rx_data_out = 10;
rx_done_tick = 1;
#2 rx_done_tick = 0;
end
frente cuando escribo esto:
initial begin
#31 rx_data_out <= 255;
rx_done_tick <= 1;
#2 rx_done_tick <= 0;
#30 rx_data_out <= 3;
rx_done_tick <= 1;
#2 rx_done_tick <= 0;
#30 rx_data_out <= 10;
rx_done_tick <= 1;
#2 rx_done_tick <= 0;
end
Esto es muy confuso. En mi práctica, la señal rx_done_tick será generada por un Flip Flop. Por lo tanto, creo que las declaraciones no bloqueantes deben usarse para representar este comportamiento. Estoy en lo correcto?
4) Finalmente, ¿cuándo usar las asignaciones de bloqueo y cuándo no usar las instrucciones de no bloqueo? Es decir, ¿es cierto que las instrucciones de bloqueo deben usarse solo en comportamientos combinacionales, y las instrucciones de no bloqueo en comportamientos secuenciales solamente? En caso afirmativo o no, ¿por qué?