fix(fpga): TX range-mode awareness + clamp reserved host codes

C-1: plfm_chirp_controller was not range_mode-aware; it always ran
LONG_CHIRP for CHIRP_MAX/2 chirps even when the host selected 3 km
mode, where the ~4.5 km blind zone exceeds the 3 km max range and
pollutes the RX window. IDLE now branches straight to SHORT_CHIRP
when range_mode == RP_RANGE_MODE_3KM. host_range_mode is passed from
radar_system_top through radar_transmitter, CDC'd per-bit from
clk_100m to clk_120m_dac (coherency-safe: reserved codes are clamped
at the source so only bit[0] toggles).

S-3: opcode 0x20 now clamps reserved range-mode codes (2'b10, 2'b11)
to the 3 km default so a garbled host write cannot silently enable
long-range TX behaviour.

Regression: tb_chirp_controller adds a 3 km-mode group (5 checks)
verifying IDLE->SHORT_CHIRP skip path and DONE after CHIRP_MAX short
chirps; tb_system_e2e G14 labels updated for clamped reserved codes.
32/32 regression PASS (50/50 on chirp TB).
This commit is contained in:
Jason
2026-04-22 20:26:43 +05:45
parent 27c9c22ad2
commit 6af79f9c74
5 changed files with 228 additions and 105 deletions
+104 -78
View File
@@ -23,60 +23,65 @@ module radar_transmitter(
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,
output wire dac_clk,
output wire dac_sleep,
output wire rx_mixer_en,
input wire reset_100m_n, // Reset synchronized to clk_100m (for edge detectors/CDC)
// DAC Interface
output wire [7:0] dac_data,
output wire dac_clk,
output wire dac_sleep,
output wire rx_mixer_en,
output wire tx_mixer_en,
// STM32 Control Interface
input wire stm32_new_chirp,
input wire stm32_new_elevation,
input wire stm32_new_azimuth,
// STM32 Control Interface
input wire stm32_new_chirp,
input wire stm32_new_elevation,
input wire stm32_new_azimuth,
input wire stm32_mixers_enable,
// Range mode from host (clk_100m domain, opcode 0x20). CDC'd to clk_120m_dac
// internally and fed to plfm_chirp_controller_enhanced so 3 km mode skips
// the long-chirp half of the waveform entirely.
input wire [1:0] host_range_mode,
output wire fpga_rf_switch,
// ADAR1000 Control Interface
output wire adar_tx_load_1,
output wire adar_rx_load_1,
output wire adar_tx_load_2,
output wire adar_rx_load_2,
output wire adar_tx_load_3,
output wire adar_rx_load_3,
output wire adar_tx_load_4,
output wire adar_rx_load_4,
output wire adar_tr_1,
output wire adar_tr_2,
output wire adar_tr_3,
output wire 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,
input wire stm32_cs_adar2_3v3,
input wire stm32_cs_adar3_3v3,
input wire 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,
output wire stm32_cs_adar2_1v8,
output wire stm32_cs_adar3_1v8,
// ADAR1000 Control Interface
output wire adar_tx_load_1,
output wire adar_rx_load_1,
output wire adar_tx_load_2,
output wire adar_rx_load_2,
output wire adar_tx_load_3,
output wire adar_rx_load_3,
output wire adar_tx_load_4,
output wire adar_rx_load_4,
output wire adar_tr_1,
output wire adar_tr_2,
output wire adar_tr_3,
output wire 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,
input wire stm32_cs_adar2_3v3,
input wire stm32_cs_adar3_3v3,
input wire 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,
output wire stm32_cs_adar2_1v8,
output wire stm32_cs_adar3_1v8,
output wire stm32_cs_adar4_1v8,
// Beam Position Tracking
output wire [5:0] current_elevation,
output wire [5:0] current_azimuth,
// 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
output wire new_chirp_frame
);
@@ -143,6 +148,26 @@ always @(posedge clk_120m_dac or negedge reset_n) begin
end
assign new_chirp_pulse_120m = chirp_toggle_120m ^ chirp_toggle_120m_prev;
// Sync host_range_mode (clk_100m level) to clk_120m_dac domain.
// Only bit[0] toggles between the two valid codes (2'b00 / 2'b01) since
// reserved codes are clamped at the source, so per-bit 2FF synchronization
// has no coherency hazard.
wire [1:0] range_mode_120m;
cdc_single_bit #(.STAGES(2)) cdc_range_mode_bit0 (
.src_clk(clk_100m),
.dst_clk(clk_120m_dac),
.reset_n(reset_n),
.src_signal(host_range_mode[0]),
.dst_signal(range_mode_120m[0])
);
cdc_single_bit #(.STAGES(2)) cdc_range_mode_bit1 (
.src_clk(clk_100m),
.dst_clk(clk_120m_dac),
.reset_n(reset_n),
.src_signal(host_range_mode[1]),
.dst_signal(range_mode_120m[1])
);
// Sync stm32_mixers_enable (async GPIO level) to clk_120m_dac domain
cdc_single_bit #(.STAGES(3)) cdc_mixers_en_120m (
.src_clk(clk_100m), // Treat as pseudo-source (GPIO is async)
@@ -150,7 +175,7 @@ cdc_single_bit #(.STAGES(3)) cdc_mixers_en_120m (
.reset_n(reset_n),
.src_signal(stm32_mixers_enable),
.dst_signal(mixers_enable_120m)
);
);
// CDC synchronizers: async STM32 GPIO inputs -> clk_100m domain
// These prevent metastability in the edge detectors. Without these,
@@ -201,7 +226,7 @@ edge_detector_enhanced azimuth_edge (
.reset_n(reset_100m_n),
.signal_in(stm32_new_azimuth_sync),
.rising_falling_edge(new_azimuth_pulse)
);
);
// Enhanced PLFM Chirp Generation
plfm_chirp_controller_enhanced plfm_chirp_inst (
@@ -212,38 +237,39 @@ plfm_chirp_controller_enhanced plfm_chirp_inst (
.new_elevation(new_elevation_pulse),
.new_azimuth(new_azimuth_pulse),
.new_chirp_frame(new_chirp_frame),
.mixers_enable(mixers_enable_120m), // CDC-synchronized level in clk_120m domain
.chirp_data(chirp_data),
.chirp_valid(chirp_valid),
.chirp_done(chirp_sequence_done),
.rf_switch_ctrl(fpga_rf_switch),
.rx_mixer_en(rx_mixer_en),
.tx_mixer_en(tx_mixer_en),
.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),
.elevation_counter(current_elevation),
.azimuth_counter(current_azimuth),
.chirp_counter(current_chirp)
.mixers_enable(mixers_enable_120m), // CDC-synchronized level in clk_120m domain
.range_mode(range_mode_120m), // CDC-synchronized range mode in clk_120m domain
.chirp_data(chirp_data),
.chirp_valid(chirp_valid),
.chirp_done(chirp_sequence_done),
.rf_switch_ctrl(fpga_rf_switch),
.rx_mixer_en(rx_mixer_en),
.tx_mixer_en(tx_mixer_en),
.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),
.elevation_counter(current_elevation),
.azimuth_counter(current_azimuth),
.chirp_counter(current_chirp)
);
// Enhanced DAC Interface
dac_interface_enhanced dac_interface_inst (
.clk_120m(clk_120m_dac),
.reset_n(reset_n),
.chirp_data(chirp_data),
.chirp_valid(chirp_valid),
.dac_data(dac_data),
.dac_clk(dac_clk),
.dac_sleep(dac_sleep)
// Enhanced DAC Interface
dac_interface_enhanced dac_interface_inst (
.clk_120m(clk_120m_dac),
.reset_n(reset_n),
.chirp_data(chirp_data),
.chirp_valid(chirp_valid),
.dac_data(dac_data),
.dac_clk(dac_clk),
.dac_sleep(dac_sleep)
);
endmodule