Fix 1: Combinational magnitude + non-sticky detection flag (tb: 23/23) Fix 2: Rename all cfar_* signals to detect_*/threshold_* (honest naming) Fix 3: New rx_gain_control.v between DDC and FFT, opcode 0x16 (tb: 33/33) Fix 4: Clamp host_chirps_per_elev to DOPPLER_FFT_SIZE, error flag (E2E: 54/54) Fix 5: Decimator watchdog timeout, 256-cycle limit (tb: 63/63) Fix 6: Remove bypass_mode dead code from ddc_400m.v (DDC tb: 21/21) Fix 7: Range-mode register 0x20 with status readback (USB tb: 77/77)
406 lines
15 KiB
Verilog
406 lines
15 KiB
Verilog
`timescale 1ns / 1ps
|
|
|
|
/**
|
|
* range_bin_decimator.v
|
|
*
|
|
* Reduces 1024 range bins from the matched filter output down to 64 bins
|
|
* for the Doppler processor. Supports multiple decimation modes:
|
|
*
|
|
* Mode 2'b00: Simple decimation (take every Nth sample)
|
|
* Mode 2'b01: Peak detection (select max-magnitude sample from each group)
|
|
* Mode 2'b10: Averaging (sum group and divide by N)
|
|
* Mode 2'b11: Reserved
|
|
*
|
|
* Interface contract (from radar_receiver_final.v line 229):
|
|
* .clk, .reset_n
|
|
* .range_i_in, .range_q_in, .range_valid_in ← from matched_filter output
|
|
* .range_i_out, .range_q_out, .range_valid_out → to Doppler processor
|
|
* .range_bin_index → 6-bit output bin index
|
|
* .decimation_mode ← 2-bit mode select
|
|
* .start_bin ← 10-bit start offset
|
|
*
|
|
* start_bin usage:
|
|
* When start_bin > 0, the decimator skips the first 'start_bin' valid
|
|
* input samples before beginning decimation. This allows selecting a
|
|
* region of interest within the 1024 range bins (e.g., to focus on
|
|
* near-range or far-range targets). When start_bin = 0 (default),
|
|
* all 1024 bins are processed starting from bin 0.
|
|
*
|
|
* Clock domain: clk (100 MHz)
|
|
* Decimation: 1024 → 64 (factor of 16)
|
|
*/
|
|
|
|
module range_bin_decimator #(
|
|
parameter INPUT_BINS = 1024,
|
|
parameter OUTPUT_BINS = 64,
|
|
parameter DECIMATION_FACTOR = 16
|
|
) (
|
|
input wire clk,
|
|
input wire reset_n,
|
|
|
|
// Input from matched filter
|
|
input wire signed [15:0] range_i_in,
|
|
input wire signed [15:0] range_q_in,
|
|
input wire range_valid_in,
|
|
|
|
// Output to Doppler processor
|
|
output reg signed [15:0] range_i_out,
|
|
output reg signed [15:0] range_q_out,
|
|
output reg range_valid_out,
|
|
output reg [5:0] range_bin_index,
|
|
|
|
// Configuration
|
|
input wire [1:0] decimation_mode, // 00=decimate, 01=peak, 10=average
|
|
input wire [9:0] start_bin, // First input bin to process
|
|
|
|
// Diagnostics
|
|
output reg watchdog_timeout // Pulses high for 1 cycle on watchdog reset
|
|
|
|
`ifdef FORMAL
|
|
,
|
|
output wire [2:0] fv_state,
|
|
output wire [9:0] fv_in_bin_count,
|
|
output wire [3:0] fv_group_sample_count,
|
|
output wire [5:0] fv_output_bin_count,
|
|
output wire [9:0] fv_skip_count
|
|
`endif
|
|
);
|
|
|
|
// Fix 5: Watchdog timeout — if no valid input arrives for WATCHDOG_LIMIT
|
|
// clocks while in ST_PROCESS or ST_SKIP, return to ST_IDLE to prevent hang.
|
|
// 256 clocks at 100MHz = 2.56us, well beyond normal inter-sample gap.
|
|
localparam WATCHDOG_LIMIT = 10'd256;
|
|
|
|
// ============================================================================
|
|
// INTERNAL SIGNALS
|
|
// ============================================================================
|
|
|
|
// Input bin counter (0..1023)
|
|
reg [9:0] in_bin_count;
|
|
|
|
// Group tracking
|
|
reg [3:0] group_sample_count; // 0..15 within current group of 16
|
|
reg [5:0] output_bin_count; // 0..63 output bin index
|
|
|
|
// State machine
|
|
reg [2:0] state;
|
|
localparam ST_IDLE = 3'd0;
|
|
localparam ST_SKIP = 3'd1; // Skip first start_bin samples
|
|
localparam ST_PROCESS = 3'd2;
|
|
localparam ST_EMIT = 3'd3;
|
|
localparam ST_DONE = 3'd4;
|
|
|
|
// Skip counter for start_bin
|
|
reg [9:0] skip_count;
|
|
|
|
// Watchdog counter — counts consecutive clocks with no range_valid_in
|
|
reg [9:0] watchdog_count;
|
|
|
|
`ifdef FORMAL
|
|
assign fv_state = state;
|
|
assign fv_in_bin_count = in_bin_count;
|
|
assign fv_group_sample_count = group_sample_count;
|
|
assign fv_output_bin_count = output_bin_count;
|
|
assign fv_skip_count = skip_count;
|
|
`endif
|
|
|
|
// ============================================================================
|
|
// PEAK DETECTION (Mode 01)
|
|
// ============================================================================
|
|
// Track the sample with the largest magnitude in the current group of 16
|
|
reg signed [15:0] peak_i, peak_q;
|
|
reg [16:0] peak_mag; // |I| + |Q| approximation
|
|
wire [16:0] cur_mag;
|
|
|
|
// Magnitude approximation: |I| + |Q| (avoids multiplier for sqrt(I²+Q²))
|
|
wire [15:0] abs_i = range_i_in[15] ? (~range_i_in + 1) : range_i_in;
|
|
wire [15:0] abs_q = range_q_in[15] ? (~range_q_in + 1) : range_q_in;
|
|
assign cur_mag = {1'b0, abs_i} + {1'b0, abs_q};
|
|
|
|
// ============================================================================
|
|
// AVERAGING (Mode 10)
|
|
// ============================================================================
|
|
// Accumulate I and Q separately, then divide by DECIMATION_FACTOR (>>4)
|
|
reg signed [19:0] sum_i, sum_q; // 16 + 4 guard bits for sum of 16 values
|
|
|
|
// ============================================================================
|
|
// SIMPLE DECIMATION (Mode 00)
|
|
// ============================================================================
|
|
// Just take sample at offset (group_start + DECIMATION_FACTOR/2) for center
|
|
reg signed [15:0] decim_i, decim_q;
|
|
|
|
// ============================================================================
|
|
// MAIN STATE MACHINE
|
|
// ============================================================================
|
|
always @(posedge clk or negedge reset_n) begin
|
|
if (!reset_n) begin
|
|
state <= ST_IDLE;
|
|
in_bin_count <= 10'd0;
|
|
group_sample_count <= 4'd0;
|
|
output_bin_count <= 6'd0;
|
|
skip_count <= 10'd0;
|
|
watchdog_count <= 10'd0;
|
|
watchdog_timeout <= 1'b0;
|
|
range_valid_out <= 1'b0;
|
|
range_i_out <= 16'd0;
|
|
range_q_out <= 16'd0;
|
|
range_bin_index <= 6'd0;
|
|
peak_i <= 16'd0;
|
|
peak_q <= 16'd0;
|
|
peak_mag <= 17'd0;
|
|
sum_i <= 20'd0;
|
|
sum_q <= 20'd0;
|
|
decim_i <= 16'd0;
|
|
decim_q <= 16'd0;
|
|
end else begin
|
|
// Default: output not valid, watchdog not triggered
|
|
range_valid_out <= 1'b0;
|
|
watchdog_timeout <= 1'b0;
|
|
|
|
case (state)
|
|
// ================================================================
|
|
// IDLE: Wait for first valid input
|
|
// ================================================================
|
|
ST_IDLE: begin
|
|
in_bin_count <= 10'd0;
|
|
group_sample_count <= 4'd0;
|
|
output_bin_count <= 6'd0;
|
|
skip_count <= 10'd0;
|
|
watchdog_count <= 10'd0;
|
|
peak_i <= 16'd0;
|
|
peak_q <= 16'd0;
|
|
peak_mag <= 17'd0;
|
|
sum_i <= 20'd0;
|
|
sum_q <= 20'd0;
|
|
|
|
if (range_valid_in) begin
|
|
in_bin_count <= 10'd1;
|
|
|
|
if (start_bin > 10'd0) begin
|
|
// Need to skip 'start_bin' samples first
|
|
skip_count <= 10'd1;
|
|
state <= ST_SKIP;
|
|
end else begin
|
|
// No skip — process first sample immediately
|
|
state <= ST_PROCESS;
|
|
group_sample_count <= 4'd1;
|
|
|
|
// Mode-specific first sample handling
|
|
case (decimation_mode)
|
|
2'b00: begin // Simple decimation — check if center sample
|
|
if (4'd0 == (DECIMATION_FACTOR / 2)) begin
|
|
decim_i <= range_i_in;
|
|
decim_q <= range_q_in;
|
|
end
|
|
end
|
|
2'b01: begin // Peak detection
|
|
peak_i <= range_i_in;
|
|
peak_q <= range_q_in;
|
|
peak_mag <= cur_mag;
|
|
end
|
|
2'b10: begin // Averaging
|
|
sum_i <= {{4{range_i_in[15]}}, range_i_in};
|
|
sum_q <= {{4{range_q_in[15]}}, range_q_in};
|
|
end
|
|
default: ;
|
|
endcase
|
|
end
|
|
end
|
|
end
|
|
|
|
// ================================================================
|
|
// SKIP: Discard input samples until start_bin reached
|
|
// ================================================================
|
|
ST_SKIP: begin
|
|
if (range_valid_in) begin
|
|
watchdog_count <= 10'd0;
|
|
in_bin_count <= in_bin_count + 1;
|
|
|
|
if (skip_count >= start_bin) begin
|
|
// Done skipping — this sample is the first to process
|
|
state <= ST_PROCESS;
|
|
group_sample_count <= 4'd1;
|
|
|
|
case (decimation_mode)
|
|
2'b00: begin
|
|
if (4'd0 == (DECIMATION_FACTOR / 2)) begin
|
|
decim_i <= range_i_in;
|
|
decim_q <= range_q_in;
|
|
end
|
|
end
|
|
2'b01: begin
|
|
peak_i <= range_i_in;
|
|
peak_q <= range_q_in;
|
|
peak_mag <= cur_mag;
|
|
end
|
|
2'b10: begin
|
|
sum_i <= {{4{range_i_in[15]}}, range_i_in};
|
|
sum_q <= {{4{range_q_in[15]}}, range_q_in};
|
|
end
|
|
default: ;
|
|
endcase
|
|
end else begin
|
|
skip_count <= skip_count + 1;
|
|
end
|
|
end else begin
|
|
// No valid input — increment watchdog
|
|
if (watchdog_count >= WATCHDOG_LIMIT - 1) begin
|
|
watchdog_timeout <= 1'b1;
|
|
state <= ST_IDLE;
|
|
`ifdef SIMULATION
|
|
$display("[RNG_DECIM] WATCHDOG: timeout in ST_SKIP after %0d idle clocks", WATCHDOG_LIMIT);
|
|
`endif
|
|
end else begin
|
|
watchdog_count <= watchdog_count + 1;
|
|
end
|
|
end
|
|
end
|
|
|
|
// ================================================================
|
|
// PROCESS: Accumulate samples within each group of DECIMATION_FACTOR
|
|
// ================================================================
|
|
ST_PROCESS: begin
|
|
if (range_valid_in) begin
|
|
watchdog_count <= 10'd0;
|
|
in_bin_count <= in_bin_count + 1;
|
|
|
|
// Mode-specific sample processing — always process
|
|
// the current sample before checking overflow
|
|
case (decimation_mode)
|
|
2'b00: begin // Simple decimation
|
|
if (group_sample_count == (DECIMATION_FACTOR / 2)) begin
|
|
decim_i <= range_i_in;
|
|
decim_q <= range_q_in;
|
|
end
|
|
end
|
|
2'b01: begin // Peak detection
|
|
if (cur_mag > peak_mag) begin
|
|
peak_i <= range_i_in;
|
|
peak_q <= range_q_in;
|
|
peak_mag <= cur_mag;
|
|
end
|
|
end
|
|
2'b10: begin // Averaging
|
|
sum_i <= sum_i + {{4{range_i_in[15]}}, range_i_in};
|
|
sum_q <= sum_q + {{4{range_q_in[15]}}, range_q_in};
|
|
end
|
|
default: ;
|
|
endcase
|
|
|
|
// Check if group is complete
|
|
if (group_sample_count == DECIMATION_FACTOR - 1) begin
|
|
// Group complete — emit output
|
|
state <= ST_EMIT;
|
|
group_sample_count <= 4'd0;
|
|
end else if (in_bin_count >= INPUT_BINS - 1) begin
|
|
// Overflow guard: consumed all input bins but group
|
|
// is not yet complete. Stop to prevent corruption of
|
|
// downstream Doppler BRAM if matched filter emits
|
|
// more than INPUT_BINS valid samples.
|
|
state <= ST_DONE;
|
|
end else begin
|
|
group_sample_count <= group_sample_count + 1;
|
|
end
|
|
end else begin
|
|
// No valid input — increment watchdog
|
|
if (watchdog_count >= WATCHDOG_LIMIT - 1) begin
|
|
watchdog_timeout <= 1'b1;
|
|
state <= ST_IDLE;
|
|
`ifdef SIMULATION
|
|
$display("[RNG_DECIM] WATCHDOG: timeout in ST_PROCESS after %0d idle clocks", WATCHDOG_LIMIT);
|
|
`endif
|
|
end else begin
|
|
watchdog_count <= watchdog_count + 1;
|
|
end
|
|
end
|
|
end
|
|
|
|
// ================================================================
|
|
// EMIT: Output one decimated range bin
|
|
// ================================================================
|
|
ST_EMIT: begin
|
|
range_valid_out <= 1'b1;
|
|
range_bin_index <= output_bin_count;
|
|
|
|
case (decimation_mode)
|
|
2'b00: begin // Simple decimation
|
|
range_i_out <= decim_i;
|
|
range_q_out <= decim_q;
|
|
end
|
|
2'b01: begin // Peak detection
|
|
range_i_out <= peak_i;
|
|
range_q_out <= peak_q;
|
|
end
|
|
2'b10: begin // Averaging (sum >> 4 = divide by 16)
|
|
range_i_out <= sum_i[19:4];
|
|
range_q_out <= sum_q[19:4];
|
|
end
|
|
default: begin
|
|
range_i_out <= 16'd0;
|
|
range_q_out <= 16'd0;
|
|
end
|
|
endcase
|
|
|
|
// Reset group accumulators
|
|
peak_i <= 16'd0;
|
|
peak_q <= 16'd0;
|
|
peak_mag <= 17'd0;
|
|
sum_i <= 20'd0;
|
|
sum_q <= 20'd0;
|
|
|
|
// Advance output bin
|
|
output_bin_count <= output_bin_count + 1;
|
|
|
|
// Check if all output bins emitted
|
|
if (output_bin_count == OUTPUT_BINS - 1) begin
|
|
state <= ST_DONE;
|
|
end else begin
|
|
// If we already have valid input waiting, process it immediately
|
|
if (range_valid_in) begin
|
|
state <= ST_PROCESS;
|
|
group_sample_count <= 4'd1;
|
|
in_bin_count <= in_bin_count + 1;
|
|
|
|
case (decimation_mode)
|
|
2'b00: begin
|
|
if (4'd0 == (DECIMATION_FACTOR / 2)) begin
|
|
decim_i <= range_i_in;
|
|
decim_q <= range_q_in;
|
|
end
|
|
end
|
|
2'b01: begin
|
|
peak_i <= range_i_in;
|
|
peak_q <= range_q_in;
|
|
peak_mag <= cur_mag;
|
|
end
|
|
2'b10: begin
|
|
sum_i <= {{4{range_i_in[15]}}, range_i_in};
|
|
sum_q <= {{4{range_q_in[15]}}, range_q_in};
|
|
end
|
|
default: ;
|
|
endcase
|
|
end else begin
|
|
state <= ST_PROCESS;
|
|
group_sample_count <= 4'd0;
|
|
end
|
|
end
|
|
end
|
|
|
|
// ================================================================
|
|
// DONE: All 64 output bins emitted, return to idle
|
|
// ================================================================
|
|
ST_DONE: begin
|
|
state <= ST_IDLE;
|
|
|
|
`ifdef SIMULATION
|
|
$display("[RNG_DECIM] Frame complete: %0d output bins emitted", OUTPUT_BINS);
|
|
`endif
|
|
end
|
|
|
|
default: state <= ST_IDLE;
|
|
endcase
|
|
end
|
|
end
|
|
|
|
endmodule
|