Integrate CA-CFAR detector: replace fixed-threshold comparator with adaptive sliding-window CFAR engine (22/22 regression PASS)
- Add cfar_ca.v: CA/GO/SO-CFAR with BRAM magnitude buffer, host-configurable guard cells, training cells, alpha multiplier, and mode selection - Replace old threshold detector block in radar_system_top.v with cfar_ca instantiation; backward-compatible (cfar_enable defaults to 0) - Add 5 new host registers: guard (0x21), train (0x22), alpha (0x23), mode (0x24), enable (0x25) - Expose doppler_frame_done_out from radar_receiver_final for CFAR frame sync - Add tb_cfar_ca.v standalone testbench (14 tests, 24 checks) - Add Group 14 E2E tests: 13 checks covering range-mode (0x20) and all CFAR config registers (0x21-0x25) through full USB command path - Update run_regression.sh with CFAR in lint, Phase 1, and integration compiles
This commit is contained in:
@@ -164,6 +164,9 @@ 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)
|
||||
|
||||
// Frame-complete signal from Doppler processor (for CFAR)
|
||||
wire rx_frame_complete;
|
||||
|
||||
// Data packing for USB
|
||||
wire [31:0] usb_range_profile;
|
||||
wire usb_range_valid;
|
||||
@@ -224,6 +227,13 @@ reg chirps_mismatch_error; // Set if host tried to set chirps != FFT
|
||||
// Currently a configuration store only — antenna/timing switching TBD.
|
||||
reg [1:0] host_range_mode;
|
||||
|
||||
// CFAR configuration registers (host-configurable via USB)
|
||||
reg [3:0] host_cfar_guard; // Opcode 0x21: guard cells per side (0..8)
|
||||
reg [4:0] host_cfar_train; // Opcode 0x22: training cells per side (1..16)
|
||||
reg [7:0] host_cfar_alpha; // Opcode 0x23: threshold multiplier (Q4.4)
|
||||
reg [1:0] host_cfar_mode; // Opcode 0x24: 00=CA, 01=GO, 10=SO
|
||||
reg host_cfar_enable; // Opcode 0x25: 1=CFAR, 0=simple threshold
|
||||
|
||||
// ============================================================================
|
||||
// CLOCK BUFFERING
|
||||
// ============================================================================
|
||||
@@ -474,7 +484,9 @@ radar_receiver_final rx_inst (
|
||||
// (inside radar_mode_controller) handle debouncing/edge detection.
|
||||
.stm32_new_chirp_rx(stm32_new_chirp),
|
||||
.stm32_new_elevation_rx(stm32_new_elevation),
|
||||
.stm32_new_azimuth_rx(stm32_new_azimuth)
|
||||
.stm32_new_azimuth_rx(stm32_new_azimuth),
|
||||
// CFAR: Doppler frame-complete pulse
|
||||
.doppler_frame_done_out(rx_frame_complete)
|
||||
);
|
||||
|
||||
// ============================================================================
|
||||
@@ -488,44 +500,64 @@ assign rx_doppler_imag = rx_doppler_output[31:16];
|
||||
assign rx_doppler_data_valid = rx_doppler_valid;
|
||||
|
||||
// ============================================================================
|
||||
// THRESHOLD DETECTOR (renamed from misleading "CFAR" — this is NOT CFAR)
|
||||
// CFAR DETECTOR (replaces simple threshold detector)
|
||||
// ============================================================================
|
||||
// Simple magnitude threshold: |I|+|Q| > host_detect_threshold
|
||||
// This is a placeholder until real CFAR (Gap 1) is implemented.
|
||||
//
|
||||
// BUG FIXES applied (Build 22):
|
||||
// 1. cfar_mag was registered (<=) then compared in same always block,
|
||||
// causing one-cycle-lag: comparison used PREVIOUS sample's magnitude.
|
||||
// FIX: compute magnitude combinationally (wire), compare same cycle.
|
||||
// 2. rx_cfar_detection was never cleared on non-detect cycles — stayed
|
||||
// latched high after first detection until system reset.
|
||||
// FIX: clear detection flag every cycle, set only on actual detect.
|
||||
// Cell-Averaging CFAR with CA/GO/SO modes. When cfg_cfar_enable=0,
|
||||
// falls back to simple magnitude threshold (backward-compatible).
|
||||
// See cfar_ca.v for architecture details.
|
||||
|
||||
// Combinational magnitude: no pipeline lag
|
||||
wire [16:0] detect_mag;
|
||||
wire [15:0] detect_abs_i = rx_doppler_real[15] ? (~rx_doppler_real + 16'd1) : rx_doppler_real;
|
||||
wire [15:0] detect_abs_q = rx_doppler_imag[15] ? (~rx_doppler_imag + 16'd1) : rx_doppler_imag;
|
||||
assign detect_mag = {1'b0, detect_abs_i} + {1'b0, detect_abs_q};
|
||||
wire cfar_detect_flag;
|
||||
wire cfar_detect_valid;
|
||||
wire [5:0] cfar_detect_range;
|
||||
wire [4:0] cfar_detect_doppler;
|
||||
wire [16:0] cfar_detect_magnitude;
|
||||
wire [16:0] cfar_detect_threshold;
|
||||
wire [15:0] cfar_detect_count;
|
||||
wire cfar_busy_w;
|
||||
wire [7:0] cfar_status_w;
|
||||
|
||||
reg [7:0] detect_counter;
|
||||
cfar_ca cfar_inst (
|
||||
.clk(clk_100m_buf),
|
||||
.reset_n(sys_reset_n),
|
||||
|
||||
// Doppler processor outputs
|
||||
.doppler_data(rx_doppler_output),
|
||||
.doppler_valid(rx_doppler_valid),
|
||||
.doppler_bin_in(rx_doppler_bin),
|
||||
.range_bin_in(rx_range_bin),
|
||||
.frame_complete(rx_frame_complete),
|
||||
|
||||
// Configuration
|
||||
.cfg_guard_cells(host_cfar_guard),
|
||||
.cfg_train_cells(host_cfar_train),
|
||||
.cfg_alpha(host_cfar_alpha),
|
||||
.cfg_cfar_mode(host_cfar_mode),
|
||||
.cfg_cfar_enable(host_cfar_enable),
|
||||
.cfg_simple_threshold(host_detect_threshold),
|
||||
|
||||
// Detection outputs
|
||||
.detect_flag(cfar_detect_flag),
|
||||
.detect_valid(cfar_detect_valid),
|
||||
.detect_range(cfar_detect_range),
|
||||
.detect_doppler(cfar_detect_doppler),
|
||||
.detect_magnitude(cfar_detect_magnitude),
|
||||
.detect_threshold(cfar_detect_threshold),
|
||||
|
||||
// Status
|
||||
.detect_count(cfar_detect_count),
|
||||
.cfar_busy(cfar_busy_w),
|
||||
.cfar_status(cfar_status_w)
|
||||
);
|
||||
|
||||
// Connect CFAR outputs to existing detection signals
|
||||
// (rx_detect_flag/valid are regs — drive them from CFAR combinationally)
|
||||
always @(posedge clk_100m_buf or negedge sys_reset_n) begin
|
||||
if (!sys_reset_n) begin
|
||||
detect_counter <= 8'd0;
|
||||
rx_detect_flag <= 1'b0;
|
||||
rx_detect_valid <= 1'b0;
|
||||
end else begin
|
||||
// Default: clear every cycle (fixes sticky detection bug)
|
||||
rx_detect_flag <= 1'b0;
|
||||
rx_detect_valid <= 1'b0;
|
||||
|
||||
if (rx_doppler_valid) begin
|
||||
// Compare combinational magnitude against threshold (same cycle)
|
||||
if (detect_mag > {1'b0, host_detect_threshold}) begin
|
||||
rx_detect_flag <= 1'b1;
|
||||
rx_detect_valid <= 1'b1;
|
||||
detect_counter <= detect_counter + 1;
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
rx_detect_flag <= cfar_detect_flag;
|
||||
rx_detect_valid <= cfar_detect_valid;
|
||||
end
|
||||
end
|
||||
|
||||
@@ -665,6 +697,12 @@ always @(posedge clk_100m_buf or negedge sys_reset_n) begin
|
||||
host_status_request <= 1'b0;
|
||||
chirps_mismatch_error <= 1'b0;
|
||||
host_range_mode <= 2'b00; // Default: auto
|
||||
// CFAR defaults (disabled by default — backward-compatible)
|
||||
host_cfar_guard <= 4'd2; // 2 guard cells each side
|
||||
host_cfar_train <= 5'd8; // 8 training cells each side
|
||||
host_cfar_alpha <= 8'h30; // alpha=3.0 (Q4.4)
|
||||
host_cfar_mode <= 2'b00; // CA-CFAR
|
||||
host_cfar_enable <= 1'b0; // Disabled (simple threshold)
|
||||
end else begin
|
||||
host_trigger_pulse <= 1'b0; // Self-clearing pulse
|
||||
host_status_request <= 1'b0; // Self-clearing pulse
|
||||
@@ -697,6 +735,12 @@ always @(posedge clk_100m_buf or negedge sys_reset_n) begin
|
||||
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]; // Fix 7: range mode
|
||||
// CFAR configuration opcodes
|
||||
8'h21: host_cfar_guard <= usb_cmd_value[3:0];
|
||||
8'h22: host_cfar_train <= usb_cmd_value[4:0];
|
||||
8'h23: host_cfar_alpha <= usb_cmd_value[7:0];
|
||||
8'h24: host_cfar_mode <= usb_cmd_value[1:0];
|
||||
8'h25: host_cfar_enable <= usb_cmd_value[0];
|
||||
8'hFF: host_status_request <= 1'b1; // Gap 2: status readback
|
||||
default: ;
|
||||
endcase
|
||||
|
||||
Reference in New Issue
Block a user