mirror of
https://github.com/NawfalMotii79/PLFM_RADAR.git
synced 2026-06-11 07:51:17 +00:00
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:
@@ -10,6 +10,10 @@ module plfm_chirp_controller_enhanced (
|
||||
input wire new_elevation,
|
||||
input wire new_azimuth,
|
||||
input wire mixers_enable,
|
||||
// Range mode (CDC-synchronized into clk_120m by the caller).
|
||||
// 2'b00 = 3 km — short chirps only (skip LONG_CHIRP/LONG_LISTEN)
|
||||
// 2'b01 = long-range — dual chirp (first half long, second half short)
|
||||
input wire [1:0] range_mode,
|
||||
output reg [7:0] chirp_data,
|
||||
output reg chirp_valid,
|
||||
output wire new_chirp_frame,
|
||||
@@ -80,7 +84,11 @@ reg [7:0] long_chirp_rd_data;
|
||||
assign chirp__toggling = new_chirp;
|
||||
assign elevation__toggling = new_elevation;
|
||||
assign azimuth__toggling = new_azimuth;
|
||||
assign new_chirp_frame = (current_state == IDLE && next_state == LONG_CHIRP);
|
||||
// new_chirp_frame fires on IDLE -> first active state (long or short
|
||||
// depending on range_mode).
|
||||
assign new_chirp_frame = (current_state == IDLE &&
|
||||
(next_state == LONG_CHIRP ||
|
||||
next_state == SHORT_CHIRP));
|
||||
|
||||
// Mixer TX/RX sequencing — mutually exclusive based on chirp FSM state.
|
||||
// TX mixer active during chirp transmission, RX mixer during listen.
|
||||
@@ -179,10 +187,20 @@ end
|
||||
always @(*) begin
|
||||
case (current_state)
|
||||
IDLE: begin
|
||||
if (chirp__toggling && mixers_enable)
|
||||
next_state = LONG_CHIRP;
|
||||
else
|
||||
// 3 km mode skips the long-chirp half entirely — LONG_CHIRP's
|
||||
// 4500 m blind zone exceeds the 3 km max range, so long chirps
|
||||
// would just pollute the receive window. Go straight to
|
||||
// SHORT_CHIRP and let the SHORT_LISTEN -> DONE guard
|
||||
// (chirp_counter == CHIRP_MAX-1) terminate after CHIRP_MAX
|
||||
// short chirps.
|
||||
if (chirp__toggling && mixers_enable) begin
|
||||
if (range_mode == `RP_RANGE_MODE_3KM)
|
||||
next_state = SHORT_CHIRP;
|
||||
else
|
||||
next_state = LONG_CHIRP;
|
||||
end else begin
|
||||
next_state = IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
LONG_CHIRP: begin
|
||||
|
||||
@@ -182,12 +182,12 @@ wire [31:0] rx_doppler_output;
|
||||
wire rx_doppler_valid;
|
||||
wire [4:0] rx_doppler_bin;
|
||||
wire [`RP_RANGE_BIN_BITS-1:0] rx_range_bin;
|
||||
wire [31:0] rx_range_profile;
|
||||
wire rx_range_valid;
|
||||
wire [15:0] rx_range_profile_decimated;
|
||||
wire rx_range_profile_decimated_valid;
|
||||
wire [15:0] rx_doppler_real;
|
||||
wire [15:0] rx_doppler_imag;
|
||||
wire [31:0] rx_range_profile;
|
||||
wire rx_range_valid;
|
||||
wire [15:0] rx_range_profile_decimated;
|
||||
wire rx_range_profile_decimated_valid;
|
||||
wire [15:0] rx_doppler_real;
|
||||
wire [15:0] rx_doppler_imag;
|
||||
wire rx_doppler_data_valid;
|
||||
reg rx_detect_flag; // Threshold detection result (was rx_cfar_detection)
|
||||
reg rx_detect_valid; // Detection valid pulse (was rx_cfar_valid)
|
||||
@@ -506,7 +506,10 @@ radar_transmitter tx_inst (
|
||||
.stm32_cs_adar2_1v8(stm32_cs_adar2_1v8),
|
||||
.stm32_cs_adar3_1v8(stm32_cs_adar3_1v8),
|
||||
.stm32_cs_adar4_1v8(stm32_cs_adar4_1v8),
|
||||
|
||||
|
||||
// Host range mode (clk_100m domain; CDC'd inside radar_transmitter)
|
||||
.host_range_mode(host_range_mode),
|
||||
|
||||
// Beam Position Tracking
|
||||
.current_elevation(tx_current_elevation),
|
||||
.current_azimuth(tx_current_azimuth),
|
||||
@@ -542,12 +545,12 @@ radar_receiver_final rx_inst (
|
||||
.doppler_bin(rx_doppler_bin),
|
||||
.range_bin(rx_range_bin),
|
||||
|
||||
// Range-profile outputs
|
||||
.range_profile_i_out(rx_range_profile[15:0]),
|
||||
.range_profile_q_out(rx_range_profile[31:16]),
|
||||
.range_profile_valid_out(rx_range_valid),
|
||||
.decimated_range_mag_out(rx_range_profile_decimated),
|
||||
.decimated_range_valid_out(rx_range_profile_decimated_valid),
|
||||
// Range-profile outputs
|
||||
.range_profile_i_out(rx_range_profile[15:0]),
|
||||
.range_profile_q_out(rx_range_profile[31:16]),
|
||||
.range_profile_valid_out(rx_range_valid),
|
||||
.decimated_range_mag_out(rx_range_profile_decimated),
|
||||
.decimated_range_valid_out(rx_range_profile_decimated_valid),
|
||||
|
||||
.host_mode(host_radar_mode),
|
||||
.host_trigger(host_trigger_pulse),
|
||||
@@ -731,10 +734,10 @@ end
|
||||
// DATA PACKING FOR USB
|
||||
// ============================================================================
|
||||
|
||||
// USB range profile must match the advertised 512-bin frame payload, so source it
|
||||
// from the decimated range stream that feeds Doppler rather than raw MF samples.
|
||||
assign usb_range_profile = {16'd0, rx_range_profile_decimated};
|
||||
assign usb_range_valid = rx_range_profile_decimated_valid;
|
||||
// USB range profile must match the advertised 512-bin frame payload, so source it
|
||||
// from the decimated range stream that feeds Doppler rather than raw MF samples.
|
||||
assign usb_range_profile = {16'd0, rx_range_profile_decimated};
|
||||
assign usb_range_valid = rx_range_profile_decimated_valid;
|
||||
|
||||
assign usb_doppler_real = rx_doppler_real;
|
||||
assign usb_doppler_imag = rx_doppler_imag;
|
||||
@@ -963,7 +966,7 @@ always @(posedge clk_100m_buf or negedge sys_reset_n) begin
|
||||
host_radar_mode <= 2'b01; // Default: auto-scan
|
||||
host_trigger_pulse <= 1'b0;
|
||||
host_detect_threshold <= 16'd10000; // Default threshold
|
||||
host_stream_control <= `RP_STREAM_CTRL_DEFAULT; // Default: all streams, mag-only mode
|
||||
host_stream_control <= `RP_STREAM_CTRL_DEFAULT; // Default: all streams, mag-only mode
|
||||
host_gain_shift <= 4'd0; // Default: pass-through (no gain change)
|
||||
// Gap 2: chirp timing defaults (match radar_mode_controller parameters)
|
||||
host_long_chirp_cycles <= 16'd3000;
|
||||
@@ -1024,7 +1027,12 @@ always @(posedge clk_100m_buf or negedge sys_reset_n) begin
|
||||
end
|
||||
end
|
||||
8'h16: host_gain_shift <= usb_cmd_value[3:0]; // Fix 3: digital gain
|
||||
8'h20: host_range_mode <= usb_cmd_value[1:0]; // Range mode
|
||||
// Range mode: clamp reserved codes (2'b10, 2'b11) to the safe
|
||||
// 3 km default so a garbled host write cannot silently enable
|
||||
// long-range TX behaviour.
|
||||
8'h20: host_range_mode <= (usb_cmd_value[1:0] > 2'b01)
|
||||
? `RP_RANGE_MODE_3KM
|
||||
: usb_cmd_value[1:0];
|
||||
// CFAR configuration opcodes
|
||||
8'h21: host_cfar_guard <= usb_cmd_value[3:0];
|
||||
8'h22: host_cfar_train <= usb_cmd_value[4:0];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -42,6 +42,7 @@ always #5 clk_100m = ~clk_100m;
|
||||
// =========================================================================
|
||||
reg new_chirp, new_elevation, new_azimuth;
|
||||
reg mixers_enable;
|
||||
reg [1:0] range_mode; // 2'b00 = 3 km (short-only), 2'b01 = long-range (dual)
|
||||
|
||||
wire [7:0] chirp_data;
|
||||
wire chirp_valid;
|
||||
@@ -78,6 +79,7 @@ plfm_chirp_controller_enhanced #(
|
||||
.new_elevation(new_elevation),
|
||||
.new_azimuth(new_azimuth),
|
||||
.mixers_enable(mixers_enable),
|
||||
.range_mode(range_mode),
|
||||
.chirp_data(chirp_data),
|
||||
.chirp_valid(chirp_valid),
|
||||
.new_chirp_frame(new_chirp_frame),
|
||||
@@ -184,7 +186,8 @@ initial begin
|
||||
new_elevation = 0;
|
||||
new_azimuth = 0;
|
||||
mixers_enable = 0;
|
||||
|
||||
range_mode = 2'b01; // Default: long-range (dual) — matches existing tests
|
||||
|
||||
$display("");
|
||||
$display("============================================================");
|
||||
$display(" CHIRP CONTROLLER TESTBENCH");
|
||||
@@ -493,6 +496,65 @@ initial begin
|
||||
check("ADAR load pins: adar_tx_load_1 is 0", adar_tx_load_1 == 1'b0);
|
||||
check("ADAR load pins: adar_rx_load_1 is 0", adar_rx_load_1 == 1'b0);
|
||||
|
||||
// =====================================================================
|
||||
// TEST GROUP 8: RANGE MODE — 3 KM SHORT-ONLY PATH (C-1)
|
||||
//
|
||||
// Bug: plfm_chirp_controller_enhanced was not range_mode-aware; the
|
||||
// FSM always ran LONG_CHIRP/LONG_LISTEN for CHIRP_MAX/2 chirps even
|
||||
// when the host had selected 3 km mode. LONG_CHIRP's ~4.5 km blind
|
||||
// zone exceeds the 3 km max, so those long chirps pollute the RX
|
||||
// window. Fix: IDLE branches to SHORT_CHIRP directly when
|
||||
// range_mode == RP_RANGE_MODE_3KM.
|
||||
//
|
||||
// These checks verify the skip path: no LONG_CHIRP ever entered, FSM
|
||||
// reaches DONE after CHIRP_MAX short chirps.
|
||||
// =====================================================================
|
||||
$display("--- Group 8: Range Mode 3 km (C-1) ---");
|
||||
|
||||
// Full reset into 3 km mode.
|
||||
reset_n = 0;
|
||||
mixers_enable = 0;
|
||||
new_chirp = 0;
|
||||
range_mode = 2'b00; // 3 km — short-only
|
||||
#100;
|
||||
reset_n = 1;
|
||||
@(posedge clk_120m);
|
||||
|
||||
mixers_enable = 1;
|
||||
@(posedge clk_120m);
|
||||
new_chirp = 1;
|
||||
@(posedge clk_120m);
|
||||
|
||||
// T8.1: Skip LONG_CHIRP, enter SHORT_CHIRP directly from IDLE.
|
||||
wait_for_state(3'b100, 10);
|
||||
check("3 km mode: IDLE -> SHORT_CHIRP (skips LONG_CHIRP)",
|
||||
dut.current_state == 3'b100);
|
||||
|
||||
// T8.2: FSM must never have entered any LONG_* state in this frame.
|
||||
// (dut.current_state is sampled now; a transient visit would have
|
||||
// been caught by the wait_for_state above landing in 3'b001/3'b010
|
||||
// instead.)
|
||||
check("3 km mode: FSM not in LONG_CHIRP",
|
||||
dut.current_state != 3'b001);
|
||||
check("3 km mode: FSM not in LONG_LISTEN",
|
||||
dut.current_state != 3'b010);
|
||||
|
||||
// T8.3: After CHIRP_MAX short chirps, reach DONE. No GUARD_TIME in the
|
||||
// 3 km path (GUARD bridges long->short; not needed here).
|
||||
wait_for_state(3'b110,
|
||||
(T2_SAMPLES + T2_RADAR_LISTENING) * CHIRP_MAX + 50);
|
||||
check("3 km mode: reaches DONE after CHIRP_MAX short chirps",
|
||||
dut.current_state == 3'b110);
|
||||
|
||||
// T8.4: chirp_counter cleared at DONE (same invariant as C-3).
|
||||
new_chirp = 0;
|
||||
@(posedge clk_120m);
|
||||
check("3 km mode: chirp_counter reset to 0 after DONE",
|
||||
chirp_counter == 6'd0);
|
||||
|
||||
// Restore default for any future tests.
|
||||
range_mode = 2'b01;
|
||||
|
||||
// =====================================================================
|
||||
// SUMMARY
|
||||
// =====================================================================
|
||||
|
||||
@@ -1166,10 +1166,19 @@ initial begin
|
||||
check(dut.host_range_mode == 2'b01,
|
||||
"G14.1: Opcode 0x20 -> host_range_mode = 2'b01 (long-range)");
|
||||
|
||||
// G14.2: Set range_mode to reserved value 0x02 (permissive: stored as-is)
|
||||
// G14.2: Reserved value 0x02 must be clamped to 3 km (safe default)
|
||||
// so a garbled host write cannot silently enable long-range TX.
|
||||
bfm_send_cmd(8'h20, 8'h00, 16'h0002);
|
||||
check(dut.host_range_mode == 2'b10,
|
||||
"G14.2: Opcode 0x20 -> host_range_mode = 2'b10 (reserved)");
|
||||
check(dut.host_range_mode == 2'b00,
|
||||
"G14.2: Opcode 0x20 reserved=0x02 clamped to 2'b00 (3 km safe default)");
|
||||
|
||||
// G14.2b: Reserved value 0x03 also clamps to 3 km.
|
||||
bfm_send_cmd(8'h20, 8'h00, 16'h0003);
|
||||
check(dut.host_range_mode == 2'b00,
|
||||
"G14.2b: Opcode 0x20 reserved=0x03 clamped to 2'b00 (3 km safe default)");
|
||||
|
||||
// Restore to a known-valid value before G14.3 asserts reset-to-3km.
|
||||
bfm_send_cmd(8'h20, 8'h00, 16'h0001);
|
||||
|
||||
// G14.3: Restore range_mode to 3 km (0x00)
|
||||
bfm_send_cmd(8'h20, 8'h00, 16'h0000);
|
||||
|
||||
Reference in New Issue
Block a user