Add SymbiYosys formal verification for 6 modules, fix 2 doppler bugs

Formal verification (SymbiYosys + smtbmc/z3):
- cdc_single_bit: BMC PASS depth 80, cover PASS 3/3
- cdc_handshake: BMC PASS depth 100, cover PASS 4/4
- cdc_adc_to_processing: BMC PASS depth 80, cover PASS
- radar_mode_controller: BMC PASS depth 200, cover PASS 8/8
- range_bin_decimator: cover PASS 7/7, BMC running (step 61+)
- doppler_processor: cover running (step 133/150), BMC running (step 35+)

DUT bug fixes found by formal:
- doppler_processor: write_chirp_index overflow past CHIRPS_PER_FRAME-1
  in S_ACCUMULATE frame-complete branch (reset to 0)
- doppler_processor: read_doppler_index unclamped prefetch in S_LOAD_FFT
  causing OOB BRAM reads (clamped to DOPPLER_FFT_SIZE-1)

CDC fix (prior session, included):
- cdc_modules: async reset changed to sync reset on all CDC sync chains
  to prevent metastability on reset deassertion

RTL changes for formal observability:
- Added ifdef FORMAL output ports to cdc_handshake (6), cdc_adc (2),
  radar_mode_controller (2), range_bin_decimator (5), doppler_processor (11)
This commit is contained in:
Jason
2026-03-17 12:47:22 +02:00
parent a9c857c447
commit fb59e98737
17 changed files with 1979 additions and 14 deletions
+38 -9
View File
@@ -17,6 +17,10 @@ module cdc_adc_to_processing #(
input wire src_valid,
output wire [WIDTH-1:0] dst_data,
output wire dst_valid
`ifdef FORMAL
,output wire [WIDTH-1:0] fv_src_data_reg,
output wire [1:0] fv_src_toggle
`endif
);
// Gray encoding for safe CDC
@@ -50,8 +54,8 @@ module cdc_adc_to_processing #(
reg dst_valid_reg = 0;
reg [1:0] prev_dst_toggle = 2'b00;
// Source domain: capture data and toggle
always @(posedge src_clk or negedge reset_n) begin
// Source domain: capture data and toggle synchronous reset
always @(posedge src_clk) begin
if (!reset_n) begin
src_data_reg <= 0;
src_toggle <= 2'b00;
@@ -97,8 +101,8 @@ module cdc_adc_to_processing #(
end
endgenerate
// Detect new data
always @(posedge dst_clk or negedge reset_n) begin
// Detect new data synchronous reset
always @(posedge dst_clk) begin
if (!reset_n) begin
dst_data_reg <= 0;
dst_valid_reg <= 0;
@@ -119,11 +123,18 @@ module cdc_adc_to_processing #(
assign dst_data = dst_data_reg;
assign dst_valid = dst_valid_reg;
`ifdef FORMAL
assign fv_src_data_reg = src_data_reg;
assign fv_src_toggle = src_toggle;
`endif
endmodule
// ============================================================================
// CDC FOR SINGLE BIT SIGNALS
// Uses synchronous reset on sync chain to avoid metastability on reset
// deassertion. Matches cdc_adc_to_processing best practice.
// ============================================================================
module cdc_single_bit #(
parameter STAGES = 3
@@ -137,7 +148,7 @@ module cdc_single_bit #(
(* ASYNC_REG = "TRUE" *) reg [STAGES-1:0] sync_chain;
always @(posedge dst_clk or negedge reset_n) begin
always @(posedge dst_clk) begin
if (!reset_n) begin
sync_chain <= 0;
end else begin
@@ -151,6 +162,7 @@ endmodule
// ============================================================================
// CDC FOR MULTI-BIT WITH HANDSHAKE
// Uses synchronous reset to avoid metastability on reset deassertion.
// ============================================================================
module cdc_handshake #(
parameter WIDTH = 32
@@ -164,6 +176,14 @@ module cdc_handshake #(
output wire [WIDTH-1:0] dst_data,
output wire dst_valid,
input wire dst_ready
`ifdef FORMAL
,output wire fv_src_busy,
output wire fv_dst_ack,
output wire fv_dst_req_sync,
output wire [1:0] fv_src_ack_sync_chain,
output wire [1:0] fv_dst_req_sync_chain,
output wire [WIDTH-1:0] fv_src_data_reg_hs
`endif
);
// Source domain
@@ -178,9 +198,18 @@ module cdc_handshake #(
reg dst_req_sync = 0;
(* ASYNC_REG = "TRUE" *) reg [1:0] dst_req_sync_chain = 2'b00;
reg dst_ack = 0;
`ifdef FORMAL
assign fv_src_busy = src_busy;
assign fv_dst_ack = dst_ack;
assign fv_dst_req_sync = dst_req_sync;
assign fv_src_ack_sync_chain = src_ack_sync_chain;
assign fv_dst_req_sync_chain = dst_req_sync_chain;
assign fv_src_data_reg_hs = src_data_reg;
`endif
// Source clock domain
always @(posedge src_clk or negedge reset_n) begin
// Source clock domain synchronous reset
always @(posedge src_clk) begin
if (!reset_n) begin
src_data_reg <= 0;
src_busy <= 0;
@@ -200,8 +229,8 @@ module cdc_handshake #(
end
end
// Destination clock domain
always @(posedge dst_clk or negedge reset_n) begin
// Destination clock domain synchronous reset
always @(posedge dst_clk) begin
if (!reset_n) begin
dst_data_reg <= 0;
dst_valid_reg <= 0;