fcf3999e39
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).
553 lines
19 KiB
Verilog
553 lines
19 KiB
Verilog
`timescale 1ns / 1ps
|
|
|
|
/**
|
|
* radar_system_top.v
|
|
*
|
|
* Complete Radar System Top Module
|
|
* Integrates:
|
|
* - Radar Transmitter (PLFM chirp generation)
|
|
* - Radar Receiver (ADC interface, DDC, matched filtering, Doppler processing)
|
|
* - USB Data Interface (FT601 for high-speed data transfer)
|
|
*
|
|
* Clock domains:
|
|
* - clk_100m: System clock (100MHz)
|
|
* - clk_120m_dac: DAC clock (120MHz)
|
|
* - ft601_clk: FT601 interface clock (100MHz from FT601)
|
|
*/
|
|
|
|
module radar_system_top (
|
|
// System Clocks
|
|
input wire clk_100m, // 100MHz system clock
|
|
input wire clk_120m_dac, // 120MHz DAC clock
|
|
input wire ft601_clk_in, // FT601 clock (100MHz)
|
|
input wire reset_n, // Active-low reset
|
|
|
|
// ========== TRANSMITTER INTERFACES ==========
|
|
|
|
// DAC Interface
|
|
output wire [7:0] dac_data,
|
|
output wire dac_clk,
|
|
output wire dac_sleep,
|
|
|
|
// RF Switch Control
|
|
output wire fpga_rf_switch,
|
|
|
|
// Mixer Enables
|
|
output wire rx_mixer_en,
|
|
output wire tx_mixer_en,
|
|
|
|
// ADAR1000 Beamformer Control (via level shifters)
|
|
output wire adar_tx_load_1, adar_rx_load_1,
|
|
output wire adar_tx_load_2, adar_rx_load_2,
|
|
output wire adar_tx_load_3, adar_rx_load_3,
|
|
output wire adar_tx_load_4, adar_rx_load_4,
|
|
output wire adar_tr_1, adar_tr_2, adar_tr_3, adar_tr_4,
|
|
|
|
// Level Shifter SPI Interface (STM32F7 to ADAR1000)
|
|
input wire stm32_sclk_3v3,
|
|
input wire stm32_mosi_3v3,
|
|
output wire stm32_miso_3v3,
|
|
input wire stm32_cs_adar1_3v3, stm32_cs_adar2_3v3,
|
|
input wire stm32_cs_adar3_3v3, stm32_cs_adar4_3v3,
|
|
|
|
output wire stm32_sclk_1v8,
|
|
output wire stm32_mosi_1v8,
|
|
input wire stm32_miso_1v8,
|
|
output wire stm32_cs_adar1_1v8, stm32_cs_adar2_1v8,
|
|
output wire stm32_cs_adar3_1v8, stm32_cs_adar4_1v8,
|
|
|
|
// ========== RECEIVER INTERFACES ==========
|
|
|
|
// ADC Physical Interface (LVDS)
|
|
input wire [7:0] adc_d_p, // ADC Data P (LVDS)
|
|
input wire [7:0] adc_d_n, // ADC Data N (LVDS)
|
|
input wire adc_dco_p, // Data Clock Output P (400MHz LVDS)
|
|
input wire adc_dco_n, // Data Clock Output N (400MHz LVDS)
|
|
output wire adc_pwdn, // ADC Power Down
|
|
|
|
// ========== STM32 CONTROL INTERFACES ==========
|
|
|
|
// Chirp/Beam Control (toggle signals from STM32)
|
|
input wire stm32_new_chirp,
|
|
input wire stm32_new_elevation,
|
|
input wire stm32_new_azimuth,
|
|
input wire stm32_mixers_enable,
|
|
|
|
// ========== FT601 USB 3.0 INTERFACE ==========
|
|
|
|
// Data bus
|
|
inout wire [31:0] ft601_data, // 32-bit bidirectional data bus
|
|
output wire [3:0] ft601_be, // Byte enable (4 lanes for 32-bit mode)
|
|
|
|
// Control signals
|
|
output wire ft601_txe_n, // Transmit enable (active low)
|
|
output wire ft601_rxf_n, // Receive enable (active low)
|
|
input wire ft601_txe, // Transmit FIFO empty
|
|
input wire ft601_rxf, // Receive FIFO full
|
|
output wire ft601_wr_n, // Write strobe (active low)
|
|
output wire ft601_rd_n, // Read strobe (active low)
|
|
output wire ft601_oe_n, // Output enable (active low)
|
|
output wire ft601_siwu_n, // Send immediate / Wakeup
|
|
|
|
// FIFO flags
|
|
input wire [1:0] ft601_srb, // Selected read buffer
|
|
input wire [1:0] ft601_swb, // Selected write buffer
|
|
|
|
// Clock output (optional)
|
|
output wire ft601_clk_out,
|
|
|
|
// ========== STATUS OUTPUTS ==========
|
|
|
|
// Beam position tracking
|
|
output wire [5:0] current_elevation,
|
|
output wire [5:0] current_azimuth,
|
|
output wire [5:0] current_chirp,
|
|
output wire new_chirp_frame,
|
|
|
|
// Doppler processing outputs (for debugging)
|
|
output wire [31:0] dbg_doppler_data,
|
|
output wire dbg_doppler_valid,
|
|
output wire [4:0] dbg_doppler_bin,
|
|
output wire [5:0] dbg_range_bin,
|
|
|
|
// System status
|
|
output wire [3:0] system_status
|
|
);
|
|
|
|
// ============================================================================
|
|
// PARAMETERS
|
|
// ============================================================================
|
|
|
|
// System configuration
|
|
parameter USE_LONG_CHIRP = 1'b1; // Default to long chirp
|
|
parameter DOPPLER_ENABLE = 1'b1; // Enable Doppler processing
|
|
parameter USB_ENABLE = 1'b1; // Enable USB data transfer
|
|
|
|
// ============================================================================
|
|
// INTERNAL SIGNALS
|
|
// ============================================================================
|
|
|
|
// Clock and reset
|
|
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
|
|
wire sys_reset_ft601_n; // Reset synchronized to ft601_clk domain
|
|
|
|
// CDC: synchronized versions of async inputs for status_reg
|
|
wire stm32_mixers_enable_100m; // stm32_mixers_enable sync'd to clk_100m
|
|
wire ft601_txe_100m; // ft601_txe sync'd to clk_100m
|
|
|
|
// Transmitter internal signals
|
|
wire [7:0] tx_chirp_data;
|
|
wire tx_chirp_valid;
|
|
wire tx_chirp_done;
|
|
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; // 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;
|
|
wire rx_doppler_valid;
|
|
wire [4:0] rx_doppler_bin;
|
|
wire [5:0] rx_range_bin;
|
|
wire [31:0] rx_range_profile;
|
|
wire rx_range_valid;
|
|
wire [15:0] rx_doppler_real;
|
|
wire [15:0] rx_doppler_imag;
|
|
wire rx_doppler_data_valid;
|
|
reg rx_cfar_detection;
|
|
reg rx_cfar_valid;
|
|
|
|
// Data packing for USB
|
|
wire [31:0] usb_range_profile;
|
|
wire usb_range_valid;
|
|
wire [15:0] usb_doppler_real;
|
|
wire [15:0] usb_doppler_imag;
|
|
wire usb_doppler_valid;
|
|
wire usb_cfar_detection;
|
|
wire usb_cfar_valid;
|
|
|
|
// System status
|
|
reg [3:0] status_reg;
|
|
|
|
// ============================================================================
|
|
// CLOCK BUFFERING
|
|
// ============================================================================
|
|
|
|
BUFG bufg_100m (
|
|
.I(clk_100m),
|
|
.O(clk_100m_buf)
|
|
);
|
|
|
|
BUFG bufg_120m (
|
|
.I(clk_120m_dac),
|
|
.O(clk_120m_dac_buf)
|
|
);
|
|
|
|
BUFG bufg_ft601 (
|
|
.I(ft601_clk_in),
|
|
.O(ft601_clk_buf)
|
|
);
|
|
|
|
// Reset synchronization (clk_100m domain)
|
|
(* ASYNC_REG = "TRUE" *) reg [1:0] reset_sync;
|
|
always @(posedge clk_100m_buf or negedge reset_n) begin
|
|
if (!reset_n) begin
|
|
reset_sync <= 2'b00;
|
|
end else begin
|
|
reset_sync <= {reset_sync[0], 1'b1};
|
|
end
|
|
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.
|
|
(* ASYNC_REG = "TRUE" *) 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];
|
|
|
|
// Reset synchronization (ft601_clk domain)
|
|
// FT601 has its own asynchronous clock from the USB controller.
|
|
// All FT601-domain registers need a properly synchronized reset.
|
|
(* ASYNC_REG = "TRUE" *) reg [2:0] reset_sync_ft601; // 3-stage for better MTBF
|
|
always @(posedge ft601_clk_buf or negedge reset_n) begin
|
|
if (!reset_n) begin
|
|
reset_sync_ft601 <= 3'b000;
|
|
end else begin
|
|
reset_sync_ft601 <= {reset_sync_ft601[1:0], 1'b1};
|
|
end
|
|
end
|
|
assign sys_reset_ft601_n = reset_sync_ft601[2];
|
|
|
|
// CDC synchronizers for status_reg inputs (async -> clk_100m)
|
|
// stm32_mixers_enable is an async GPIO; ft601_txe is on ft601_clk domain
|
|
cdc_single_bit #(.STAGES(2)) cdc_mixers_en_status (
|
|
.src_clk(clk_100m_buf), // Pseudo-source for async GPIO
|
|
.dst_clk(clk_100m_buf),
|
|
.reset_n(sys_reset_n),
|
|
.src_signal(stm32_mixers_enable),
|
|
.dst_signal(stm32_mixers_enable_100m)
|
|
);
|
|
|
|
cdc_single_bit #(.STAGES(2)) cdc_ft601_txe_status (
|
|
.src_clk(ft601_clk_buf),
|
|
.dst_clk(clk_100m_buf),
|
|
.reset_n(sys_reset_n),
|
|
.src_signal(ft601_txe),
|
|
.dst_signal(ft601_txe_100m)
|
|
);
|
|
|
|
// ============================================================================
|
|
// CLOCK DOMAIN CROSSING: TRANSMITTER (120 MHz) -> SYSTEM (100 MHz)
|
|
// ============================================================================
|
|
|
|
// CDC for chirp_counter: 6-bit multi-bit Gray-code synchronizer
|
|
// Source domain is clk_120m_dac, so reset must be synchronized to that domain.
|
|
// The cdc_adc_to_processing module uses synchronous reset internally, so
|
|
// using sys_reset_120m_n (120m-synchronized) is correct for the source side.
|
|
// The destination side will sample it synchronously on dst_clk, which at worst
|
|
// delays reset deassertion by 1-2 cycles — acceptable for CDC reset.
|
|
cdc_adc_to_processing #(
|
|
.WIDTH(6),
|
|
.STAGES(3)
|
|
) cdc_chirp_counter (
|
|
.src_clk(clk_120m_dac_buf),
|
|
.dst_clk(clk_100m_buf),
|
|
.src_reset_n(sys_reset_120m_n),
|
|
.dst_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: toggle CDC (pulse on clk_120m -> pulse on clk_100m)
|
|
// new_chirp_frame is a 1-cycle pulse on clk_120m_dac. A level synchronizer
|
|
// at 100 MHz can miss it. Toggle CDC converts pulse -> level toggle,
|
|
// synchronizes the toggle, then detects edges to recover the pulse.
|
|
reg chirp_frame_toggle_120m;
|
|
always @(posedge clk_120m_dac_buf or negedge sys_reset_120m_n) begin
|
|
if (!sys_reset_120m_n)
|
|
chirp_frame_toggle_120m <= 1'b0;
|
|
else if (tx_new_chirp_frame)
|
|
chirp_frame_toggle_120m <= ~chirp_frame_toggle_120m;
|
|
end
|
|
|
|
wire chirp_frame_toggle_100m;
|
|
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(chirp_frame_toggle_120m),
|
|
.dst_signal(chirp_frame_toggle_100m)
|
|
);
|
|
|
|
reg chirp_frame_toggle_100m_prev;
|
|
always @(posedge clk_100m_buf or negedge sys_reset_n) begin
|
|
if (!sys_reset_n)
|
|
chirp_frame_toggle_100m_prev <= 1'b0;
|
|
else
|
|
chirp_frame_toggle_100m_prev <= chirp_frame_toggle_100m;
|
|
end
|
|
assign tx_new_chirp_frame_sync = chirp_frame_toggle_100m ^ chirp_frame_toggle_100m_prev;
|
|
|
|
// ============================================================================
|
|
// RADAR TRANSMITTER INSTANTIATION
|
|
// ============================================================================
|
|
|
|
radar_transmitter tx_inst (
|
|
// System Clocks
|
|
.clk_100m(clk_100m_buf),
|
|
.clk_120m_dac(clk_120m_dac_buf),
|
|
.reset_n(sys_reset_120m_n), // 120 MHz-synchronized reset for DAC-domain logic
|
|
.reset_100m_n(sys_reset_n), // 100 MHz-synchronized reset for edge detectors/CDC
|
|
|
|
// DAC Interface
|
|
.dac_data(dac_data),
|
|
.dac_clk(dac_clk),
|
|
.dac_sleep(dac_sleep),
|
|
|
|
// Mixer Enables
|
|
.rx_mixer_en(rx_mixer_en),
|
|
.tx_mixer_en(tx_mixer_en),
|
|
|
|
// STM32 Control Interface
|
|
.stm32_new_chirp(stm32_new_chirp),
|
|
.stm32_new_elevation(stm32_new_elevation),
|
|
.stm32_new_azimuth(stm32_new_azimuth),
|
|
.stm32_mixers_enable(stm32_mixers_enable),
|
|
|
|
// RF Switch Control
|
|
.fpga_rf_switch(fpga_rf_switch),
|
|
|
|
// ADAR1000 Control Interface
|
|
.adar_tx_load_1(adar_tx_load_1),
|
|
.adar_rx_load_1(adar_rx_load_1),
|
|
.adar_tx_load_2(adar_tx_load_2),
|
|
.adar_rx_load_2(adar_rx_load_2),
|
|
.adar_tx_load_3(adar_tx_load_3),
|
|
.adar_rx_load_3(adar_rx_load_3),
|
|
.adar_tx_load_4(adar_tx_load_4),
|
|
.adar_rx_load_4(adar_rx_load_4),
|
|
.adar_tr_1(adar_tr_1),
|
|
.adar_tr_2(adar_tr_2),
|
|
.adar_tr_3(adar_tr_3),
|
|
.adar_tr_4(adar_tr_4),
|
|
|
|
// Level Shifter SPI Interface
|
|
.stm32_sclk_3v3(stm32_sclk_3v3),
|
|
.stm32_mosi_3v3(stm32_mosi_3v3),
|
|
.stm32_miso_3v3(stm32_miso_3v3),
|
|
.stm32_cs_adar1_3v3(stm32_cs_adar1_3v3),
|
|
.stm32_cs_adar2_3v3(stm32_cs_adar2_3v3),
|
|
.stm32_cs_adar3_3v3(stm32_cs_adar3_3v3),
|
|
.stm32_cs_adar4_3v3(stm32_cs_adar4_3v3),
|
|
|
|
.stm32_sclk_1v8(stm32_sclk_1v8),
|
|
.stm32_mosi_1v8(stm32_mosi_1v8),
|
|
.stm32_miso_1v8(stm32_miso_1v8),
|
|
.stm32_cs_adar1_1v8(stm32_cs_adar1_1v8),
|
|
.stm32_cs_adar2_1v8(stm32_cs_adar2_1v8),
|
|
.stm32_cs_adar3_1v8(stm32_cs_adar3_1v8),
|
|
.stm32_cs_adar4_1v8(stm32_cs_adar4_1v8),
|
|
|
|
// Beam Position Tracking
|
|
.current_elevation(tx_current_elevation),
|
|
.current_azimuth(tx_current_azimuth),
|
|
.current_chirp(tx_current_chirp),
|
|
.new_chirp_frame(tx_new_chirp_frame)
|
|
);
|
|
|
|
// ============================================================================
|
|
// RADAR RECEIVER INSTANTIATION
|
|
// ============================================================================
|
|
|
|
radar_receiver_final rx_inst (
|
|
.clk(clk_100m_buf),
|
|
.reset_n(sys_reset_n),
|
|
|
|
// 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),
|
|
.adc_d_n(adc_d_n),
|
|
.adc_dco_p(adc_dco_p),
|
|
.adc_dco_n(adc_dco_n),
|
|
.adc_pwdn(adc_pwdn),
|
|
|
|
// Doppler Outputs
|
|
.doppler_output(rx_doppler_output),
|
|
.doppler_valid(rx_doppler_valid),
|
|
.doppler_bin(rx_doppler_bin),
|
|
.range_bin(rx_range_bin)
|
|
);
|
|
|
|
// ============================================================================
|
|
// DOPPLER DATA DECODING
|
|
// ============================================================================
|
|
|
|
// Decode 32-bit doppler output into real and imaginary parts
|
|
// Format: {doppler_q[15:0], doppler_i[15:0]}
|
|
assign rx_doppler_real = rx_doppler_output[15:0];
|
|
assign rx_doppler_imag = rx_doppler_output[31:16];
|
|
assign rx_doppler_data_valid = rx_doppler_valid;
|
|
|
|
// For this implementation, we'll create a simple CFAR detection simulation
|
|
// In a real system, this would come from a CFAR module
|
|
reg [7:0] cfar_counter;
|
|
reg [16:0] cfar_mag; // Approximate magnitude for threshold detection
|
|
always @(posedge clk_100m_buf or negedge sys_reset_n) begin
|
|
if (!sys_reset_n) begin
|
|
cfar_counter <= 8'd0;
|
|
rx_cfar_detection <= 1'b0;
|
|
rx_cfar_valid <= 1'b0;
|
|
cfar_mag <= 17'd0;
|
|
end else begin
|
|
rx_cfar_valid <= 1'b0;
|
|
|
|
// Simple threshold detection on doppler magnitude
|
|
if (rx_doppler_valid) begin
|
|
// Calculate approximate magnitude (|I| + |Q|)
|
|
cfar_mag = (rx_doppler_real[15] ? -rx_doppler_real : rx_doppler_real) +
|
|
(rx_doppler_imag[15] ? -rx_doppler_imag : rx_doppler_imag);
|
|
|
|
// Threshold detection
|
|
if (cfar_mag > 17'd10000) begin
|
|
rx_cfar_detection <= 1'b1;
|
|
rx_cfar_valid <= 1'b1;
|
|
cfar_counter <= cfar_counter + 1;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
// ============================================================================
|
|
// DATA PACKING FOR USB
|
|
// ============================================================================
|
|
|
|
// For range profile, we'll use the doppler data as a placeholder
|
|
// In a real system, this would come from the matched filter output
|
|
assign usb_range_profile = rx_doppler_output;
|
|
assign usb_range_valid = rx_doppler_valid;
|
|
|
|
assign usb_doppler_real = rx_doppler_real;
|
|
assign usb_doppler_imag = rx_doppler_imag;
|
|
assign usb_doppler_valid = rx_doppler_valid;
|
|
|
|
assign usb_cfar_detection = rx_cfar_detection;
|
|
assign usb_cfar_valid = rx_cfar_valid;
|
|
|
|
// ============================================================================
|
|
// USB DATA INTERFACE INSTANTIATION
|
|
// ============================================================================
|
|
|
|
usb_data_interface usb_inst (
|
|
.clk(clk_100m_buf),
|
|
.reset_n(sys_reset_n),
|
|
.ft601_reset_n(sys_reset_ft601_n), // FT601-domain synchronized reset
|
|
|
|
// Radar data inputs
|
|
.range_profile(usb_range_profile),
|
|
.range_valid(usb_range_valid),
|
|
.doppler_real(usb_doppler_real),
|
|
.doppler_imag(usb_doppler_imag),
|
|
.doppler_valid(usb_doppler_valid),
|
|
.cfar_detection(usb_cfar_detection),
|
|
.cfar_valid(usb_cfar_valid),
|
|
|
|
// FT601 Interface
|
|
.ft601_data(ft601_data),
|
|
.ft601_be(ft601_be),
|
|
.ft601_txe_n(ft601_txe_n),
|
|
.ft601_rxf_n(ft601_rxf_n),
|
|
.ft601_txe(ft601_txe),
|
|
.ft601_rxf(ft601_rxf),
|
|
.ft601_wr_n(ft601_wr_n),
|
|
.ft601_rd_n(ft601_rd_n),
|
|
.ft601_oe_n(ft601_oe_n),
|
|
.ft601_siwu_n(ft601_siwu_n),
|
|
.ft601_srb(ft601_srb),
|
|
.ft601_swb(ft601_swb),
|
|
.ft601_clk_out(ft601_clk_out),
|
|
.ft601_clk_in(ft601_clk_buf)
|
|
);
|
|
|
|
// ============================================================================
|
|
// OUTPUT ASSIGNMENTS
|
|
// ============================================================================
|
|
|
|
assign current_elevation = tx_current_elevation;
|
|
assign current_azimuth = tx_current_azimuth;
|
|
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;
|
|
assign dbg_doppler_bin = rx_doppler_bin;
|
|
assign dbg_range_bin = rx_range_bin;
|
|
|
|
// ============================================================================
|
|
// SYSTEM STATUS MONITORING
|
|
// ============================================================================
|
|
|
|
always @(posedge clk_100m_buf or negedge sys_reset_n) begin
|
|
if (!sys_reset_n) begin
|
|
status_reg <= 4'b0000;
|
|
end else begin
|
|
status_reg[0] <= stm32_mixers_enable_100m; // Mixers enabled (CDC sync'd)
|
|
status_reg[1] <= ft601_txe_100m; // USB TX ready (CDC sync'd)
|
|
status_reg[2] <= rx_doppler_valid; // Data valid
|
|
status_reg[3] <= tx_new_chirp_frame_sync; // New chirp frame (CDC-sync'd)
|
|
end
|
|
end
|
|
|
|
assign system_status = status_reg;
|
|
|
|
// ============================================================================
|
|
// DEBUG AND VERIFICATION
|
|
// ============================================================================
|
|
|
|
`ifdef SIMULATION
|
|
// Simulation-only debug monitoring
|
|
reg [31:0] debug_cycle_counter;
|
|
reg [31:0] data_packet_counter;
|
|
|
|
always @(posedge clk_100m_buf) begin
|
|
debug_cycle_counter <= debug_cycle_counter + 1;
|
|
|
|
if (tx_new_chirp_frame_sync) begin
|
|
$display("[TOP] New chirp frame started at cycle %0d", debug_cycle_counter);
|
|
end
|
|
|
|
if (rx_doppler_valid) begin
|
|
data_packet_counter <= data_packet_counter + 1;
|
|
if (data_packet_counter < 10) begin
|
|
$display("[TOP] Doppler data[%0d]: bin=%0d, range=%0d, I=%0d, Q=%0d",
|
|
data_packet_counter, rx_doppler_bin, rx_range_bin,
|
|
rx_doppler_real, rx_doppler_imag);
|
|
end
|
|
end
|
|
|
|
if (data_packet_counter == 100) begin
|
|
$display("[TOP] First 100 doppler packets processed");
|
|
end
|
|
end
|
|
`endif
|
|
|
|
endmodule |