diff --git a/9_Firmware/9_2_FPGA/radar_system_top.v b/9_Firmware/9_2_FPGA/radar_system_top.v index 780c6a5..4f50b0a 100644 --- a/9_Firmware/9_2_FPGA/radar_system_top.v +++ b/9_Firmware/9_2_FPGA/radar_system_top.v @@ -132,15 +132,19 @@ wire clk_100m_buf; wire clk_120m_dac_buf; wire ft601_clk_buf; wire sys_reset_n; +wire sys_reset_120m_n; // Reset synchronized to clk_120m_dac domain // Transmitter internal signals wire [7:0] tx_chirp_data; wire tx_chirp_valid; wire tx_chirp_done; -wire tx_new_chirp_frame; +wire tx_new_chirp_frame; // In clk_120m_dac domain +wire tx_new_chirp_frame_sync; // Synchronized to clk_100m domain wire [5:0] tx_current_elevation; wire [5:0] tx_current_azimuth; -wire [5:0] tx_current_chirp; +wire [5:0] tx_current_chirp; // In clk_120m_dac domain +wire [5:0] tx_current_chirp_sync; // Synchronized to clk_100m domain +wire tx_current_chirp_sync_valid; // Receiver internal signals wire [31:0] rx_doppler_output; @@ -186,7 +190,7 @@ BUFG bufg_ft601 ( .O(ft601_clk_buf) ); -// Reset synchronization +// Reset synchronization (clk_100m domain) reg [1:0] reset_sync; always @(posedge clk_100m_buf or negedge reset_n) begin if (!reset_n) begin @@ -197,6 +201,48 @@ always @(posedge clk_100m_buf or negedge reset_n) begin end assign sys_reset_n = reset_sync[1]; +// Reset synchronization (clk_120m_dac domain) +// Ensures reset deassertion is synchronous to the DAC clock, +// preventing recovery/removal timing violations on 120 MHz FFs. +reg [1:0] reset_sync_120m; +always @(posedge clk_120m_dac_buf or negedge reset_n) begin + if (!reset_n) begin + reset_sync_120m <= 2'b00; + end else begin + reset_sync_120m <= {reset_sync_120m[0], 1'b1}; + end +end +assign sys_reset_120m_n = reset_sync_120m[1]; + +// ============================================================================ +// CLOCK DOMAIN CROSSING: TRANSMITTER (120 MHz) -> SYSTEM (100 MHz) +// ============================================================================ + +// CDC for chirp_counter: 6-bit multi-bit Gray-code synchronizer +cdc_adc_to_processing #( + .WIDTH(6), + .STAGES(3) +) cdc_chirp_counter ( + .src_clk(clk_120m_dac_buf), + .dst_clk(clk_100m_buf), + .reset_n(sys_reset_n), + .src_data(tx_current_chirp), + .src_valid(1'b1), // Always valid — counter updates continuously + .dst_data(tx_current_chirp_sync), + .dst_valid(tx_current_chirp_sync_valid) +); + +// CDC for new_chirp_frame: single-bit 3-stage synchronizer +cdc_single_bit #( + .STAGES(3) +) cdc_new_chirp_frame ( + .src_clk(clk_120m_dac_buf), + .dst_clk(clk_100m_buf), + .reset_n(sys_reset_n), + .src_signal(tx_new_chirp_frame), + .dst_signal(tx_new_chirp_frame_sync) +); + // ============================================================================ // RADAR TRANSMITTER INSTANTIATION // ============================================================================ @@ -205,7 +251,7 @@ radar_transmitter tx_inst ( // System Clocks .clk_100m(clk_100m_buf), .clk_120m_dac(clk_120m_dac_buf), - .reset_n(sys_reset_n), + .reset_n(sys_reset_120m_n), // Use 120 MHz-synchronized reset // DAC Interface .dac_data(dac_data), @@ -271,8 +317,8 @@ radar_receiver_final rx_inst ( .clk(clk_100m_buf), .reset_n(sys_reset_n), - // Chirp counter from transmitter (NEW-1 fix: was disconnected) - .chirp_counter(tx_current_chirp), + // Chirp counter from transmitter (CDC-synchronized from 120 MHz domain) + .chirp_counter(tx_current_chirp_sync), // ADC Physical Interface .adc_d_p(adc_d_p), @@ -383,8 +429,8 @@ usb_data_interface usb_inst ( assign current_elevation = tx_current_elevation; assign current_azimuth = tx_current_azimuth; -assign current_chirp = tx_current_chirp; -assign new_chirp_frame = tx_new_chirp_frame; +assign current_chirp = tx_current_chirp_sync; // Use CDC-synchronized version +assign new_chirp_frame = tx_new_chirp_frame_sync; // Use CDC-synchronized version assign dbg_doppler_data = rx_doppler_output; assign dbg_doppler_valid = rx_doppler_valid; @@ -402,7 +448,7 @@ always @(posedge clk_100m_buf or negedge sys_reset_n) begin status_reg[0] <= stm32_mixers_enable; // Mixers enabled status_reg[1] <= ft601_txe; // USB TX ready status_reg[2] <= rx_doppler_valid; // Data valid - status_reg[3] <= tx_new_chirp_frame; // New chirp frame + status_reg[3] <= tx_new_chirp_frame_sync; // New chirp frame (CDC-sync'd) end end @@ -420,7 +466,7 @@ reg [31:0] data_packet_counter; always @(posedge clk_100m_buf) begin debug_cycle_counter <= debug_cycle_counter + 1; - if (tx_new_chirp_frame) begin + if (tx_new_chirp_frame_sync) begin $display("[TOP] New chirp frame started at cycle %0d", debug_cycle_counter); end diff --git a/9_Firmware/9_2_FPGA/usb_data_interface.v b/9_Firmware/9_2_FPGA/usb_data_interface.v index 5f5acd1..e3d872f 100644 --- a/9_Firmware/9_2_FPGA/usb_data_interface.v +++ b/9_Firmware/9_2_FPGA/usb_data_interface.v @@ -58,6 +58,57 @@ reg [31:0] data_buffer; reg [31:0] ft601_data_out; reg ft601_data_oe; // Output enable for bidirectional data bus +// ========== CDC INPUT SYNCHRONIZERS (clk domain -> ft601_clk_in domain) ========== +// The valid signals arrive from clk_100m but the state machine runs on ft601_clk_in. +// Even though both are 100 MHz, they are asynchronous clocks and need synchronization. + +// 2-stage synchronizers for valid signals +reg [1:0] range_valid_sync; +reg [1:0] doppler_valid_sync; +reg [1:0] cfar_valid_sync; + +// Synchronized data captures (registered in ft601_clk_in domain) +reg [31:0] range_profile_cap; +reg [15:0] doppler_real_cap; +reg [15:0] doppler_imag_cap; +reg cfar_detection_cap; + +wire range_valid_ft; +wire doppler_valid_ft; +wire cfar_valid_ft; + +always @(posedge ft601_clk_in or negedge reset_n) begin + if (!reset_n) begin + range_valid_sync <= 2'b00; + doppler_valid_sync <= 2'b00; + cfar_valid_sync <= 2'b00; + range_profile_cap <= 32'd0; + doppler_real_cap <= 16'd0; + doppler_imag_cap <= 16'd0; + cfar_detection_cap <= 1'b0; + end else begin + // Synchronize valid strobes + range_valid_sync <= {range_valid_sync[0], range_valid}; + doppler_valid_sync <= {doppler_valid_sync[0], doppler_valid}; + cfar_valid_sync <= {cfar_valid_sync[0], cfar_valid}; + + // Capture data on rising edge of synchronized valid + if (range_valid_sync[0] && !range_valid_sync[1]) + range_profile_cap <= range_profile; + if (doppler_valid_sync[0] && !doppler_valid_sync[1]) begin + doppler_real_cap <= doppler_real; + doppler_imag_cap <= doppler_imag; + end + if (cfar_valid_sync[0] && !cfar_valid_sync[1]) + cfar_detection_cap <= cfar_detection; + end +end + +// Rising-edge detect on synchronized valid (pulse in ft601_clk_in domain) +assign range_valid_ft = range_valid_sync[0] && !range_valid_sync[1]; +assign doppler_valid_ft = doppler_valid_sync[0] && !doppler_valid_sync[1]; +assign cfar_valid_ft = cfar_valid_sync[0] && !cfar_valid_sync[1]; + // FT601 data bus direction control assign ft601_data = ft601_data_oe ? ft601_data_out : 32'hzzzz_zzzz; @@ -74,13 +125,14 @@ always @(posedge ft601_clk_in or negedge reset_n) begin ft601_rd_n <= 1; ft601_oe_n <= 1; ft601_siwu_n <= 1; - ft601_clk_out <= 0; + // NOTE: ft601_clk_out is driven by the clk-domain always block below. + // Do NOT assign it here (ft601_clk_in domain) — causes multi-driven net. end else begin case (current_state) IDLE: begin ft601_wr_n <= 1; ft601_data_oe <= 0; // Release data bus - if (range_valid || doppler_valid || cfar_valid) begin + if (range_valid_ft || doppler_valid_ft || cfar_valid_ft) begin current_state <= SEND_HEADER; byte_counter <= 0; end @@ -102,10 +154,10 @@ always @(posedge ft601_clk_in or negedge reset_n) begin ft601_be <= 2'b11; // All bytes valid for 32-bit word case (byte_counter) - 0: ft601_data_out <= range_profile; - 1: ft601_data_out <= {range_profile[23:0], 8'h00}; - 2: ft601_data_out <= {range_profile[15:0], 16'h0000}; - 3: ft601_data_out <= {range_profile[7:0], 24'h000000}; + 0: ft601_data_out <= range_profile_cap; + 1: ft601_data_out <= {range_profile_cap[23:0], 8'h00}; + 2: ft601_data_out <= {range_profile_cap[15:0], 16'h0000}; + 3: ft601_data_out <= {range_profile_cap[7:0], 24'h000000}; endcase ft601_wr_n <= 0; @@ -120,15 +172,15 @@ always @(posedge ft601_clk_in or negedge reset_n) begin end SEND_DOPPLER_DATA: begin - if (!ft601_txe && doppler_valid) begin + if (!ft601_txe && doppler_valid_ft) begin ft601_data_oe <= 1; ft601_be <= 2'b11; case (byte_counter) - 0: ft601_data_out <= {doppler_real, doppler_imag}; - 1: ft601_data_out <= {doppler_imag, doppler_real[15:8], 8'h00}; - 2: ft601_data_out <= {doppler_real[7:0], doppler_imag[15:8], 16'h0000}; - 3: ft601_data_out <= {doppler_imag[7:0], 24'h000000}; + 0: ft601_data_out <= {doppler_real_cap, doppler_imag_cap}; + 1: ft601_data_out <= {doppler_imag_cap, doppler_real_cap[15:8], 8'h00}; + 2: ft601_data_out <= {doppler_real_cap[7:0], doppler_imag_cap[15:8], 16'h0000}; + 3: ft601_data_out <= {doppler_imag_cap[7:0], 24'h000000}; endcase ft601_wr_n <= 0; @@ -143,10 +195,10 @@ always @(posedge ft601_clk_in or negedge reset_n) begin end SEND_DETECTION_DATA: begin - if (!ft601_txe && cfar_valid) begin + if (!ft601_txe && cfar_valid_ft) begin ft601_data_oe <= 1; ft601_be <= 2'b01; - ft601_data_out <= {24'b0, 7'b0, cfar_detection}; + ft601_data_out <= {24'b0, 7'b0, cfar_detection_cap}; ft601_wr_n <= 0; current_state <= SEND_FOOTER; end