chirp-v2 PR-F: doppler/CFAR widen to 3 sub-frames + 2-class detect

Bumps RP_CHIRPS_PER_FRAME 32 -> 48 (= 3 sub-frames × 16 chirps), widens
doppler_bin from 5 to 6 bits ({sub_frame[1:0], bin[3:0]}), and replaces the
1-bit detect_flag rail with a 2-bit detect_class (NONE / CANDIDATE /
CONFIRMED) sourced from a soft+confirm CFAR threshold pair.

doppler_processor:
  Generalised the 2-subframe FSM to NUM_SUBFRAMES = CHIRPS_PER_FRAME /
  CHIRPS_PER_SUBFRAME (=3 in production, =2 when TBs override). S_OUTPUT
  walks current_sub_frame 0..NUM_SUBFRAMES-1 then advances range_bin;
  the chirp_base * CHIRPS_PER_SUBFRAME formula replaces the if/else split.
  write_chirp_index, read_doppler_index, sub_frame, current_sub_frame all
  widened to 6/2 bits accordingly. doppler_bin packing {current_sub_frame[1:0],
  fft_sample_counter[3:0]} naturally yields 6 bits.

cfar_ca:
  Adds cfg_alpha_soft input + r_alpha_soft register (default
  RP_DEF_CFAR_ALPHA_SOFT = 0x18 ≈ 1.5 in Q4.4 → Pfa_soft ≈ 1e-5). ST_CFAR_MUL
  computes both noise_product (alpha) and noise_product_soft (alpha_soft) in
  parallel DSPs; ST_CFAR_CMP emits detect_class = CONFIRMED when cur > thr,
  CANDIDATE when cur > thr_soft (and not CONFIRMED), NONE otherwise.
  detect_flag is preserved as (class != NONE) for backward compat.
  Address packing now pads doppler axis to next power-of-2 (DOPPLER_PAD =
  1 << ceil(log2(NUM_DOPPLER))) so {range, doppler} packs contiguously
  for both NUM_DOPPLER=32 (legacy TB) and NUM_DOPPLER=48 (production).
  Mag-BRAM grows from ~16 to ~30 RAMB18 on 50T (acceptable on the budget).

usb_data_interface_ft2232h:
  doppler_bin_in widened to 6 bits. FRAME_CELLS pads to next power of two
  (32K) so {range, doppler[5:0]} concatenation lands cleanly. Address regs
  bumped: mag_wr/rd_addr 14→15, detect_byte_addr 11→12, detect_clear bit-
  counter 14→15. Detect-bit BRAM grows 2K→4K bytes. Wire-protocol byte
  counts auto-scale with FRAME_CELLS / DOPPLER_MAG_SECTION_BYTES; PR-G
  bumps the bulk-frame protocol version so the host parser knows.

Other:
  - radar_params.vh: RP_CHIRPS_PER_FRAME 32→48, RP_NUM_DOPPLER_BINS 32→48,
    RP_DOPPLER_MEM_ADDR_W 14→15 (50T) / 17→18 (200T), RP_CFAR_MAG_ADDR_W
    likewise. Other macros (RP_DOPPLER_BIN_WIDTH=6, RP_DETECT_CLASS_WIDTH=2,
    RP_DEF_CFAR_ALPHA_SOFT=0x18, RP_NUM_SUBFRAMES=3) were already in place
    from PR-A.
  - radar_system_top: rx_doppler_bin / dbg_doppler_bin widened. Adds
    host_cfar_alpha_soft register (default RP_DEF_CFAR_ALPHA_SOFT). USB
    opcode mapping deferred to PR-G.
  - radar_system_top_50t: dbg_doppler_bin_nc width.
  - radar_receiver_final: doppler_bin port width.

Test summary:
  - tb_chirp_controller_v2:  43/43 PASS
  - tb_chirp_contract:       10/10 PASS
  - tb_cfar_ca:              24/0 PASS
  - tb_mti_canceller:        43/43 PASS
  - tb_rxb_fullchain:        peak 24033 ~80x (parity with PR-D/E)
  - tb_doppler_realdata:     2056/2056 PASS  (had been broken pre-PR-F due
                             to missing RANGE_BINS=64 override; this PR fixes
                             the parameter override along with the widening)
  - tb_system_e2e:           33/49 PASS — identical to PR-E baseline; the
                             one new fail vs PR-D (G2.2) carries over.
  - tb_radar_receiver_final: still finishing in background (~10 min).
This commit is contained in:
Jason
2026-05-01 03:36:03 +05:45
parent a1a8fa7107
commit 7862f4d63c
10 changed files with 287 additions and 192 deletions

View File

@@ -102,11 +102,12 @@
// CFAR magnitude BRAM depth uses `RP_CFAR_MAG_DEPTH which already scales.
module cfar_ca #(
parameter NUM_RANGE_BINS = `RP_MAX_OUTPUT_BINS, // 512 (50T) / 4096 (200T)
parameter NUM_DOPPLER_BINS = `RP_NUM_DOPPLER_BINS, // 32
parameter NUM_DOPPLER_BINS = `RP_NUM_DOPPLER_BINS, // 48 (PR-F)
parameter MAG_WIDTH = 17,
parameter ALPHA_WIDTH = 8,
parameter MAX_GUARD = 8,
parameter MAX_TRAIN = 16
parameter MAX_TRAIN = 16,
parameter DBIN_WIDTH = `RP_DOPPLER_BIN_WIDTH // 6 (PR-F)
) (
input wire clk,
input wire reset_n,
@@ -114,7 +115,7 @@ module cfar_ca #(
// ========== DOPPLER PROCESSOR INPUTS ==========
input wire [31:0] doppler_data,
input wire doppler_valid,
input wire [4:0] doppler_bin_in,
input wire [DBIN_WIDTH-1:0] doppler_bin_in,
input wire [`RP_RANGE_BIN_WIDTH_MAX-1:0] range_bin_in, // 9-bit (50T) / 12-bit (200T)
input wire frame_complete,
@@ -122,20 +123,24 @@ module cfar_ca #(
input wire [3:0] cfg_guard_cells,
input wire [4:0] cfg_train_cells,
input wire [ALPHA_WIDTH-1:0] cfg_alpha,
input wire [ALPHA_WIDTH-1:0] cfg_alpha_soft, // PR-F: candidate-tier threshold
input wire [1:0] cfg_cfar_mode,
input wire cfg_cfar_enable,
input wire [15:0] cfg_simple_threshold,
// ========== DETECTION OUTPUTS ==========
output reg detect_flag,
output reg detect_flag, // = (detect_class != RP_DETECT_NONE)
output reg [`RP_DETECT_CLASS_WIDTH-1:0] detect_class, // PR-F: NONE/CANDIDATE/CONFIRMED
output reg detect_valid,
output reg [`RP_RANGE_BIN_WIDTH_MAX-1:0] detect_range, // 9-bit (50T) / 12-bit (200T)
output reg [4:0] detect_doppler,
output reg [`RP_RANGE_BIN_WIDTH_MAX-1:0] detect_range,
output reg [DBIN_WIDTH-1:0] detect_doppler,
output reg [MAG_WIDTH-1:0] detect_magnitude,
output reg [MAG_WIDTH-1:0] detect_threshold,
output reg [MAG_WIDTH-1:0] detect_threshold, // confirmed threshold (legacy)
output reg [MAG_WIDTH-1:0] detect_threshold_soft, // PR-F: soft (candidate) threshold
// ========== STATUS ==========
output reg [15:0] detect_count,
output reg [15:0] detect_count, // total detections (CONFIRMED only)
output reg [15:0] detect_count_cand, // PR-F: candidate-only counter
output wire cfar_busy,
output reg [7:0] cfar_status
);
@@ -143,10 +148,24 @@ module cfar_ca #(
// ============================================================================
// INTERNAL PARAMETERS
// ============================================================================
localparam TOTAL_CELLS = NUM_RANGE_BINS * NUM_DOPPLER_BINS;
localparam ADDR_WIDTH = `RP_CFAR_MAG_ADDR_W; // 14 (50T) / 17 (200T)
localparam COL_BITS = 5;
localparam ROW_BITS = `RP_RANGE_BIN_WIDTH_MAX; // 9 (50T) / 12 (200T)
// Doppler-axis index width: enough bits to count 0..NUM_DOPPLER_BINS-1.
// Packed BRAM addressing pads to the next power of two so the {range,doppler}
// concatenation lands in a contiguous block per range bin (works for both
// NUM_DOPPLER_BINS=32, legacy power-of-two, and NUM_DOPPLER_BINS=48, PR-F).
function integer clog2;
input integer v;
integer i;
begin
clog2 = 0;
for (i = v - 1; i > 0; i = i >> 1) clog2 = clog2 + 1;
end
endfunction
localparam DBIN_INDEX_BITS = clog2(NUM_DOPPLER_BINS); // 5 (NUM=32) / 6 (NUM=48)
localparam DOPPLER_PAD = (1 << DBIN_INDEX_BITS); // 32 / 64
localparam TOTAL_CELLS = NUM_RANGE_BINS * DOPPLER_PAD; // 16K (50T legacy) / 32K (50T PR-F)
localparam ADDR_WIDTH = `RP_RANGE_BIN_WIDTH_MAX + DBIN_INDEX_BITS;
localparam COL_BITS = DBIN_INDEX_BITS; // address-axis col counter
localparam ROW_BITS = `RP_RANGE_BIN_WIDTH_MAX; // 9 (50T) / 12 (200T)
localparam SUM_WIDTH = MAG_WIDTH + ROW_BITS; // 26 (50T) / 29 (200T)
localparam PROD_WIDTH = SUM_WIDTH + ALPHA_WIDTH; // 34 bits
localparam ALPHA_FRAC_BITS = 4; // Q4.4
@@ -213,13 +232,15 @@ reg [COL_BITS-1:0] col_idx;
reg [3:0] r_guard;
reg [4:0] r_train;
reg [ALPHA_WIDTH-1:0] r_alpha;
reg [ALPHA_WIDTH-1:0] r_alpha_soft; // PR-F: candidate threshold multiplier
reg [1:0] r_mode;
reg r_enable;
reg [15:0] r_simple_thr;
// Threshold pipeline registers
reg [SUM_WIDTH-1:0] noise_sum_reg; // Stage 1: registered noise_sum_comb output
reg [PROD_WIDTH-1:0] noise_product; // Stage 2: alpha * noise_sum_reg
reg [PROD_WIDTH-1:0] noise_product; // Stage 2: alpha * noise_sum_reg
reg [PROD_WIDTH-1:0] noise_product_soft; // PR-F: alpha_soft * noise_sum_reg
reg [MAG_WIDTH-1:0] adaptive_thr;
// Init counter for computing initial lagging sum
@@ -324,12 +345,15 @@ always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
state <= ST_IDLE;
detect_flag <= 1'b0;
detect_class <= `RP_DETECT_NONE;
detect_valid <= 1'b0;
detect_range <= {ROW_BITS{1'b0}};
detect_doppler <= 5'd0;
detect_doppler <= {DBIN_WIDTH{1'b0}};
detect_magnitude <= {MAG_WIDTH{1'b0}};
detect_threshold <= {MAG_WIDTH{1'b0}};
detect_threshold_soft <= {MAG_WIDTH{1'b0}};
detect_count <= 16'd0;
detect_count_cand <= 16'd0;
cfar_status <= 8'd0;
mag_we <= 1'b0;
mag_waddr <= {ADDR_WIDTH{1'b0}};
@@ -345,6 +369,7 @@ always @(posedge clk or negedge reset_n) begin
init_idx <= 0;
noise_sum_reg <= 0;
noise_product <= 0;
noise_product_soft <= 0;
adaptive_thr <= 0;
lead_add_val_r <= 0;
lead_rem_val_r <= 0;
@@ -356,7 +381,8 @@ always @(posedge clk or negedge reset_n) begin
lag_add_valid_r <= 0;
r_guard <= 4'd2;
r_train <= 5'd8;
r_alpha <= 8'h30;
r_alpha <= `RP_DEF_CFAR_ALPHA;
r_alpha_soft <= `RP_DEF_CFAR_ALPHA_SOFT;
r_mode <= 2'b00;
r_enable <= 1'b0;
r_simple_thr <= 16'd10000;
@@ -364,6 +390,7 @@ always @(posedge clk or negedge reset_n) begin
// Defaults: clear one-shot outputs
detect_valid <= 1'b0;
detect_flag <= 1'b0;
detect_class <= `RP_DETECT_NONE;
mag_we <= 1'b0;
case (state)
@@ -374,27 +401,35 @@ always @(posedge clk or negedge reset_n) begin
cfar_status <= 8'd0;
if (doppler_valid) begin
// Capture configuration at frame start
// Capture configuration at frame start. PR-F: per-frame counters
// reset to 0 here (matches the AUDIT-C6 fix in ST_DONE for the
// legacy detect_count).
r_guard <= cfg_guard_cells;
r_train <= (cfg_train_cells == 0) ? 5'd1 : cfg_train_cells;
r_alpha <= cfg_alpha;
r_alpha_soft <= cfg_alpha_soft;
r_mode <= cfg_cfar_mode;
r_enable <= cfg_cfar_enable;
r_simple_thr <= cfg_simple_threshold;
// Buffer first sample
mag_we <= 1'b1;
mag_waddr <= {range_bin_in, doppler_bin_in};
mag_waddr <= {range_bin_in, doppler_bin_in[DBIN_INDEX_BITS-1:0]};
mag_wdata <= cur_mag;
// Simple threshold pass-through when CFAR disabled
// Simple threshold pass-through when CFAR disabled.
// Without an adaptive estimate we can't form a soft tier, so
// detect_class collapses to NONE/CONFIRMED on the simple thr.
if (!cfg_cfar_enable) begin
detect_flag <= (cur_mag > {1'b0, cfg_simple_threshold});
detect_class <= (cur_mag > {1'b0, cfg_simple_threshold})
? `RP_DETECT_CONFIRMED : `RP_DETECT_NONE;
detect_valid <= 1'b1;
detect_range <= range_bin_in;
detect_doppler <= doppler_bin_in;
detect_magnitude <= cur_mag;
detect_threshold <= {1'b0, cfg_simple_threshold};
detect_threshold_soft <= {1'b0, cfg_simple_threshold};
if (cur_mag > {1'b0, cfg_simple_threshold})
detect_count <= detect_count + 1;
end
@@ -411,16 +446,19 @@ always @(posedge clk or negedge reset_n) begin
if (doppler_valid) begin
mag_we <= 1'b1;
mag_waddr <= {range_bin_in, doppler_bin_in};
mag_waddr <= {range_bin_in, doppler_bin_in[DBIN_INDEX_BITS-1:0]};
mag_wdata <= cur_mag;
if (!r_enable) begin
detect_flag <= (cur_mag > {1'b0, r_simple_thr});
detect_class <= (cur_mag > {1'b0, r_simple_thr})
? `RP_DETECT_CONFIRMED : `RP_DETECT_NONE;
detect_valid <= 1'b1;
detect_range <= range_bin_in;
detect_doppler <= doppler_bin_in;
detect_magnitude <= cur_mag;
detect_threshold <= {1'b0, r_simple_thr};
detect_threshold_soft <= {1'b0, r_simple_thr};
if (cur_mag > {1'b0, r_simple_thr})
detect_count <= detect_count + 1;
end
@@ -430,7 +468,7 @@ always @(posedge clk or negedge reset_n) begin
if (r_enable) begin
col_idx <= 0;
col_load_idx <= 0;
mag_raddr <= {{ROW_BITS{1'b0}}, 5'd0};
mag_raddr <= {{ROW_BITS{1'b0}}, {COL_BITS{1'b0}}};
state <= ST_COL_LOAD;
end else begin
state <= ST_DONE;
@@ -531,7 +569,9 @@ always @(posedge clk or negedge reset_n) begin
ST_CFAR_MUL: begin
cfar_status <= {4'd4, 1'b1, col_idx[2:0]};
noise_product <= r_alpha * noise_sum_reg;
// Two parallel multiplies — each maps to a single DSP48 slice.
noise_product <= r_alpha * noise_sum_reg; // confirmed tier
noise_product_soft <= r_alpha_soft * noise_sum_reg; // candidate tier (PR-F)
state <= ST_CFAR_CMP;
end
@@ -554,19 +594,39 @@ always @(posedge clk or negedge reset_n) begin
detect_doppler <= col_idx;
detect_valid <= 1'b1;
// Compare: threshold computed this cycle from noise_product
// Compare: confirm + soft thresholds computed this cycle from
// noise_product / noise_product_soft. detect_class encodes the
// tier (NONE / CANDIDATE / CONFIRMED) so downstream can re-cue
// CANDIDATEs and track CONFIRMEDs.
begin : threshold_compare
reg [MAG_WIDTH-1:0] thr_val;
reg [MAG_WIDTH-1:0] thr_val_soft;
reg [MAG_WIDTH-1:0] cur_val;
if (noise_product[PROD_WIDTH-1:ALPHA_FRAC_BITS+MAG_WIDTH] != 0)
thr_val = {MAG_WIDTH{1'b1}};
else
thr_val = noise_product[ALPHA_FRAC_BITS +: MAG_WIDTH];
detect_threshold <= thr_val;
if (noise_product_soft[PROD_WIDTH-1:ALPHA_FRAC_BITS+MAG_WIDTH] != 0)
thr_val_soft = {MAG_WIDTH{1'b1}};
else
thr_val_soft = noise_product_soft[ALPHA_FRAC_BITS +: MAG_WIDTH];
if (col_buf[cut_idx[ROW_BITS-1:0]] > thr_val) begin
detect_threshold <= thr_val;
detect_threshold_soft <= thr_val_soft;
cur_val = col_buf[cut_idx[ROW_BITS-1:0]];
if (cur_val > thr_val) begin
detect_flag <= 1'b1;
detect_class <= `RP_DETECT_CONFIRMED;
detect_count <= detect_count + 1;
end else if (cur_val > thr_val_soft) begin
// Above soft, below confirm — host re-cues this cell.
detect_flag <= 1'b1;
detect_class <= `RP_DETECT_CANDIDATE;
detect_count_cand <= detect_count_cand + 1;
end
end
@@ -592,7 +652,7 @@ always @(posedge clk or negedge reset_n) begin
if (col_idx < NUM_DOPPLER_BINS - 1) begin
col_idx <= col_idx + 1;
col_load_idx <= 0;
mag_raddr <= {{ROW_BITS{1'b0}}, col_idx + 5'd1};
mag_raddr <= {{ROW_BITS{1'b0}}, col_idx + {{(COL_BITS-1){1'b0}}, 1'b1}};
state <= ST_COL_LOAD;
end else begin
state <= ST_DONE;
@@ -613,10 +673,12 @@ always @(posedge clk or negedge reset_n) begin
state <= ST_IDLE;
`ifdef SIMULATION
$display("[CFAR] Frame complete: %0d frame detections", detect_count);
$display("[CFAR] Frame complete: %0d confirmed, %0d candidates",
detect_count, detect_count_cand);
`endif
detect_count <= 16'd0;
detect_count <= 16'd0;
detect_count_cand <= 16'd0;
end
default: state <= ST_IDLE;

View File

@@ -1,31 +1,29 @@
`timescale 1ns / 1ps
// ============================================================================
// doppler_processor.v Staggered-PRF Doppler Processor (CORRECTED)
// doppler_processor.v Multi-subframe Doppler Processor (chirp-v2 PR-F)
// ============================================================================
//
// ARCHITECTURE:
// This module implements dual 16-point FFTs for the AERIS-10 staggered-PRF
// waveform. The radar transmits 16 long-PRI chirps followed by 16 short-PRI
// chirps per frame (32 total). Rather than a single 32-point FFT over the
// non-uniformly sampled frame (which is signal-processing invalid), this
// module processes each sub-frame independently:
// Processes NUM_SUBFRAMES = CHIRPS_PER_FRAME / CHIRPS_PER_SUBFRAME independent
// 16-point FFTs per range bin. The chirp-v2 production build runs three
// sub-frames (SHORT, MEDIUM, LONG) at 16 chirps each = 48 chirps per frame:
//
// Sub-frame 0 (long PRI): chirps 0..15 16-pt windowed FFT
// Sub-frame 1 (short PRI): chirps 16..31 16-pt windowed FFT
// Sub-frame 0: chirps 0..15 16-pt windowed FFT (SHORT in chirp-v2)
// Sub-frame 1: chirps 16..31 16-pt windowed FFT (MEDIUM in chirp-v2)
// Sub-frame 2: chirps 32..47 16-pt windowed FFT (LONG in chirp-v2)
//
// Each sub-frame produces 16 Doppler bins per range bin. The outputs are
// tagged with a sub_frame bit and the 4-bit bin index is packed into the
// existing 5-bit doppler_bin port as {sub_frame, bin[3:0]}.
// Each sub-frame produces 16 Doppler bins per range bin. Outputs are tagged
// with the 2-bit sub_frame index and the 4-bit bin index is packed into the
// 6-bit doppler_bin port as {sub_frame[1:0], bin[3:0]}.
//
// This architecture enables downstream staggered-PRF ambiguity resolution:
// the same target velocity maps to DIFFERENT Doppler bins at different PRIs,
// and comparing the two sub-frame results resolves velocity ambiguity.
// Legacy 2-subframe golden-vector tests (tb_doppler_realdata,
// tb_fullchain_realdata) override CHIRPS_PER_FRAME=32 + CHIRPS_PER_SUBFRAME=16
// to make NUM_SUBFRAMES=2; the FSM generalises cleanly. doppler_bin still
// reports 6 bits there with the high bit always zero.
//
// INTERFACE COMPATIBILITY:
// The port list is a superset of the original module. Existing instantiations
// that don't connect `sub_frame` will still work. The FORMAL ports are
// retained. CHIRPS_PER_FRAME must be 32 (16 per sub-frame).
// Staggered-PRF ambiguity resolution downstream picks the matching Doppler
// bin from the SHORT vs MEDIUM vs LONG sub-frame to resolve velocity.
//
// WINDOW:
// 16-point Hamming window (Q15), symmetric. Computed as:
@@ -46,7 +44,7 @@
module doppler_processor_optimized #(
parameter DOPPLER_FFT_SIZE = `RP_DOPPLER_FFT_SIZE, // 16
parameter RANGE_BINS = `RP_MAX_OUTPUT_BINS, // 512 (50T) / 4096 (200T)
parameter CHIRPS_PER_FRAME = `RP_CHIRPS_PER_FRAME, // 32
parameter CHIRPS_PER_FRAME = `RP_CHIRPS_PER_FRAME, // 48 (PR-F); legacy TBs override to 32
parameter CHIRPS_PER_SUBFRAME = `RP_CHIRPS_PER_SUBFRAME, // 16
parameter WINDOW_TYPE = 0, // 0=Hamming, 1=Rectangular
parameter DATA_WIDTH = `RP_DATA_WIDTH // 16
@@ -58,9 +56,9 @@ module doppler_processor_optimized #(
input wire new_chirp_frame,
output reg [31:0] doppler_output,
output reg doppler_valid,
output reg [4:0] doppler_bin, // {sub_frame, bin[3:0]}
output reg [`RP_RANGE_BIN_WIDTH_MAX-1:0] range_bin, // 9-bit (50T) / 12-bit (200T)
output reg sub_frame, // 0=long PRI, 1=short PRI
output reg [`RP_DOPPLER_BIN_WIDTH-1:0] doppler_bin, // 6-bit {sub_frame[1:0], bin[3:0]}
output reg [`RP_RANGE_BIN_WIDTH_MAX-1:0] range_bin, // 9-bit (50T) / 12-bit (200T)
output reg [`RP_SUBFRAME_ID_WIDTH-1:0] sub_frame, // 2-bit subframe index
output wire processing_active,
output wire frame_complete,
output reg [3:0] status
@@ -71,9 +69,9 @@ module doppler_processor_optimized #(
output wire [`RP_DOPPLER_MEM_ADDR_W-1:0] fv_mem_write_addr,
output wire [`RP_DOPPLER_MEM_ADDR_W-1:0] fv_mem_read_addr,
output wire [`RP_RANGE_BIN_WIDTH_MAX-1:0] fv_write_range_bin,
output wire [4:0] fv_write_chirp_index,
output wire [5:0] fv_write_chirp_index,
output wire [`RP_RANGE_BIN_WIDTH_MAX-1:0] fv_read_range_bin,
output wire [4:0] fv_read_doppler_index,
output wire [5:0] fv_read_doppler_index,
output wire [9:0] fv_processing_timeout,
output wire fv_frame_buffer_full,
output wire fv_mem_we,
@@ -81,6 +79,11 @@ module doppler_processor_optimized #(
`endif
);
// Derived: number of sub-frames in the current configuration. Production
// build = 3 (SHORT/MEDIUM/LONG @ 16 chirps each = 48 frame). Legacy TBs
// override CHIRPS_PER_FRAME=32 to get NUM_SUBFRAMES=2 for golden compat.
localparam NUM_SUBFRAMES = CHIRPS_PER_FRAME / CHIRPS_PER_SUBFRAME;
// ==============================================
// Window Coefficients 16-point Hamming (Q15)
// ==============================================
@@ -127,9 +130,9 @@ localparam MEM_DEPTH = RANGE_BINS * CHIRPS_PER_FRAME;
// Control Registers
// ==============================================
reg [`RP_RANGE_BIN_WIDTH_MAX-1:0] write_range_bin;
reg [4:0] write_chirp_index;
reg [5:0] write_chirp_index; // 6-bit: 0..47 (PR-F)
reg [`RP_RANGE_BIN_WIDTH_MAX-1:0] read_range_bin;
reg [4:0] read_doppler_index;
reg [5:0] read_doppler_index; // 6-bit (PR-F)
reg frame_buffer_full;
reg [9:0] chirps_received;
reg [1:0] chirp_state;
@@ -146,7 +149,7 @@ reg [1:0] chirp_state;
reg frame_armed;
// Sub-frame tracking
reg current_sub_frame; // 0=processing long, 1=processing short
reg [`RP_SUBFRAME_ID_WIDTH-1:0] current_sub_frame; // 2-bit (PR-F): 0..NUM_SUBFRAMES-1
// ==============================================
// FFT Interface
@@ -365,20 +368,17 @@ always @(posedge clk or negedge reset_n) begin
end
S_OUTPUT: begin
if (current_sub_frame == 0) begin
// Just finished long PRI sub-frame now do short PRI
current_sub_frame <= 1;
if (current_sub_frame < NUM_SUBFRAMES - 1) begin
// Advance to next sub-frame; same range bin, next FFT
current_sub_frame <= current_sub_frame + 1;
fft_sample_counter <= 0;
state <= S_PRE_READ;
// read_range_bin stays the same, read_doppler_index
// will be set to CHIRPS_PER_SUBFRAME in Block 2
end else begin
// Finished both sub-frames for this range bin
// Finished all NUM_SUBFRAMES for this range bin
current_sub_frame <= 0;
if (read_range_bin < RANGE_BINS - 1) begin
fft_sample_counter <= 0;
state <= S_PRE_READ;
// read_range_bin incremented in Block 2
end else begin
state <= S_IDLE;
frame_buffer_full <= 0;
@@ -445,12 +445,9 @@ always @(posedge clk) begin
end
S_PRE_READ: begin
// Set read_doppler_index to first chirp of current sub-frame + 1
// (because address is presented this cycle, data arrives next)
if (current_sub_frame == 0)
read_doppler_index <= 1; // Long PRI: chirps 0..15
else
read_doppler_index <= CHIRPS_PER_SUBFRAME + 1; // Short PRI: chirps 16..31
// First chirp of current sub-frame + 1 (address-then-data pipe).
// Generalised: chirp_base = current_sub_frame * CHIRPS_PER_SUBFRAME.
read_doppler_index <= current_sub_frame * CHIRPS_PER_SUBFRAME + 6'd1;
// BREG priming: window coeff for sample 0
window_val_reg <= $signed(window_coeff[0]);
@@ -463,13 +460,7 @@ always @(posedge clk) begin
mult_q_raw <= $signed(mem_rdata_q) * window_val_reg;
window_val_reg <= $signed(window_coeff[1]);
// Advance to chirp base+2
if (current_sub_frame == 0)
read_doppler_index <= (2 < CHIRPS_PER_SUBFRAME) ? 2
: CHIRPS_PER_SUBFRAME - 1;
else
read_doppler_index <= (CHIRPS_PER_SUBFRAME + 2 < CHIRPS_PER_FRAME)
? CHIRPS_PER_SUBFRAME + 2
: CHIRPS_PER_FRAME - 1;
read_doppler_index <= current_sub_frame * CHIRPS_PER_SUBFRAME + 6'd2;
end else if (fft_sample_counter == 1) begin
mult_i <= mult_i_raw;
mult_q <= mult_q_raw;
@@ -478,14 +469,7 @@ always @(posedge clk) begin
if (2 < CHIRPS_PER_SUBFRAME)
window_val_reg <= $signed(window_coeff[2]);
// Advance to chirp base+3
begin : advance_chirp3
reg [4:0] next_chirp;
next_chirp = (current_sub_frame == 0) ? 3 : CHIRPS_PER_SUBFRAME + 3;
if (next_chirp < CHIRPS_PER_FRAME)
read_doppler_index <= next_chirp;
else
read_doppler_index <= CHIRPS_PER_FRAME - 1;
end
read_doppler_index <= current_sub_frame * CHIRPS_PER_SUBFRAME + 6'd3;
end else if (fft_sample_counter <= CHIRPS_PER_SUBFRAME + 1) begin
// Steady state
fft_input_i <= (mult_i + (1 << 14)) >>> 15;
@@ -503,39 +487,34 @@ always @(posedge clk) begin
if (win_idx < CHIRPS_PER_SUBFRAME)
window_val_reg <= $signed(window_coeff[win_idx]);
end
// Advance BRAM read
begin : advance_bram
reg [4:0] chirp_offset;
reg [4:0] chirp_base;
chirp_offset = fft_sample_counter[3:0] + 2;
chirp_base = (current_sub_frame == 0) ? 0 : CHIRPS_PER_SUBFRAME;
if (chirp_base + chirp_offset < CHIRPS_PER_FRAME)
read_doppler_index <= chirp_base + chirp_offset;
else
read_doppler_index <= CHIRPS_PER_FRAME - 1;
end
// Advance BRAM read: chirp_base + (counter + 2).
// For NUM_SUBFRAMES * CHIRPS_PER_SUBFRAME = CHIRPS_PER_FRAME
// and counter <= CHIRPS_PER_SUBFRAME-1, chirp_base+offset
// is bounded by CHIRPS_PER_FRAME so no clamp is needed.
read_doppler_index <= current_sub_frame * CHIRPS_PER_SUBFRAME
+ {2'd0, fft_sample_counter[3:0]} + 6'd2;
end
if (fft_sample_counter == CHIRPS_PER_SUBFRAME + 1) begin
// Reset read index for potential next operation
if (current_sub_frame == 0)
read_doppler_index <= CHIRPS_PER_SUBFRAME; // Ready for short sub-frame
// Reset read index for the next sub-frame (or wrap to 0
// when we've finished all NUM_SUBFRAMES).
if (current_sub_frame < NUM_SUBFRAMES - 1)
read_doppler_index <= (current_sub_frame + 6'd1) * CHIRPS_PER_SUBFRAME;
else
read_doppler_index <= 0;
read_doppler_index <= 6'd0;
end
end
end
S_OUTPUT: begin
if (current_sub_frame == 0) begin
// Transitioning to short PRI sub-frame
// Set read_doppler_index to start of short sub-frame
read_doppler_index <= CHIRPS_PER_SUBFRAME;
if (current_sub_frame < NUM_SUBFRAMES - 1) begin
// Transitioning to next sub-frame for the same range bin.
read_doppler_index <= (current_sub_frame + 6'd1) * CHIRPS_PER_SUBFRAME;
end else begin
// Both sub-frames done
// All sub-frames done for this range bin
if (read_range_bin < RANGE_BINS - 1) begin
read_range_bin <= read_range_bin + 1;
read_doppler_index <= 0; // Next range bin starts with long sub-frame
read_doppler_index <= 6'd0; // Next range bin starts with sub-frame 0
end
end
end

View File

@@ -77,9 +77,9 @@
`define RP_RANGE_BIN_BITS 9 // ceil(log2(512))
`define RP_DOPPLER_FFT_SIZE 16 // Per sub-frame Doppler FFT (scan mode)
`define RP_DOPPLER_FFT_SIZE_TRACK 64 // Track-mode dwell N (xfft_64, single waveform)
`define RP_CHIRPS_PER_FRAME 32 // (LEGACY: scan-only 2-subframe; bumped to 48 in PR-F)
`define RP_CHIRPS_PER_FRAME 48 // 3 sub-frames * 16 chirps = 48 (PR-F)
`define RP_CHIRPS_PER_SUBFRAME 16 // Chirps per Doppler sub-frame
`define RP_NUM_DOPPLER_BINS 32 // (LEGACY: 2 sub-frames * 16 = 32; bumped to 48 in PR-F)
`define RP_NUM_DOPPLER_BINS 48 // 3 sub-frames * 16 bins = 48 (PR-F)
`define RP_DATA_WIDTH 16 // ADC/processing data width
// 3-ladder waveform identity (replaces 1-bit use_long_chirp rail in PR-C onward)
@@ -153,13 +153,13 @@
`ifdef SUPPORT_LONG_RANGE
`define RP_SEGMENT_IDX_WIDTH 3
`define RP_RANGE_BIN_WIDTH_MAX 12 // ceil(log2(4096))
`define RP_DOPPLER_MEM_ADDR_W 17 // ceil(log2(4096*32)) = 17
`define RP_CFAR_MAG_ADDR_W 17 // ceil(log2(4096*32)) = 17
`define RP_DOPPLER_MEM_ADDR_W 18 // ceil(log2(4096*48)) = 18 (PR-F)
`define RP_CFAR_MAG_ADDR_W 18 // ceil(log2(4096*48)) = 18 (PR-F)
`else
`define RP_SEGMENT_IDX_WIDTH 2
`define RP_RANGE_BIN_WIDTH_MAX 9 // ceil(log2(512))
`define RP_DOPPLER_MEM_ADDR_W 14 // ceil(log2(512*32)) = 14
`define RP_CFAR_MAG_ADDR_W 14 // ceil(log2(512*32)) = 14
`define RP_DOPPLER_MEM_ADDR_W 15 // ceil(log2(512*48)) = 15 (PR-F)
`define RP_CFAR_MAG_ADDR_W 15 // ceil(log2(512*48)) = 15 (PR-F)
`endif
// Derived depths (for memory declarations)

View File

@@ -23,7 +23,7 @@ module radar_receiver_final (
output wire [31:0] doppler_output,
output wire doppler_valid,
output wire [4:0] doppler_bin,
output wire [`RP_DOPPLER_BIN_WIDTH-1:0] doppler_bin,
output wire [`RP_RANGE_BIN_WIDTH_MAX-1:0] range_bin, // 9-bit
// Raw matched-filter output (debug/bring-up)

View File

@@ -126,7 +126,7 @@ module radar_system_top (
// Doppler processing outputs (for debugging)
output wire [31:0] dbg_doppler_data,
output wire dbg_doppler_valid,
output wire [4:0] dbg_doppler_bin,
output wire [`RP_DOPPLER_BIN_WIDTH-1:0] dbg_doppler_bin,
output wire [`RP_RANGE_BIN_WIDTH_MAX-1:0] dbg_range_bin,
// System status
@@ -186,7 +186,7 @@ wire sched_frame_pulse;
// Receiver internal signals
wire [31:0] rx_doppler_output;
wire rx_doppler_valid;
wire [4:0] rx_doppler_bin;
wire [`RP_DOPPLER_BIN_WIDTH-1:0] rx_doppler_bin;
wire [`RP_RANGE_BIN_WIDTH_MAX-1:0] rx_range_bin;
wire [31:0] rx_range_profile;
wire rx_range_valid;
@@ -273,7 +273,7 @@ reg host_status_request; // Opcode 0xFF (self-clearing pulse)
// Doppler path (16 long + 16 short). If host sets chirps_per_elev to a
// different value, Doppler accumulation is corrupted. Clamp at command decode
// and flag the mismatch so the host knows.
localparam DOPPLER_FRAME_CHIRPS = 32; // Total chirps per Doppler frame
localparam DOPPLER_FRAME_CHIRPS = `RP_CHIRPS_PER_FRAME; // 48 (PR-F); was 32
reg chirps_mismatch_error; // Set if host tried to set chirps != FFT size
// Range-mode register (opcode 0x20)
@@ -288,6 +288,8 @@ reg [1:0] host_range_mode;
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 [7:0] host_cfar_alpha_soft; // PR-F: soft / candidate-tier multiplier (Q4.4).
// USB opcode mapping deferred to PR-G (planned 0x28).
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
@@ -650,17 +652,16 @@ assign rx_doppler_data_valid = rx_doppler_valid;
// ============================================================================
// DC NOTCH FILTER (post-Doppler-FFT, pre-CFAR)
// ============================================================================
// Zeros out Doppler bins within ±host_dc_notch_width of DC for BOTH
// sub-frames in the dual 16-pt FFT architecture.
// doppler_bin[4:0] = {sub_frame, bin[3:0]}:
// Sub-frame 0: bins 0-15, DC = bin 0, wrap = bin 15
// Zeros out Doppler bins within ±host_dc_notch_width of DC for ALL
// 16-pt sub-frames in the chirp-v2 architecture (3 sub-frames in production).
// doppler_bin[5:0] = {sub_frame[1:0], bin[3:0]}:
// Sub-frame 0: bins 0-15, DC = bin 0, wrap = bin 15
// Sub-frame 1: bins 16-31, DC = bin 16, wrap = bin 31
// notch_width=1 zero bins {0,16}. notch_width=2 zero bins
// {0,1,15,16,17,31}. etc.
// When host_dc_notch_width=0: pass-through (no zeroing).
// Sub-frame 2: bins 32-47, DC = bin 32, wrap = bin 47
// The DC test ignores the sub-frame field and gates on the 4-bit per-FFT bin.
wire dc_notch_active;
wire [4:0] dop_bin_unsigned = rx_doppler_bin;
wire [`RP_DOPPLER_BIN_WIDTH-1:0] dop_bin_unsigned = rx_doppler_bin;
wire [3:0] bin_within_sf = dop_bin_unsigned[3:0];
assign dc_notch_active = (host_dc_notch_width != 3'd0) &&
(bin_within_sf < {1'b0, host_dc_notch_width} ||
@@ -669,8 +670,8 @@ assign dc_notch_active = (host_dc_notch_width != 3'd0) &&
// Notched Doppler data: zero I/Q when in notch zone, pass through otherwise
wire [31:0] notched_doppler_data = dc_notch_active ? 32'd0 : rx_doppler_output;
wire notched_doppler_valid = rx_doppler_valid;
wire [4:0] notched_doppler_bin = rx_doppler_bin;
wire [`RP_RANGE_BIN_WIDTH_MAX-1:0] notched_range_bin = rx_range_bin;
wire [`RP_DOPPLER_BIN_WIDTH-1:0] notched_doppler_bin = rx_doppler_bin;
wire [`RP_RANGE_BIN_WIDTH_MAX-1:0] notched_range_bin = rx_range_bin;
// ============================================================================
// CFAR DETECTOR (replaces simple threshold detector)
@@ -680,12 +681,15 @@ wire [`RP_RANGE_BIN_WIDTH_MAX-1:0] notched_range_bin = rx_range_bin;
// See cfar_ca.v for architecture details.
wire cfar_detect_flag;
wire [`RP_DETECT_CLASS_WIDTH-1:0] cfar_detect_class;
wire cfar_detect_valid;
wire [`RP_RANGE_BIN_WIDTH_MAX-1:0] cfar_detect_range;
wire [4:0] cfar_detect_doppler;
wire [`RP_DOPPLER_BIN_WIDTH-1:0] cfar_detect_doppler;
wire [16:0] cfar_detect_magnitude;
wire [16:0] cfar_detect_threshold;
wire [16:0] cfar_detect_threshold_soft;
wire [15:0] cfar_detect_count;
wire [15:0] cfar_detect_count_cand;
wire cfar_busy_w;
wire [7:0] cfar_status_w;
@@ -704,20 +708,24 @@ cfar_ca cfar_inst (
.cfg_guard_cells(host_cfar_guard),
.cfg_train_cells(host_cfar_train),
.cfg_alpha(host_cfar_alpha),
.cfg_alpha_soft(host_cfar_alpha_soft),
.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_class(cfar_detect_class),
.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),
.detect_threshold_soft(cfar_detect_threshold_soft),
// Status
.detect_count(cfar_detect_count),
.detect_count_cand(cfar_detect_count_cand),
.cfar_busy(cfar_busy_w),
.cfar_status(cfar_status_w)
);
@@ -1032,7 +1040,8 @@ always @(posedge clk_100m_buf or negedge sys_reset_n) begin
// 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_alpha <= `RP_DEF_CFAR_ALPHA; // 3.0 (Q4.4)
host_cfar_alpha_soft <= `RP_DEF_CFAR_ALPHA_SOFT; // 1.5 (Q4.4) PR-F
host_cfar_mode <= 2'b00; // CA-CFAR
host_cfar_enable <= 1'b0; // Disabled (simple threshold)
// Ground clutter removal defaults (disabled backward-compatible)

View File

@@ -112,7 +112,7 @@ module radar_system_top_50t (
wire new_chirp_frame_nc;
wire [31:0] dbg_doppler_data_nc;
wire dbg_doppler_valid_nc;
wire [4:0] dbg_doppler_bin_nc;
wire [`RP_DOPPLER_BIN_WIDTH-1:0] dbg_doppler_bin_nc;
wire [5:0] dbg_range_bin_nc;
wire [3:0] system_status_nc;

View File

@@ -24,6 +24,8 @@
* T14: cfar_busy asserts during processing, deasserts after
*/
`include "radar_params.vh"
module tb_cfar_ca;
// ============================================================================
@@ -43,24 +45,28 @@ reg reset_n;
reg [31:0] doppler_data;
reg doppler_valid;
reg [4:0] doppler_bin_in;
reg [5:0] range_bin_in;
reg [`RP_DOPPLER_BIN_WIDTH-1:0] doppler_bin_in;
reg [`RP_RANGE_BIN_WIDTH_MAX-1:0] range_bin_in;
reg frame_complete;
reg [3:0] cfg_guard_cells;
reg [4:0] cfg_train_cells;
reg [ALPHA_W-1:0] cfg_alpha;
reg [ALPHA_W-1:0] cfg_alpha_soft; // PR-F
reg [1:0] cfg_cfar_mode;
reg cfg_cfar_enable;
reg [15:0] cfg_simple_threshold;
wire detect_flag;
wire [`RP_DETECT_CLASS_WIDTH-1:0] detect_class; // PR-F
wire detect_valid;
wire [5:0] detect_range;
wire [4:0] detect_doppler;
wire [`RP_RANGE_BIN_WIDTH_MAX-1:0] detect_range;
wire [`RP_DOPPLER_BIN_WIDTH-1:0] detect_doppler;
wire [MAG_W-1:0] detect_magnitude;
wire [MAG_W-1:0] detect_threshold;
wire [MAG_W-1:0] detect_threshold_soft; // PR-F
wire [15:0] detect_count;
wire [15:0] detect_count_cand; // PR-F
wire cfar_busy;
wire [7:0] cfar_status;
@@ -84,8 +90,8 @@ reg [255:0] test_name;
// Detection capture (flagged detections only)
integer det_cap_count;
reg [5:0] det_cap_range [0:255];
reg [4:0] det_cap_doppler[0:255];
reg [`RP_RANGE_BIN_WIDTH_MAX-1:0] det_cap_range [0:255];
reg [`RP_DOPPLER_BIN_WIDTH-1:0] det_cap_doppler[0:255];
reg [MAG_W-1:0] det_cap_mag[0:255];
reg [MAG_W-1:0] det_cap_thr[0:255];
reg det_cap_flag [0:255];
@@ -110,16 +116,20 @@ cfar_ca #(
.cfg_guard_cells(cfg_guard_cells),
.cfg_train_cells(cfg_train_cells),
.cfg_alpha(cfg_alpha),
.cfg_alpha_soft(cfg_alpha_soft), // PR-F
.cfg_cfar_mode(cfg_cfar_mode),
.cfg_cfar_enable(cfg_cfar_enable),
.cfg_simple_threshold(cfg_simple_threshold),
.detect_flag(detect_flag),
.detect_class(detect_class), // PR-F
.detect_valid(detect_valid),
.detect_range(detect_range),
.detect_doppler(detect_doppler),
.detect_magnitude(detect_magnitude),
.detect_threshold(detect_threshold),
.detect_threshold_soft(detect_threshold_soft), // PR-F
.detect_count(detect_count),
.detect_count_cand(detect_count_cand), // PR-F
.cfar_busy(cfar_busy),
.cfar_status(cfar_status)
);
@@ -165,8 +175,8 @@ endtask
// Feed one Doppler sample (I/Q packed as {Q, I})
task feed_sample;
input [5:0] rbin;
input [4:0] dbin;
input [`RP_RANGE_BIN_WIDTH_MAX-1:0] rbin;
input [`RP_DOPPLER_BIN_WIDTH-1:0] dbin;
input signed [15:0] i_val;
input signed [15:0] q_val;
begin
@@ -184,8 +194,8 @@ endtask
// noise_level: base I value for all cells
// num_targets: number of target cells
// tgt_range[0..3], tgt_doppler[0..3], tgt_level[0..3]: target parameters
reg [5:0] tgt_range [0:7];
reg [4:0] tgt_doppler[0:7];
reg [`RP_RANGE_BIN_WIDTH_MAX-1:0] tgt_range [0:7];
reg [`RP_DOPPLER_BIN_WIDTH-1:0] tgt_doppler[0:7];
reg [15:0] tgt_level [0:7];
integer num_targets;
@@ -207,7 +217,9 @@ task feed_frame;
i_val = tgt_level[t];
end
end
feed_sample(r[5:0], d[4:0], $signed(i_val), 16'sd0);
feed_sample(r[`RP_RANGE_BIN_WIDTH_MAX-1:0],
d[`RP_DOPPLER_BIN_WIDTH-1:0],
$signed(i_val), 16'sd0);
end
end
end
@@ -304,6 +316,11 @@ initial begin
cfg_guard_cells = 4'd2;
cfg_train_cells = 5'd8;
cfg_alpha = 8'h30;
// PR-F: pin alpha_soft to max so the candidate tier never triggers in
// these tests preserves the original "detect_flag means CONFIRMED"
// semantics. The 2-class detection path is exercised by tb_system_e2e
// (live data) rather than this unit test.
cfg_alpha_soft = 8'hFF;
cfg_cfar_mode = 2'b00;
cfg_cfar_enable = 1'b1;
cfg_simple_threshold = 16'd5000;

View File

@@ -23,6 +23,8 @@
* vvp tb/tb_doppler_realdata.vvp
*/
`include "radar_params.vh"
module tb_doppler_realdata;
// ============================================================================
@@ -56,16 +58,23 @@ reg data_valid;
reg new_chirp_frame;
wire [31:0] doppler_output;
wire doppler_valid;
wire [4:0] doppler_bin;
wire [5:0] range_bin;
wire [`RP_DOPPLER_BIN_WIDTH-1:0] doppler_bin;
wire [`RP_RANGE_BIN_WIDTH_MAX-1:0] range_bin;
wire processing_active;
wire frame_complete;
wire [3:0] dut_status;
// ============================================================================
// DUT INSTANTIATION
// DUT INSTANTIATION override CHIRPS_PER_FRAME=32 to keep this TB compatible
// with the legacy 2-subframe golden vectors (chirp-v2 production runs 3
// sub-frames at 48 chirps/frame; the Doppler FSM is parameterised over
// NUM_SUBFRAMES = CHIRPS_PER_FRAME / CHIRPS_PER_SUBFRAME).
// ============================================================================
doppler_processor_optimized dut (
doppler_processor_optimized #(
.CHIRPS_PER_FRAME(32),
.CHIRPS_PER_SUBFRAME(16),
.RANGE_BINS(64)
) dut (
.clk(clk),
.reset_n(reset_n),
.range_data(range_data),
@@ -109,8 +118,8 @@ end
// ============================================================================
reg signed [15:0] cap_out_i [0:TOTAL_OUTPUTS-1];
reg signed [15:0] cap_out_q [0:TOTAL_OUTPUTS-1];
reg [5:0] cap_rbin [0:TOTAL_OUTPUTS-1];
reg [4:0] cap_dbin [0:TOTAL_OUTPUTS-1];
reg [`RP_RANGE_BIN_WIDTH_MAX-1:0] cap_rbin [0:TOTAL_OUTPUTS-1];
reg [`RP_DOPPLER_BIN_WIDTH-1:0] cap_dbin [0:TOTAL_OUTPUTS-1];
integer out_count;
// ============================================================================

View File

@@ -33,6 +33,8 @@
* vvp tb/tb_fullchain_realdata.vvp
*/
`include "radar_params.vh"
module tb_fullchain_realdata;
// ============================================================================
@@ -91,8 +93,8 @@ reg new_chirp_frame;
wire [31:0] doppler_output;
wire doppler_valid;
wire [4:0] doppler_bin;
wire [5:0] range_bin;
wire [`RP_DOPPLER_BIN_WIDTH-1:0] doppler_bin;
wire [`RP_RANGE_BIN_WIDTH_MAX-1:0] range_bin;
wire processing_active;
wire frame_complete;
wire [3:0] dut_status;
@@ -122,7 +124,14 @@ range_bin_decimator #(
// ============================================================================
// DUT INSTANTIATION: Doppler Processor
// ============================================================================
doppler_processor_optimized doppler_proc (
doppler_processor_optimized #(
// Override CHIRPS_PER_FRAME / RANGE_BINS to keep this TB compatible with
// the legacy 2-subframe golden vectors (chirp-v2 production uses 48
// chirps × 512 ranges; this co-sim runs 32 × 64).
.CHIRPS_PER_FRAME(32),
.CHIRPS_PER_SUBFRAME(16),
.RANGE_BINS(64)
) doppler_proc (
.clk(clk),
.reset_n(reset_n),
.range_data(range_data_32bit),
@@ -174,8 +183,8 @@ reg signed [15:0] decim_cap_q [0:CHIRPS*RANGE_BINS-1];
// ============================================================================
reg signed [15:0] cap_out_i [0:TOTAL_OUTPUT_SAMPLES-1];
reg signed [15:0] cap_out_q [0:TOTAL_OUTPUT_SAMPLES-1];
reg [5:0] cap_rbin [0:TOTAL_OUTPUT_SAMPLES-1];
reg [4:0] cap_dbin [0:TOTAL_OUTPUT_SAMPLES-1];
reg [`RP_RANGE_BIN_WIDTH_MAX-1:0] cap_rbin [0:TOTAL_OUTPUT_SAMPLES-1];
reg [`RP_DOPPLER_BIN_WIDTH-1:0] cap_dbin [0:TOTAL_OUTPUT_SAMPLES-1];
integer out_count;
// ============================================================================

View File

@@ -115,7 +115,7 @@ module usb_data_interface_ft2232h (
// (4096 bins, 131072 cells) requires a wire-protocol extension before
// bins 512..4095 can be transported to the host.
input wire [`RP_RANGE_BIN_WIDTH_MAX-1:0] range_bin_in,
input wire [4:0] doppler_bin_in, // 5-bit doppler bin index
input wire [`RP_DOPPLER_BIN_WIDTH-1:0] doppler_bin_in, // 6-bit (PR-F): {sub_frame[1:0], bin[3:0]}
input wire frame_complete, // 1-cycle pulse from radar_receiver_final edge detector
// FT2232H Physical Interface (245 Synchronous FIFO mode)
@@ -181,9 +181,18 @@ localparam FOOTER = 8'h55;
localparam STATUS_HEADER = 8'hBB;
localparam NUM_RANGE_BINS = `RP_NUM_RANGE_BINS; // 512
localparam NUM_DOPPLER_BINS = `RP_NUM_DOPPLER_BINS; // 32
localparam NUM_DOPPLER_BINS = `RP_NUM_DOPPLER_BINS; // 48 (PR-F)
localparam RANGE_BIN_BITS = `RP_RANGE_BIN_BITS; // 9
localparam FRAME_CELLS = NUM_RANGE_BINS * NUM_DOPPLER_BINS; // 16384
localparam DOPPLER_BIN_BITS = `RP_DOPPLER_BIN_WIDTH;// 6 (PR-F)
// PR-F: pad FRAME_CELLS to next-power-of-2 along the doppler axis so the
// {range, doppler[N-1:0]} concatenation lands in a contiguous BRAM block per
// range bin. Costs ~10 extra RAMB18 vs the previous 16K-cell packing but
// avoids a per-write multiply on the 100 MHz path.
localparam FRAME_CELLS = NUM_RANGE_BINS * (1 << DOPPLER_BIN_BITS); // 32768 (PR-F)
// Frame-cell address widths.
localparam FRAME_ADDR_W = RANGE_BIN_BITS + DOPPLER_BIN_BITS; // 15
localparam DETECT_BYTE_ADDR_W = FRAME_ADDR_W - 3; // 12
localparam DETECT_BYTE_LAST = (FRAME_CELLS / 8) - 1; // 4095
// Frame header: 8 bytes (0xAA + flags + frame_num[2] + range_bins[2] + doppler_bins[2])
localparam FRAME_HDR_BYTES = 8;
@@ -250,7 +259,7 @@ assign ft_data = ft_data_oe ? ft_data_out : 8'hZZ;
(* ram_style = "block" *) reg [15:0] doppler_mag_bram [0:FRAME_CELLS-1];
// Write port (clk domain)
reg [13:0] mag_wr_addr;
reg [FRAME_ADDR_W-1:0] mag_wr_addr; // PR-F: 15-bit
reg [15:0] mag_wr_data;
reg mag_wr_en;
@@ -260,7 +269,7 @@ always @(posedge clk) begin
end
// Read port (ft_clk domain)
reg [13:0] mag_rd_addr;
reg [FRAME_ADDR_W-1:0] mag_rd_addr; // PR-F: 15-bit
reg [15:0] mag_rd_data;
always @(posedge ft_clk) begin
@@ -315,9 +324,9 @@ end
// a 1-cycle read then 1-cycle write-back with the bit set. This works because
// CFAR outputs arrive one cell per clock cycle (sequential scan).
(* ram_style = "block" *) reg [7:0] detect_bram [0:2047];
(* ram_style = "block" *) reg [7:0] detect_bram [0:DETECT_BYTE_LAST]; // PR-F: 3072 entries (was 2048)
reg [10:0] detect_wr_addr;
reg [DETECT_BYTE_ADDR_W-1:0] detect_wr_addr; // PR-F: 12-bit byte addr (was 11)
reg [7:0] detect_wr_data;
reg detect_wr_en;
@@ -326,15 +335,15 @@ always @(posedge clk) begin
detect_bram[detect_wr_addr] <= detect_wr_data;
end
reg [10:0] detect_rd_addr;
reg [7:0] detect_rd_data;
reg [DETECT_BYTE_ADDR_W-1:0] detect_rd_addr; // PR-F: 12-bit byte addr (was 11)
reg [7:0] detect_rd_data;
always @(posedge ft_clk) begin
detect_rd_data <= detect_bram[detect_rd_addr];
end
// Detection BRAM read-modify-write pipeline (clk domain)
reg [10:0] detect_rmw_addr;
reg [DETECT_BYTE_ADDR_W-1:0] detect_rmw_addr; // PR-F: 12-bit byte addr (was 11)
reg [7:0] detect_rmw_rd;
reg [2:0] detect_rmw_bit;
reg detect_rmw_value;
@@ -380,7 +389,7 @@ wire [15:0] range_mag = range_manhattan[16] ? 16'hFFFF : range_manhattan[15:0];
reg [15:0] frame_number; // Incrementing frame counter
reg frame_ready_toggle; // Toggle CDC: frame ready for USB transfer
reg frame_filling; // 1 = currently accumulating frame data
reg [13:0] detect_clear_addr; // Counter for bulk-clearing detection BRAM
reg [FRAME_ADDR_W-1:0] detect_clear_addr; // PR-F: 15-bit bit-counter (FRAME_CELLS = 24576 < 32768)
reg detect_clearing; // 1 = bulk clear in progress
// Range bin counter for range profile writes
@@ -409,18 +418,18 @@ always @(posedge clk or negedge reset_n) begin
frame_ready_toggle <= 1'b0;
frame_filling <= 1'b1;
mag_wr_en <= 1'b0;
mag_wr_addr <= 14'd0;
mag_wr_addr <= {FRAME_ADDR_W{1'b0}};
mag_wr_data <= 16'd0;
range_wr_en <= 1'b0;
range_wr_addr <= {RANGE_BIN_BITS{1'b0}};
range_wr_data <= 16'd0;
detect_wr_en <= 1'b0;
detect_wr_addr <= 11'd0;
detect_wr_addr <= {DETECT_BYTE_ADDR_W{1'b0}};
detect_wr_data <= 8'd0;
detect_clearing <= 1'b0;
detect_clear_addr <= 14'd0;
detect_clear_addr <= {FRAME_ADDR_W{1'b0}};
detect_rmw_state <= 2'd0;
detect_rmw_addr <= 11'd0;
detect_rmw_addr <= {DETECT_BYTE_ADDR_W{1'b0}};
detect_rmw_bit <= 3'd0;
detect_rmw_value <= 1'b0;
range_write_counter <= {RANGE_BIN_BITS{1'b0}};
@@ -431,15 +440,17 @@ always @(posedge clk or negedge reset_n) begin
detect_wr_en <= 1'b0;
// === Detection BRAM bulk clear (runs after frame_complete) ===
// PR-F: bit-counter is FRAME_ADDR_W (15-bit), byte addr is the upper
// (FRAME_ADDR_W-3) bits.
if (detect_clearing) begin
detect_wr_en <= 1'b1;
detect_wr_addr <= detect_clear_addr[13:3];
detect_wr_addr <= detect_clear_addr[FRAME_ADDR_W-1:3];
detect_wr_data <= 8'd0;
if (detect_clear_addr[13:3] == 11'd2047) begin
if (detect_clear_addr[FRAME_ADDR_W-1:3] == DETECT_BYTE_LAST[DETECT_BYTE_ADDR_W-1:0]) begin
detect_clearing <= 1'b0;
detect_clear_addr <= 14'd0;
detect_clear_addr <= {FRAME_ADDR_W{1'b0}};
end else begin
detect_clear_addr <= detect_clear_addr + 14'd8; // Step by 8 bits = 1 byte
detect_clear_addr <= detect_clear_addr + 15'd8; // Step by 8 bits = 1 byte
end
end
@@ -479,11 +490,10 @@ always @(posedge clk or negedge reset_n) begin
end
// === CFAR detection write (read-modify-write) ===
// PR-F: bit_addr = {range_bin_in[8:0], doppler_bin_in[5:0]} = 15-bit.
// byte_addr = bit_addr[14:3] (12 bits), bit_pos = bit_addr[2:0].
if (cfar_valid && frame_filling && detect_rmw_state == 2'd0 && !detect_clearing) begin
// Start RMW: compute byte address and bit position
// bit_addr = {range_bin_in, doppler_bin_in} = 14-bit
// byte_addr = bit_addr[13:3], bit_pos = bit_addr[2:0]
detect_rmw_addr <= {range_bin_in, doppler_bin_in[4:3]};
detect_rmw_addr <= {range_bin_in, doppler_bin_in[DOPPLER_BIN_BITS-1:3]};
detect_rmw_bit <= doppler_bin_in[2:0];
detect_rmw_value <= cfar_detection;
detect_rmw_state <= 2'd1;
@@ -525,7 +535,7 @@ always @(posedge clk or negedge reset_n) begin
if (!frame_filling && !frame_complete) begin
frame_filling <= 1'b1;
detect_clearing <= 1'b1; // Clear detection BRAM for next frame
detect_clear_addr <= 14'd0;
detect_clear_addr <= {FRAME_ADDR_W{1'b0}};
end
end
end
@@ -640,10 +650,10 @@ reg [31:0] status_words [0:5];
reg [15:0] wr_byte_idx;
// BRAM read address for frame transfer
reg [13:0] bram_rd_cell; // Cell index 0..16383 for doppler/detect
reg [RANGE_BIN_BITS-1:0] range_rd_idx; // Range bin index 0..511
reg wr_byte_phase; // 0=MSB, 1=LSB for 16-bit values
reg [10:0] detect_rd_idx; // Byte index 0..2047 for detection flags
reg [FRAME_ADDR_W-1:0] bram_rd_cell; // PR-F: 15-bit cell index 0..24575
reg [RANGE_BIN_BITS-1:0] range_rd_idx; // Range bin index 0..511
reg wr_byte_phase; // 0=MSB, 1=LSB for 16-bit values
reg [DETECT_BYTE_ADDR_W-1:0] detect_rd_idx; // PR-F: 12-bit byte index 0..3071
// ============================================================================
// CLOCK-ACTIVITY WATCHDOG (clk domain)
@@ -732,12 +742,12 @@ always @(posedge ft_clk or negedge ft_effective_reset_n) begin
wr_state <= WR_IDLE;
wr_byte_idx <= 16'd0;
wr_byte_phase <= 1'b0;
bram_rd_cell <= 14'd0;
bram_rd_cell <= {FRAME_ADDR_W{1'b0}};
range_rd_idx <= {RANGE_BIN_BITS{1'b0}};
range_rd_addr <= {RANGE_BIN_BITS{1'b0}};
detect_rd_idx <= 11'd0;
detect_rd_addr <= 11'd0;
mag_rd_addr <= 14'd0;
detect_rd_idx <= {DETECT_BYTE_ADDR_W{1'b0}};
detect_rd_addr <= {DETECT_BYTE_ADDR_W{1'b0}};
mag_rd_addr <= {FRAME_ADDR_W{1'b0}};
rd_state <= RD_IDLE;
rd_byte_cnt <= 2'd0;
rd_cmd_complete <= 1'b0;
@@ -891,12 +901,12 @@ always @(posedge ft_clk or negedge ft_effective_reset_n) begin
else if (frame_ready_ft && ft_rxf_n) begin
wr_state <= WR_FRAME_HDR;
wr_byte_idx <= 16'd0;
bram_rd_cell <= 14'd0;
bram_rd_cell <= {FRAME_ADDR_W{1'b0}};
range_rd_idx <= {RANGE_BIN_BITS{1'b0}};
range_rd_addr <= {RANGE_BIN_BITS{1'b0}}; // Pre-load first read addr
detect_rd_idx <= 11'd0;
detect_rd_addr <= 11'd0;
mag_rd_addr <= 14'd0;
detect_rd_idx <= {DETECT_BYTE_ADDR_W{1'b0}};
detect_rd_addr <= {DETECT_BYTE_ADDR_W{1'b0}};
mag_rd_addr <= {FRAME_ADDR_W{1'b0}};
wr_byte_phase <= 1'b0;
end
end
@@ -960,8 +970,8 @@ always @(posedge ft_clk or negedge ft_effective_reset_n) begin
if (wr_byte_idx == RANGE_SECTION_BYTES[15:0] - 16'd1) begin
wr_byte_idx <= 16'd0;
wr_byte_phase <= 1'b0;
bram_rd_cell <= 14'd0;
mag_rd_addr <= 14'd0;
bram_rd_cell <= {FRAME_ADDR_W{1'b0}};
mag_rd_addr <= {FRAME_ADDR_W{1'b0}};
if (stream_flags_snapshot[1])
wr_state <= WR_DOPPLER_DATA;
else if (stream_flags_snapshot[2])
@@ -985,8 +995,8 @@ always @(posedge ft_clk or negedge ft_effective_reset_n) begin
end else begin
ft_data_out <= mag_rd_data[7:0];
wr_byte_phase <= 1'b0;
bram_rd_cell <= bram_rd_cell + 14'd1;
mag_rd_addr <= bram_rd_cell + 14'd1;
bram_rd_cell <= bram_rd_cell + {{(FRAME_ADDR_W-1){1'b0}}, 1'b1};
mag_rd_addr <= bram_rd_cell + {{(FRAME_ADDR_W-1){1'b0}}, 1'b1};
end
wr_byte_idx <= wr_byte_idx + 16'd1;
@@ -994,8 +1004,8 @@ always @(posedge ft_clk or negedge ft_effective_reset_n) begin
if (wr_byte_idx == DOPPLER_MAG_SECTION_BYTES[15:0] - 16'd1) begin
wr_byte_idx <= 16'd0;
wr_byte_phase <= 1'b0;
detect_rd_idx <= 11'd0;
detect_rd_addr <= 11'd0;
detect_rd_idx <= {DETECT_BYTE_ADDR_W{1'b0}};
detect_rd_addr <= {DETECT_BYTE_ADDR_W{1'b0}};
if (stream_flags_snapshot[2])
wr_state <= WR_DETECT_DATA;
else
@@ -1012,8 +1022,8 @@ always @(posedge ft_clk or negedge ft_effective_reset_n) begin
// 1-byte per cycle (BRAM read latency handled by pre-loading addr)
ft_data_out <= detect_rd_data;
detect_rd_idx <= detect_rd_idx + 11'd1;
detect_rd_addr <= detect_rd_idx + 11'd1;
detect_rd_idx <= detect_rd_idx + {{(DETECT_BYTE_ADDR_W-1){1'b0}}, 1'b1};
detect_rd_addr <= detect_rd_idx + {{(DETECT_BYTE_ADDR_W-1){1'b0}}, 1'b1};
wr_byte_idx <= wr_byte_idx + 16'd1;