Add 3 missing FPGA modules with enhanced testbenches (168/168 pass)
Implement the 3 modules identified as missing during repo audit: - matched_filter_processing_chain: behavioral FFT-based pulse compression - range_bin_decimator: 1024→64 bin decimation with 3 modes + start_bin - radar_mode_controller: 4-mode beam/chirp controller Wire radar_mode_controller into radar_receiver_final.v to drive the previously-undriven use_long_chirp and mc_new_* signals. Implement start_bin functionality in range_bin_decimator (was dead code in the original interface contract — now skips N input bins before decimation for region-of-interest selection). Add comprehensive testbenches with Tier 1 confidence improvements: - Golden reference co-simulation (Python FFT → hex → bin comparison) - Saturation boundary tests (0x7FFF / 0x8000 extremes) - Reset mid-operation recovery tests - Valid-gap / stall handling tests - Mode switching and counter persistence tests - Accumulator overflow stress tests Test counts: matched_filter 40/40, range_bin_decimator 55/55, radar_mode_controller 73/73 — all passing with iverilog -g2001.
This commit is contained in:
@@ -0,0 +1,371 @@
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/**
|
||||
* radar_mode_controller.v
|
||||
*
|
||||
* Generates beam scanning and chirp mode control signals for the AERIS-10
|
||||
* receiver processing chain. This module drives:
|
||||
* - use_long_chirp : selects long (30us) or short (0.5us) chirp mode
|
||||
* - mc_new_chirp : toggle signal indicating new chirp start
|
||||
* - mc_new_elevation : toggle signal indicating elevation step
|
||||
* - mc_new_azimuth : toggle signal indicating azimuth step
|
||||
*
|
||||
* These signals are consumed by matched_filter_multi_segment and
|
||||
* chirp_memory_loader_param in the receiver path.
|
||||
*
|
||||
* The controller mirrors the transmitter's chirp sequence defined in
|
||||
* plfm_chirp_controller_enhanced:
|
||||
* - 32 chirps per elevation
|
||||
* - 31 elevations per azimuth
|
||||
* - 50 azimuths per full scan
|
||||
* - Each chirp: Long chirp → Listen → Guard → Short chirp → Listen
|
||||
*
|
||||
* Modes of operation:
|
||||
* mode[1:0]:
|
||||
* 2'b00 = STM32-driven (pass through stm32 toggle signals)
|
||||
* 2'b01 = Free-running auto-scan (internal timing)
|
||||
* 2'b10 = Single-chirp (fire one chirp per trigger, for debug)
|
||||
* 2'b11 = Reserved
|
||||
*
|
||||
* Clock domain: clk (100 MHz)
|
||||
*/
|
||||
|
||||
module radar_mode_controller #(
|
||||
parameter CHIRPS_PER_ELEVATION = 32,
|
||||
parameter ELEVATIONS_PER_AZIMUTH = 31,
|
||||
parameter AZIMUTHS_PER_SCAN = 50,
|
||||
|
||||
// Timing in 100 MHz clock cycles
|
||||
// Long chirp: 30us = 3000 cycles at 100 MHz
|
||||
// Long listen: 137us = 13700 cycles
|
||||
// Guard: 175.4us = 17540 cycles
|
||||
// Short chirp: 0.5us = 50 cycles
|
||||
// Short listen: 174.5us = 17450 cycles
|
||||
parameter LONG_CHIRP_CYCLES = 3000,
|
||||
parameter LONG_LISTEN_CYCLES = 13700,
|
||||
parameter GUARD_CYCLES = 17540,
|
||||
parameter SHORT_CHIRP_CYCLES = 50,
|
||||
parameter SHORT_LISTEN_CYCLES = 17450
|
||||
) (
|
||||
input wire clk,
|
||||
input wire reset_n,
|
||||
|
||||
// Mode selection
|
||||
input wire [1:0] mode, // 00=STM32, 01=auto, 10=single, 11=rsvd
|
||||
|
||||
// STM32 pass-through inputs (active in mode 00)
|
||||
input wire stm32_new_chirp,
|
||||
input wire stm32_new_elevation,
|
||||
input wire stm32_new_azimuth,
|
||||
|
||||
// Single-chirp trigger (active in mode 10)
|
||||
input wire trigger,
|
||||
|
||||
// Outputs to receiver processing chain
|
||||
output reg use_long_chirp,
|
||||
output reg mc_new_chirp,
|
||||
output reg mc_new_elevation,
|
||||
output reg mc_new_azimuth,
|
||||
|
||||
// Beam position tracking
|
||||
output reg [5:0] chirp_count,
|
||||
output reg [5:0] elevation_count,
|
||||
output reg [5:0] azimuth_count,
|
||||
|
||||
// Status
|
||||
output wire scanning, // 1 = scan in progress
|
||||
output wire scan_complete // pulse when full scan done
|
||||
);
|
||||
|
||||
// ============================================================================
|
||||
// INTERNAL STATE
|
||||
// ============================================================================
|
||||
|
||||
// Auto-scan state machine
|
||||
reg [2:0] scan_state;
|
||||
localparam S_IDLE = 3'd0;
|
||||
localparam S_LONG_CHIRP = 3'd1;
|
||||
localparam S_LONG_LISTEN = 3'd2;
|
||||
localparam S_GUARD = 3'd3;
|
||||
localparam S_SHORT_CHIRP = 3'd4;
|
||||
localparam S_SHORT_LISTEN = 3'd5;
|
||||
localparam S_ADVANCE = 3'd6;
|
||||
|
||||
// Timing counter
|
||||
reg [17:0] timer; // enough for up to 262143 cycles (~2.6ms at 100 MHz)
|
||||
|
||||
// Edge detection for STM32 pass-through
|
||||
reg stm32_new_chirp_prev;
|
||||
reg stm32_new_elevation_prev;
|
||||
reg stm32_new_azimuth_prev;
|
||||
|
||||
// Trigger edge detection (for single-chirp mode)
|
||||
reg trigger_prev;
|
||||
wire trigger_pulse = trigger & ~trigger_prev;
|
||||
|
||||
// Scan completion
|
||||
reg scan_done_pulse;
|
||||
|
||||
// ============================================================================
|
||||
// EDGE DETECTION
|
||||
// ============================================================================
|
||||
always @(posedge clk or negedge reset_n) begin
|
||||
if (!reset_n) begin
|
||||
stm32_new_chirp_prev <= 1'b0;
|
||||
stm32_new_elevation_prev <= 1'b0;
|
||||
stm32_new_azimuth_prev <= 1'b0;
|
||||
trigger_prev <= 1'b0;
|
||||
end else begin
|
||||
stm32_new_chirp_prev <= stm32_new_chirp;
|
||||
stm32_new_elevation_prev <= stm32_new_elevation;
|
||||
stm32_new_azimuth_prev <= stm32_new_azimuth;
|
||||
trigger_prev <= trigger;
|
||||
end
|
||||
end
|
||||
|
||||
wire stm32_chirp_toggle = stm32_new_chirp ^ stm32_new_chirp_prev;
|
||||
wire stm32_elevation_toggle = stm32_new_elevation ^ stm32_new_elevation_prev;
|
||||
wire stm32_azimuth_toggle = stm32_new_azimuth ^ stm32_new_azimuth_prev;
|
||||
|
||||
// ============================================================================
|
||||
// MAIN STATE MACHINE
|
||||
// ============================================================================
|
||||
always @(posedge clk or negedge reset_n) begin
|
||||
if (!reset_n) begin
|
||||
scan_state <= S_IDLE;
|
||||
timer <= 18'd0;
|
||||
use_long_chirp <= 1'b1;
|
||||
mc_new_chirp <= 1'b0;
|
||||
mc_new_elevation <= 1'b0;
|
||||
mc_new_azimuth <= 1'b0;
|
||||
chirp_count <= 6'd0;
|
||||
elevation_count <= 6'd0;
|
||||
azimuth_count <= 6'd0;
|
||||
scan_done_pulse <= 1'b0;
|
||||
end else begin
|
||||
// Clear one-shot signals
|
||||
scan_done_pulse <= 1'b0;
|
||||
|
||||
case (mode)
|
||||
// ================================================================
|
||||
// MODE 00: STM32-driven pass-through
|
||||
// The STM32 firmware controls timing; we just detect toggle edges
|
||||
// and forward them to the receiver chain.
|
||||
// ================================================================
|
||||
2'b00: begin
|
||||
// Reset auto-scan state
|
||||
scan_state <= S_IDLE;
|
||||
timer <= 18'd0;
|
||||
|
||||
// Pass through toggle signals
|
||||
if (stm32_chirp_toggle) begin
|
||||
mc_new_chirp <= ~mc_new_chirp; // Toggle output
|
||||
use_long_chirp <= 1'b1; // Default to long chirp
|
||||
|
||||
// Track chirp count
|
||||
if (chirp_count < CHIRPS_PER_ELEVATION - 1)
|
||||
chirp_count <= chirp_count + 1;
|
||||
else
|
||||
chirp_count <= 6'd0;
|
||||
end
|
||||
|
||||
if (stm32_elevation_toggle) begin
|
||||
mc_new_elevation <= ~mc_new_elevation;
|
||||
chirp_count <= 6'd0;
|
||||
|
||||
if (elevation_count < ELEVATIONS_PER_AZIMUTH - 1)
|
||||
elevation_count <= elevation_count + 1;
|
||||
else
|
||||
elevation_count <= 6'd0;
|
||||
end
|
||||
|
||||
if (stm32_azimuth_toggle) begin
|
||||
mc_new_azimuth <= ~mc_new_azimuth;
|
||||
elevation_count <= 6'd0;
|
||||
|
||||
if (azimuth_count < AZIMUTHS_PER_SCAN - 1)
|
||||
azimuth_count <= azimuth_count + 1;
|
||||
else begin
|
||||
azimuth_count <= 6'd0;
|
||||
scan_done_pulse <= 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// ================================================================
|
||||
// MODE 01: Free-running auto-scan
|
||||
// Internally generates chirp timing matching the transmitter.
|
||||
// ================================================================
|
||||
2'b01: begin
|
||||
case (scan_state)
|
||||
S_IDLE: begin
|
||||
// Start first chirp immediately
|
||||
scan_state <= S_LONG_CHIRP;
|
||||
timer <= 18'd0;
|
||||
use_long_chirp <= 1'b1;
|
||||
mc_new_chirp <= ~mc_new_chirp; // Toggle to start chirp
|
||||
chirp_count <= 6'd0;
|
||||
elevation_count <= 6'd0;
|
||||
azimuth_count <= 6'd0;
|
||||
|
||||
`ifdef SIMULATION
|
||||
$display("[MODE_CTRL] Auto-scan starting");
|
||||
`endif
|
||||
end
|
||||
|
||||
S_LONG_CHIRP: begin
|
||||
use_long_chirp <= 1'b1;
|
||||
if (timer < LONG_CHIRP_CYCLES - 1)
|
||||
timer <= timer + 1;
|
||||
else begin
|
||||
timer <= 18'd0;
|
||||
scan_state <= S_LONG_LISTEN;
|
||||
end
|
||||
end
|
||||
|
||||
S_LONG_LISTEN: begin
|
||||
if (timer < LONG_LISTEN_CYCLES - 1)
|
||||
timer <= timer + 1;
|
||||
else begin
|
||||
timer <= 18'd0;
|
||||
scan_state <= S_GUARD;
|
||||
end
|
||||
end
|
||||
|
||||
S_GUARD: begin
|
||||
if (timer < GUARD_CYCLES - 1)
|
||||
timer <= timer + 1;
|
||||
else begin
|
||||
timer <= 18'd0;
|
||||
scan_state <= S_SHORT_CHIRP;
|
||||
use_long_chirp <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
S_SHORT_CHIRP: begin
|
||||
use_long_chirp <= 1'b0;
|
||||
if (timer < SHORT_CHIRP_CYCLES - 1)
|
||||
timer <= timer + 1;
|
||||
else begin
|
||||
timer <= 18'd0;
|
||||
scan_state <= S_SHORT_LISTEN;
|
||||
end
|
||||
end
|
||||
|
||||
S_SHORT_LISTEN: begin
|
||||
if (timer < SHORT_LISTEN_CYCLES - 1)
|
||||
timer <= timer + 1;
|
||||
else begin
|
||||
timer <= 18'd0;
|
||||
scan_state <= S_ADVANCE;
|
||||
end
|
||||
end
|
||||
|
||||
S_ADVANCE: begin
|
||||
// Advance chirp/elevation/azimuth counters
|
||||
if (chirp_count < CHIRPS_PER_ELEVATION - 1) begin
|
||||
// Next chirp in current elevation
|
||||
chirp_count <= chirp_count + 1;
|
||||
mc_new_chirp <= ~mc_new_chirp;
|
||||
scan_state <= S_LONG_CHIRP;
|
||||
use_long_chirp <= 1'b1;
|
||||
end else begin
|
||||
chirp_count <= 6'd0;
|
||||
|
||||
if (elevation_count < ELEVATIONS_PER_AZIMUTH - 1) begin
|
||||
// Next elevation
|
||||
elevation_count <= elevation_count + 1;
|
||||
mc_new_chirp <= ~mc_new_chirp;
|
||||
mc_new_elevation <= ~mc_new_elevation;
|
||||
scan_state <= S_LONG_CHIRP;
|
||||
use_long_chirp <= 1'b1;
|
||||
end else begin
|
||||
elevation_count <= 6'd0;
|
||||
|
||||
if (azimuth_count < AZIMUTHS_PER_SCAN - 1) begin
|
||||
// Next azimuth
|
||||
azimuth_count <= azimuth_count + 1;
|
||||
mc_new_chirp <= ~mc_new_chirp;
|
||||
mc_new_elevation <= ~mc_new_elevation;
|
||||
mc_new_azimuth <= ~mc_new_azimuth;
|
||||
scan_state <= S_LONG_CHIRP;
|
||||
use_long_chirp <= 1'b1;
|
||||
end else begin
|
||||
// Full scan complete — restart
|
||||
azimuth_count <= 6'd0;
|
||||
scan_done_pulse <= 1'b1;
|
||||
mc_new_chirp <= ~mc_new_chirp;
|
||||
mc_new_elevation <= ~mc_new_elevation;
|
||||
mc_new_azimuth <= ~mc_new_azimuth;
|
||||
scan_state <= S_LONG_CHIRP;
|
||||
use_long_chirp <= 1'b1;
|
||||
|
||||
`ifdef SIMULATION
|
||||
$display("[MODE_CTRL] Full scan complete, restarting");
|
||||
`endif
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
default: scan_state <= S_IDLE;
|
||||
endcase
|
||||
end
|
||||
|
||||
// ================================================================
|
||||
// MODE 10: Single-chirp (debug mode)
|
||||
// Fire one long chirp per trigger pulse, no scanning.
|
||||
// ================================================================
|
||||
2'b10: begin
|
||||
case (scan_state)
|
||||
S_IDLE: begin
|
||||
if (trigger_pulse) begin
|
||||
scan_state <= S_LONG_CHIRP;
|
||||
timer <= 18'd0;
|
||||
use_long_chirp <= 1'b1;
|
||||
mc_new_chirp <= ~mc_new_chirp;
|
||||
end
|
||||
end
|
||||
|
||||
S_LONG_CHIRP: begin
|
||||
if (timer < LONG_CHIRP_CYCLES - 1)
|
||||
timer <= timer + 1;
|
||||
else begin
|
||||
timer <= 18'd0;
|
||||
scan_state <= S_LONG_LISTEN;
|
||||
end
|
||||
end
|
||||
|
||||
S_LONG_LISTEN: begin
|
||||
if (timer < LONG_LISTEN_CYCLES - 1)
|
||||
timer <= timer + 1;
|
||||
else begin
|
||||
// Single chirp done, return to idle
|
||||
timer <= 18'd0;
|
||||
scan_state <= S_IDLE;
|
||||
end
|
||||
end
|
||||
|
||||
default: scan_state <= S_IDLE;
|
||||
endcase
|
||||
end
|
||||
|
||||
// ================================================================
|
||||
// MODE 11: Reserved — idle
|
||||
// ================================================================
|
||||
2'b11: begin
|
||||
scan_state <= S_IDLE;
|
||||
timer <= 18'd0;
|
||||
end
|
||||
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
// ============================================================================
|
||||
// OUTPUT ASSIGNMENTS
|
||||
// ============================================================================
|
||||
assign scanning = (scan_state != S_IDLE);
|
||||
assign scan_complete = scan_done_pulse;
|
||||
|
||||
endmodule
|
||||
Reference in New Issue
Block a user