Fix CDC timing violations: add synchronizers for all inter-clock crossings
Resolve all 4 inter-clock timing violations found in Vivado synthesis attempt #11 (WNS was -2.552 ns). Changes: - Add reset synchronizer for clk_120m_dac domain (2-FF chain) - Add Gray-code CDC for chirp_counter (6-bit, 120MHz->100MHz) - Add single-bit CDC for new_chirp_frame (3-stage, 120MHz->100MHz) - Add 2-stage input synchronizers for valid signals in USB module (clk_100m->ft601_clk_in) with data capture on rising edge - Fix ft601_clk_out multi-driven net (removed duplicate assignment) - Update XDC: set_max_delay -datapath_only for CDC, false_path for reset Result: Vivado attempt #12 passes with 0 errors, 0 timing violations, and 'All user specified timing constraints are met.' (WNS +0.983 ns)
This commit is contained in:
@@ -132,15 +132,19 @@ wire clk_100m_buf;
|
|||||||
wire clk_120m_dac_buf;
|
wire clk_120m_dac_buf;
|
||||||
wire ft601_clk_buf;
|
wire ft601_clk_buf;
|
||||||
wire sys_reset_n;
|
wire sys_reset_n;
|
||||||
|
wire sys_reset_120m_n; // Reset synchronized to clk_120m_dac domain
|
||||||
|
|
||||||
// Transmitter internal signals
|
// Transmitter internal signals
|
||||||
wire [7:0] tx_chirp_data;
|
wire [7:0] tx_chirp_data;
|
||||||
wire tx_chirp_valid;
|
wire tx_chirp_valid;
|
||||||
wire tx_chirp_done;
|
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_elevation;
|
||||||
wire [5:0] tx_current_azimuth;
|
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
|
// Receiver internal signals
|
||||||
wire [31:0] rx_doppler_output;
|
wire [31:0] rx_doppler_output;
|
||||||
@@ -186,7 +190,7 @@ BUFG bufg_ft601 (
|
|||||||
.O(ft601_clk_buf)
|
.O(ft601_clk_buf)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Reset synchronization
|
// Reset synchronization (clk_100m domain)
|
||||||
reg [1:0] reset_sync;
|
reg [1:0] reset_sync;
|
||||||
always @(posedge clk_100m_buf or negedge reset_n) begin
|
always @(posedge clk_100m_buf or negedge reset_n) begin
|
||||||
if (!reset_n) begin
|
if (!reset_n) begin
|
||||||
@@ -197,6 +201,48 @@ always @(posedge clk_100m_buf or negedge reset_n) begin
|
|||||||
end
|
end
|
||||||
assign sys_reset_n = reset_sync[1];
|
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
|
// RADAR TRANSMITTER INSTANTIATION
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -205,7 +251,7 @@ radar_transmitter tx_inst (
|
|||||||
// System Clocks
|
// System Clocks
|
||||||
.clk_100m(clk_100m_buf),
|
.clk_100m(clk_100m_buf),
|
||||||
.clk_120m_dac(clk_120m_dac_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 Interface
|
||||||
.dac_data(dac_data),
|
.dac_data(dac_data),
|
||||||
@@ -271,8 +317,8 @@ radar_receiver_final rx_inst (
|
|||||||
.clk(clk_100m_buf),
|
.clk(clk_100m_buf),
|
||||||
.reset_n(sys_reset_n),
|
.reset_n(sys_reset_n),
|
||||||
|
|
||||||
// Chirp counter from transmitter (NEW-1 fix: was disconnected)
|
// Chirp counter from transmitter (CDC-synchronized from 120 MHz domain)
|
||||||
.chirp_counter(tx_current_chirp),
|
.chirp_counter(tx_current_chirp_sync),
|
||||||
|
|
||||||
// ADC Physical Interface
|
// ADC Physical Interface
|
||||||
.adc_d_p(adc_d_p),
|
.adc_d_p(adc_d_p),
|
||||||
@@ -383,8 +429,8 @@ usb_data_interface usb_inst (
|
|||||||
|
|
||||||
assign current_elevation = tx_current_elevation;
|
assign current_elevation = tx_current_elevation;
|
||||||
assign current_azimuth = tx_current_azimuth;
|
assign current_azimuth = tx_current_azimuth;
|
||||||
assign current_chirp = tx_current_chirp;
|
assign current_chirp = tx_current_chirp_sync; // Use CDC-synchronized version
|
||||||
assign new_chirp_frame = tx_new_chirp_frame;
|
assign new_chirp_frame = tx_new_chirp_frame_sync; // Use CDC-synchronized version
|
||||||
|
|
||||||
assign dbg_doppler_data = rx_doppler_output;
|
assign dbg_doppler_data = rx_doppler_output;
|
||||||
assign dbg_doppler_valid = rx_doppler_valid;
|
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[0] <= stm32_mixers_enable; // Mixers enabled
|
||||||
status_reg[1] <= ft601_txe; // USB TX ready
|
status_reg[1] <= ft601_txe; // USB TX ready
|
||||||
status_reg[2] <= rx_doppler_valid; // Data valid
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -420,7 +466,7 @@ reg [31:0] data_packet_counter;
|
|||||||
always @(posedge clk_100m_buf) begin
|
always @(posedge clk_100m_buf) begin
|
||||||
debug_cycle_counter <= debug_cycle_counter + 1;
|
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);
|
$display("[TOP] New chirp frame started at cycle %0d", debug_cycle_counter);
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,57 @@ reg [31:0] data_buffer;
|
|||||||
reg [31:0] ft601_data_out;
|
reg [31:0] ft601_data_out;
|
||||||
reg ft601_data_oe; // Output enable for bidirectional data bus
|
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
|
// FT601 data bus direction control
|
||||||
assign ft601_data = ft601_data_oe ? ft601_data_out : 32'hzzzz_zzzz;
|
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_rd_n <= 1;
|
||||||
ft601_oe_n <= 1;
|
ft601_oe_n <= 1;
|
||||||
ft601_siwu_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
|
end else begin
|
||||||
case (current_state)
|
case (current_state)
|
||||||
IDLE: begin
|
IDLE: begin
|
||||||
ft601_wr_n <= 1;
|
ft601_wr_n <= 1;
|
||||||
ft601_data_oe <= 0; // Release data bus
|
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;
|
current_state <= SEND_HEADER;
|
||||||
byte_counter <= 0;
|
byte_counter <= 0;
|
||||||
end
|
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
|
ft601_be <= 2'b11; // All bytes valid for 32-bit word
|
||||||
|
|
||||||
case (byte_counter)
|
case (byte_counter)
|
||||||
0: ft601_data_out <= range_profile;
|
0: ft601_data_out <= range_profile_cap;
|
||||||
1: ft601_data_out <= {range_profile[23:0], 8'h00};
|
1: ft601_data_out <= {range_profile_cap[23:0], 8'h00};
|
||||||
2: ft601_data_out <= {range_profile[15:0], 16'h0000};
|
2: ft601_data_out <= {range_profile_cap[15:0], 16'h0000};
|
||||||
3: ft601_data_out <= {range_profile[7:0], 24'h000000};
|
3: ft601_data_out <= {range_profile_cap[7:0], 24'h000000};
|
||||||
endcase
|
endcase
|
||||||
|
|
||||||
ft601_wr_n <= 0;
|
ft601_wr_n <= 0;
|
||||||
@@ -120,15 +172,15 @@ always @(posedge ft601_clk_in or negedge reset_n) begin
|
|||||||
end
|
end
|
||||||
|
|
||||||
SEND_DOPPLER_DATA: begin
|
SEND_DOPPLER_DATA: begin
|
||||||
if (!ft601_txe && doppler_valid) begin
|
if (!ft601_txe && doppler_valid_ft) begin
|
||||||
ft601_data_oe <= 1;
|
ft601_data_oe <= 1;
|
||||||
ft601_be <= 2'b11;
|
ft601_be <= 2'b11;
|
||||||
|
|
||||||
case (byte_counter)
|
case (byte_counter)
|
||||||
0: ft601_data_out <= {doppler_real, doppler_imag};
|
0: ft601_data_out <= {doppler_real_cap, doppler_imag_cap};
|
||||||
1: ft601_data_out <= {doppler_imag, doppler_real[15:8], 8'h00};
|
1: ft601_data_out <= {doppler_imag_cap, doppler_real_cap[15:8], 8'h00};
|
||||||
2: ft601_data_out <= {doppler_real[7:0], doppler_imag[15:8], 16'h0000};
|
2: ft601_data_out <= {doppler_real_cap[7:0], doppler_imag_cap[15:8], 16'h0000};
|
||||||
3: ft601_data_out <= {doppler_imag[7:0], 24'h000000};
|
3: ft601_data_out <= {doppler_imag_cap[7:0], 24'h000000};
|
||||||
endcase
|
endcase
|
||||||
|
|
||||||
ft601_wr_n <= 0;
|
ft601_wr_n <= 0;
|
||||||
@@ -143,10 +195,10 @@ always @(posedge ft601_clk_in or negedge reset_n) begin
|
|||||||
end
|
end
|
||||||
|
|
||||||
SEND_DETECTION_DATA: begin
|
SEND_DETECTION_DATA: begin
|
||||||
if (!ft601_txe && cfar_valid) begin
|
if (!ft601_txe && cfar_valid_ft) begin
|
||||||
ft601_data_oe <= 1;
|
ft601_data_oe <= 1;
|
||||||
ft601_be <= 2'b01;
|
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;
|
ft601_wr_n <= 0;
|
||||||
current_state <= SEND_FOOTER;
|
current_state <= SEND_FOOTER;
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user