Fix CDC reset domain bug (P0), strengthen testbenches with 31 structural assertions

Split cdc_adc_to_processing reset_n into src_reset_n/dst_reset_n so source
and destination clock domains use correctly-synchronized resets. Previously
cdc_chirp_counter's destination-side sync chain (100MHz) was reset by
sys_reset_120m_n (120MHz domain), causing 30 CDC critical warnings.

RTL changes:
- cdc_modules.v: split reset port, source logic uses src_reset_n,
  destination sync chains + output logic use dst_reset_n
- radar_system_top.v: cdc_chirp_counter gets proper per-domain resets
- ddc_400m.v: CDC_FIR_i/q use reset_n_400m (src) and reset_n (dst)
- formal/fv_cdc_adc.v: updated wrapper for new port interface

Build 7 fixes (previously untouched):
- radar_transmitter.v: SPI level-shifter assigns, STM32 GPIO CDC sync
- latency_buffer_2159.v: BRAM read registration
- constraints: ft601 IOB -quiet fix
- tb_latency_buffer.v: updated for BRAM changes

Testbench hardening (tb_cdc_modules.v, +31 new assertions):
- A5-A7: split-domain reset tests (staggered deassertion, independent
  dst reset while src active — catches the P0 bug class)
- A8: port connectivity (no X/Z on outputs)
- B7: cdc_single_bit port connectivity
- C6: cdc_handshake reset recovery + port connectivity

Full regression: 13/13 test suites pass (257 total assertions).
This commit is contained in:
Jason
2026-03-17 19:38:09 +02:00
parent 6fc5a10785
commit fcf3999e39
10 changed files with 2479 additions and 2131 deletions
+73 -26
View File
@@ -19,10 +19,11 @@
//
//////////////////////////////////////////////////////////////////////////////////
module radar_transmitter(
// System Clocks
input wire clk_100m, // System clock
input wire clk_120m_dac, // 120MHz DAC clock
input wire reset_n,
// System Clocks
input wire clk_100m, // System clock
input wire clk_120m_dac, // 120MHz DAC clock
input wire reset_n, // Reset synchronized to clk_120m_dac
input wire reset_100m_n, // Reset synchronized to clk_100m (for edge detectors/CDC)
// DAC Interface
output wire [7:0] dac_data,
@@ -79,11 +80,28 @@ module radar_transmitter(
);
// ========== SPI LEVEL SHIFTER PASSTHROUGH ==========
// FPGA bridges 3.3V STM32 SPI bus (Bank 15) to 1.8V ADAR1000 SPI bus (Bank 34).
// The FPGA I/O banks handle the actual voltage translation; these assigns
// route the signals through the fabric.
assign stm32_sclk_1v8 = stm32_sclk_3v3;
assign stm32_mosi_1v8 = stm32_mosi_3v3;
assign stm32_miso_3v3 = stm32_miso_1v8;
assign stm32_cs_adar1_1v8 = stm32_cs_adar1_3v3;
assign stm32_cs_adar2_1v8 = stm32_cs_adar2_3v3;
assign stm32_cs_adar3_1v8 = stm32_cs_adar3_3v3;
assign stm32_cs_adar4_1v8 = stm32_cs_adar4_3v3;
// Edge Detection Signals
wire new_chirp_pulse;
wire new_elevation_pulse;
wire new_azimuth_pulse;
// CDC: Synchronized versions of async STM32 GPIO inputs to clk_100m
wire stm32_new_chirp_sync;
wire stm32_new_elevation_sync;
wire stm32_new_azimuth_sync;
// CDC: Synchronized versions of signals crossing clk_100m -> clk_120m_dac
wire mixers_enable_120m; // stm32_mixers_enable sync'd to clk_120m_dac
wire new_chirp_pulse_120m; // new_chirp_pulse (toggle CDC) in clk_120m_dac domain
@@ -98,8 +116,8 @@ wire chirp_sequence_done;
// would miss it (120/100 MHz ratio). Toggle CDC converts pulse to level toggle,
// syncs the toggle, then detects edges on the destination side.
reg chirp_toggle_100m;
always @(posedge clk_100m or negedge reset_n) begin
if (!reset_n)
always @(posedge clk_100m or negedge reset_100m_n) begin
if (!reset_100m_n)
chirp_toggle_100m <= 1'b0;
else if (new_chirp_pulse)
chirp_toggle_100m <= ~chirp_toggle_100m;
@@ -134,26 +152,55 @@ cdc_single_bit #(.STAGES(3)) cdc_mixers_en_120m (
.dst_signal(mixers_enable_120m)
);
// Enhanced STM32 Input Edge Detection with Debouncing
edge_detector_enhanced chirp_edge (
.clk(clk_100m),
.reset_n(reset_n),
.signal_in(stm32_new_chirp),
.rising_falling_edge(new_chirp_pulse)
);
edge_detector_enhanced elevation_edge (
.clk(clk_100m),
.reset_n(reset_n),
.signal_in(stm32_new_elevation),
.rising_falling_edge(new_elevation_pulse)
);
edge_detector_enhanced azimuth_edge (
.clk(clk_100m),
.reset_n(reset_n),
.signal_in(stm32_new_azimuth),
.rising_falling_edge(new_azimuth_pulse)
// CDC synchronizers: async STM32 GPIO inputs -> clk_100m domain
// These prevent metastability in the edge detectors. Without these,
// the edge detector's first FF can go metastable, and the XOR output
// can glitch, producing false chirp/elevation/azimuth pulses.
cdc_single_bit #(.STAGES(2)) cdc_stm32_chirp (
.src_clk(clk_100m), // Pseudo-source for async GPIO
.dst_clk(clk_100m),
.reset_n(reset_100m_n),
.src_signal(stm32_new_chirp),
.dst_signal(stm32_new_chirp_sync)
);
cdc_single_bit #(.STAGES(2)) cdc_stm32_elevation (
.src_clk(clk_100m),
.dst_clk(clk_100m),
.reset_n(reset_100m_n),
.src_signal(stm32_new_elevation),
.dst_signal(stm32_new_elevation_sync)
);
cdc_single_bit #(.STAGES(2)) cdc_stm32_azimuth (
.src_clk(clk_100m),
.dst_clk(clk_100m),
.reset_n(reset_100m_n),
.src_signal(stm32_new_azimuth),
.dst_signal(stm32_new_azimuth_sync)
);
// Enhanced STM32 Input Edge Detection with Debouncing
// Inputs are now CDC-synchronized (safe from metastability)
edge_detector_enhanced chirp_edge (
.clk(clk_100m),
.reset_n(reset_100m_n),
.signal_in(stm32_new_chirp_sync),
.rising_falling_edge(new_chirp_pulse)
);
edge_detector_enhanced elevation_edge (
.clk(clk_100m),
.reset_n(reset_100m_n),
.signal_in(stm32_new_elevation_sync),
.rising_falling_edge(new_elevation_pulse)
);
edge_detector_enhanced azimuth_edge (
.clk(clk_100m),
.reset_n(reset_100m_n),
.signal_in(stm32_new_azimuth_sync),
.rising_falling_edge(new_azimuth_pulse)
);
// Enhanced PLFM Chirp Generation