digital logic – Why is the waveform not matching? (2 clock delay in FSM code)
Context : I have been tasked with testing a HC-04 Ultrasonic sensor with Verilog, and below is the Verilog code, the testbench and the waveform that I am getting,
/*
Module HC_SR04 Ultrasonic Sensor
This module will detect objects present in front of the range, and give the distance in mm.
Input: clk_50M - 50 MHz clock
reset - reset input signal (Use negative reset)
echo_rx - receive echo from the sensor
Output: trig - trigger sensor for the sensor
op - output signal to indicate object is present.
distance_out - distance in mm, if object is present.
*/
// module Declaration
module t1b_ultrasonic(
input clk_50M, reset, echo_rx,
output reg trig,
output reg op,
output reg [15:0] distance_out
);
initial begin
trig = 0;
end
//////////////////DO NOT MAKE ANY CHANGES ABOVE THIS LINE //////////////////
// FSM states
localparam IDLE = 3'd0,
WAIT_1US = 3'd1,
TRIG_HIGH = 3'd2,
WAIT_ECHO = 3'd3,
MEASURE = 3'd4,
DONE = 3'd5,
WAIT_LONG = 3'd6,
FINAL = 3'd7;
reg [2:0] state, next_state;
reg [2:0] measurement_count;
reg [31:0] counter; // General-purpose counter for delays
reg [31:0] echo_counter; // Counter for echo pulse duration
// Timing parameters for 50MHz clock
localparam ONE_US = 32'd49; // Adjusted for exact timing match
localparam TRIG_PULSE = 32'd500; // 10 us = 500 cycles @ 50MHz
localparam W1 = 32'd550002;
localparam W2 = 32'd520002;
localparam W3 = 32'd500002;
localparam W4 = 32'd570002;
// Divisor for distance based on 340 m/s
localparam integer DIST_DIV = 32'd295;
localparam integer ROUND_OFFSET = 32'd58;
// Initialize registers for simulation safety
initial begin
state = IDLE;
next_state = IDLE;
measurement_count = 3'd0;
counter = 32'd0;
echo_counter = 32'd0;
op = 1'b0;
distance_out = 16'd0;
end
// Sequential logic: state updates, counters, and outputs
always @(posedge clk_50M or negedge reset) begin
if (!reset) begin
// Active-low asynchronous reset
state <= IDLE;
measurement_count <= 3'd0;
counter <= 32'd0;
echo_counter <= 32'd0;
trig <= 1'b0;
op <= 1'b0;
distance_out <= 16'd0;
end else begin
// Update outputs on transitions before state change
if (state == MEASURE && !echo_rx) begin
distance_out <= (echo_counter + ROUND_OFFSET) / DIST_DIV;
op <= (((echo_counter + ROUND_OFFSET) / DIST_DIV) < 16'd70) ? 1'b1 : 1'b0;
end else if (state == WAIT_ECHO && !echo_rx) begin
distance_out <= 16'd0;
op <= 1'b0;
end
state <= next_state;
case (state)
IDLE: begin
trig <= 1'b0;
counter <= 32'd0;
echo_counter <= 32'd0;
end
WAIT_1US: begin
trig <= 1'b0;
counter <= (next_state == TRIG_HIGH) ? 32'd0 : counter + 1;
end
TRIG_HIGH: begin
trig <= (counter < TRIG_PULSE) ? 1'b1 : 1'b0;
counter <= counter + 1;
end
WAIT_ECHO: begin
trig <= 1'b0;
counter <= 32'd0;
if (echo_rx) begin
echo_counter <= 32'd1;
end else begin
echo_counter <= 32'd0;
end
end
MEASURE: begin
trig <= 1'b0;
if (echo_rx) begin
echo_counter <= echo_counter + 1;
end else begin
echo_counter <= 32'd0;
end
end
DONE: begin
trig <= 1'b0;
measurement_count <= measurement_count + 1;
counter <= 32'd0;
end
WAIT_LONG: begin
trig <= 1'b0;
counter <= (next_state == IDLE) ? 32'd0 : counter + 1;
end
FINAL: begin
trig <= 1'b0;
end
default: begin
trig <= 1'b0;
end
endcase
// Override counter reset for transition to WAIT_ECHO
if (next_state == WAIT_ECHO && state != WAIT_ECHO) begin
counter <= 32'd0;
end
end
end
// Combinational: next-state logic
always @(*) begin
next_state = state;
case (state)
IDLE: begin
next_state = WAIT_1US;
end
WAIT_1US: begin
if (counter >= ONE_US) begin
next_state = TRIG_HIGH;
end
end
TRIG_HIGH: begin
if (counter >= TRIG_PULSE) begin
next_state = WAIT_ECHO;
end
end
WAIT_ECHO: begin
if (echo_rx) begin
next_state = MEASURE;
end else begin
next_state = DONE;
end
end
MEASURE: begin
if (!echo_rx) begin
next_state = DONE;
end
end
DONE: begin
if (measurement_count == 3'd4) begin
next_state = FINAL;
end else begin
next_state = WAIT_LONG;
end
end
WAIT_LONG: begin
case (measurement_count)
3'd1: begin
if (counter >= W1) next_state = IDLE;
end
3'd2: begin
if (counter >= W2) next_state = IDLE;
end
3'd3: begin
if (counter >= W3) next_state = IDLE;
end
3'd4: begin
if (counter >= W4) next_state = IDLE;
end
default: begin
next_state = IDLE;
end
endcase
end
FINAL: begin
next_state = FINAL;
end
default: begin
next_state = IDLE;
end
endcase
end
//////////////////DO NOT MAKE ANY CHANGES BELOW THIS LINE //////////////////
endmodule
The testbench is;
// This module will test the ultrasonic sensor.
`timescale 1ns/1ns
module tb();
reg clk_50M;
reg reset;
reg echo;
wire trig;
wire obstacle;
wire [15:0] distance_out;
reg exp_trig;
reg [15:0] exp_distance_out;
integer i, fw;
integer counter, error_count;
t1b_ultrasonic uut(
.clk_50M(clk_50M),
.reset(reset),
.echo_rx(echo),
.trig(trig),
.op(obstacle),
.distance_out(distance_out)
);
initial begin
clk_50M = 0;
reset = 1; echo = 0;
i = 0; fw = 0;
counter = 0; error_count = 0;
exp_trig = 0; exp_distance_out = 0;
end
always begin
#10 clk_50M = ~clk_50M;
end
always @(posedge clk_50M) begin
#100; reset = 1; #920;
exp_trig = 1;
i = i + 1;
#10000;
exp_trig = 0;
echo = 1;
repeat(50000)@(posedge clk_50M);
echo = 0;
@(posedge clk_50M);
exp_distance_out = 169;
repeat(49999)@(posedge clk_50M);
#10001100;
exp_trig = 1;
i = i + 1;
#10000;
exp_trig = 0;
echo = 1;
repeat(80000)@(posedge clk_50M);
echo = 0;
@(posedge clk_50M);
exp_distance_out = 271;
repeat(19999)@(posedge clk_50M);
#10001100;
exp_trig = 1;
i = i + 1;
#10000;
exp_trig = 0;
echo = 1;
repeat(100000)@(posedge clk_50M);
echo = 0;
@(posedge clk_50M);
exp_distance_out = 339;
#10001080;
exp_trig = 1;
i = i + 1;
#10000;
exp_trig = 0;
echo = 1;
repeat(30000)@(posedge clk_50M);
echo = 0;
@(posedge clk_50M);
exp_distance_out = 101;
repeat(69999)@(posedge clk_50M);
echo = 0;
#10001100;
exp_trig = 1;
i = i + 1;
#10000;
exp_trig = 0;
@(posedge clk_50M);
exp_distance_out = 0;
exp_trig = 0;
repeat(500)@(posedge clk_50M);
//$stop();
end
always@(posedge clk_50M) begin
#1;
if(trig !== exp_trig) error_count = error_count + 1;
if(distance_out !== exp_distance_out) error_count = error_count + 1;
if(i >= 4 ) begin
if(error_count !== 0) begin
fw = $fopen("result.txt", "w");
$fdisplay(fw, "%02h","Errors");
$display("Error(s) encountered, please check your design!");
$fclose(fw);
end else begin
fw = $fopen("result.txt", "w");
$fdisplay(fw, "%02h","No Errors");
$display("No errors encountered, congratulations!");
$fclose(fw);
end
i = 0;
end
end
endmodule
The waveforms are;
The waveform when first error is flagged;
How do I fix this issue between the trig and exp_trig signal which is 2 clocks delayed and this propagates to give error_count = 19. What change in the Verilog code can I make to fix this?
Read more here: Source link


