I need to learn Vivado and Verilog for an exam in my university, and I think I'm having a problem in learning non blocking assignments. The code is the following:
`timescale 1ns / 1ps
module button_register(
input wire clk, // Clock signal
input wire btn, // Button input (debounced)
output reg value // Register storing the button state
);
// On every rising edge of the clock, store the button value in the register
always @(posedge clk) begin
value <= btn;
end
endmodule
and I'm simulating its behavior with the following test bench:
`timescale 1ns / 1ps
module tb_button_register;
reg clk; // Testbench clock signal
reg btn; // Simulated debounced button
wire value; // Output register
// Instantiate the module under test (MUT)
button_register uut (
.clk(clk),
.btn(btn),
.value(value)
);
// Clock generation: 10 ns period (100 MHz frequency)
initial begin
clk = 0;
forever #10 clk = ~clk; // Toggle clock every 5 ns
end
// Stimulus: Simulate the button behavior
initial begin
// Initialize button to 0
btn = 0;
// Wait a few clock cycles, then toggle the button
#25 btn = 1; // Button goes high after 20 ns
#10 btn = 0; // Button goes low after 30 ns
#100 $finish; // End simulation after 100 ns
end
// Monitor signals for debugging
initial begin
$monitor("Time: %0dns | clk: %b | btn: %b | value: %b ", $time, clk, btn, value);
end
endmodule
If I understood correctly (and at this point I'm pretty sure I'm wrong), at the rising edge of the clock, the button is sampled. If it is pressed, since I'm using a non blocking assignment, "value" is scheduled to be 1 at the end of the current clock cycle. Which means that the next clock cycle it will be 1. Here the problem comes: when I simulate it, this is what I see:
"value" is updated immediately to 1 as soon as the button is detected to be pressed, at the rising edge of the clock. Why? I was expecting value to be one the following clock cycle.
1 Answer 1
"value" is scheduled to be 1 at the end of the current clock cycle
That is incorrect. You misunderstood the meaning of nonblocking assignments. value
is scheduled to be 1 at the end of the current simulation time step, not the current clock cycle which takes 10ns of simulation time. This is a subtlety of the Verilog standard (IEEE Std 1800-2023).
The simulation is behaving as expected. Since you are sampling the btn
signal when it is stable (with a value of 1) at time=30ns, value
is immediately updated with that value (1).
If you change the testbench to also use nonblocking assignments for the btn
input signal, and drive the input the same way as you drive value
(@(posedge clk)
), which is a good coding practice when simulating synchronous logic, then you will see value
change at the end of the clock cycle:
initial begin
// Initialize button to 0
btn = 0;
// Wait a few clock cycles, then toggle the button
#20;
@(posedge clk) btn <= 1;
@(posedge clk) btn <= 0;
/*
#25 btn = 1; // Button goes high after 20 ns
#10 btn = 0; // Button goes low after 30 ns
*/
#100 $finish; // End simulation after 100 ns
end