PLFM_RADAR/9_Firmware/9_2_FPGA/chirp_reference_rom.v
Jason 4238eb1b99 chirp-v2 PR-C: chirp_reference_rom replaces chirp_memory_loader_param
Drop the chirp-v1 1-bit use_long_chirp memory loader and its 6 .mem files;
introduce chirp_reference_rom — wave_sel-native, single 8192x16 BRAM array
per Q15 lane, 4-region init (SHORT, MEDIUM, LONG seg0/seg1) loaded from the
PR-B mem files. Same 1-clk read latency as the legacy module so the RX-B
autocorrelation alignment fix carries through unchanged.

Receiver-side wave_sel shim added in radar_receiver_final.v:
  wire [1:0] wave_sel = use_long_chirp ? RP_WAVE_LONG : RP_WAVE_SHORT;
This is a 1-line transitional bridge while radar_mode_controller still
emits 1-bit use_long_chirp; PR-D deletes the shim and wires chirp_scheduler
straight through. MEDIUM is loaded into the ROM but unreachable through
the production path until PR-D.

BRAM cost: 8 RAMB18 (was 6 in chirp-v1). +2 BRAM is the cost of adding
MEDIUM to the waveform set; not avoidable.

Files added:
  - chirp_reference_rom.v
Files removed:
  - chirp_memory_loader_param.v
  - long_chirp_seg{0,1}_{i,q}.mem (4 files)
  - short_chirp_{i,q}.mem (2 files)
  - tb/cosim/validate_mem_files.py (legacy file-set validator; replaced by
    gen_chirp_mem.py's internal verify_phase_match)
  - tb/cosim/analyze_short_chirp_mismatch.py (one-shot tool from the
    chirp-v1 TX-I investigation; finding incorporated, references the
    deleted short_chirp_*.mem files)
Files updated for module rename:
  - radar_receiver_final.v        — instance, comments, wave_sel shim
  - radar_mode_controller.v       — header comment
  - matched_filter_processing_chain.v — header comment
  - scripts/200t/build_200t.tcl   — explicit RTL list
  - run_regression.sh             — 5 spots
  - tb/tb_rxb_fullchain_latency.v — instance, wave_sel shim, mem filenames,
                                    SHORT_LEN 50 → 100 (1 µs at 100 MHz)
  - tb/tb_system_e2e.v            — header comment

Verification:
  - chirp_reference_rom standalone iverilog compile: clean
  - Full receiver chain compile (21 RTL files): clean
  - tb_rxb_fullchain_latency runs end-to-end with new ROM + new mem files
    + 100-sample SHORT chirp; autocorrelation peak at bin 0, peak |I|+|Q|
    = 15115. Confirms 1-clk ROM read latency is preserved and the RX-B
    direct-wire-with-1-FF alignment still holds.
  - 50T build script (scripts/50t/build_50t.tcl) uses glob *.v — no edit
    needed; it picks up the new file automatically.
2026-04-30 19:37:43 +05:45

122 lines
5.9 KiB
Verilog

`timescale 1ns / 1ps
`include "radar_params.vh"
// ============================================================================
// chirp_reference_rom.v — 3-waveform matched-filter reference ROM (RX side)
// ============================================================================
// Replaces the chirp-v1 chirp_memory_loader_param.v (1-bit `use_long_chirp`,
// 6 .mem files, separate BRAMs for long/short).
//
// Carries one of {SHORT, MEDIUM, LONG} via wave_sel[1:0] — see RP_WAVE_*
// in radar_params.vh. The .mem files (PR-B) are uniformly 2048 entries each
// in Q15 I/Q hex; LONG occupies two 2048 segments; SHORT and MEDIUM each
// occupy a single 2048 segment with internal zero-pad past the chirp end.
//
// BRAM layout (single 8192x16 array per lane — Vivado infers 4 RAMB18/lane,
// 8 RAMB18 total. Same cost as chirp-v1 dual-array layout because LONG
// already needed 4 RAMB18; folding SHORT and MEDIUM into the same address
// space costs the 4 BRAMs we'd add for medium anyway):
//
// addr[12:11] region source files
// --------- --------------------- --------------------------------
// 2'b00 SHORT ([0..2047]) rx_short_{i,q}.mem
// 2'b01 MEDIUM ([0..2047]) rx_medium_{i,q}.mem
// 2'b10 LONG seg0 ([0..2047]) rx_long_seg0_{i,q}.mem
// 2'b11 LONG seg1 ([0..2047]) rx_long_seg1_{i,q}.mem
//
// Read addressing:
// case (wave_sel)
// RP_WAVE_SHORT: full_addr = {2'b00, sample_addr}
// RP_WAVE_MEDIUM: full_addr = {2'b01, sample_addr}
// RP_WAVE_LONG: full_addr = {1'b1, segment_select[0], sample_addr}
// default: (RP_WAVE_RESERVED) zero-output, mem_ready still pulses
// endcase
//
// Output semantics — drop-in compatible with chirp_memory_loader_param:
// - Synchronous read: ref_i / ref_q valid 1 clk after mem_request.
// - mem_ready pulses with ref data (1 clk after mem_request).
// - SAME 1-cycle latency as the legacy module (preserves RX-B autocorrelation
// peak alignment validated by tb_rxb_fullchain_latency).
//
// REQP-1839/1840 compliance (BRAM output registers cannot have async resets):
// - The BRAM read block uses a SYNCHRONOUS reset, which Vivado maps to the
// RAMB18 RSTREGB port. mem_ready (a non-BRAM control register) keeps the
// async reset for clean post-reset behavior. Same split as the legacy
// chirp_memory_loader_param.v.
// ============================================================================
module chirp_reference_rom (
input wire clk,
input wire reset_n,
input wire [1:0] wave_sel, // RP_WAVE_{SHORT,MEDIUM,LONG}
input wire [1:0] segment_select, // [0]=LONG seg index; ignored for SHORT/MEDIUM
input wire mem_request,
input wire [10:0] sample_addr, // 0..2047 within the active waveform/segment
output reg [15:0] ref_i,
output reg [15:0] ref_q,
output reg mem_ready
);
// -----------------------------------------------------------------------
// BRAM arrays (one per Q15 lane). Vivado infers RAMB18 with sync read.
// -----------------------------------------------------------------------
(* ram_style = "block" *) reg [15:0] mem_i [0:8191];
(* ram_style = "block" *) reg [15:0] mem_q [0:8191];
// -----------------------------------------------------------------------
// Initialization — load 4 distinct .mem files into 4 contiguous regions
// of the unified BRAM. $readmemh range form lets us target each 2048-cell
// segment independently. Vivado honors these for RAMB18 init contents.
// -----------------------------------------------------------------------
initial begin
$readmemh("rx_short_i.mem", mem_i, 0, 2047);
$readmemh("rx_short_q.mem", mem_q, 0, 2047);
$readmemh("rx_medium_i.mem", mem_i, 2048, 4095);
$readmemh("rx_medium_q.mem", mem_q, 2048, 4095);
$readmemh("rx_long_seg0_i.mem", mem_i, 4096, 6143);
$readmemh("rx_long_seg0_q.mem", mem_q, 4096, 6143);
$readmemh("rx_long_seg1_i.mem", mem_i, 6144, 8191);
$readmemh("rx_long_seg1_q.mem", mem_q, 6144, 8191);
end
// -----------------------------------------------------------------------
// Address mux — combinational. Encodes the region select into addr[12:11]
// and passes sample_addr through addr[10:0].
// -----------------------------------------------------------------------
reg [12:0] full_addr;
always @(*) begin
case (wave_sel)
`RP_WAVE_SHORT: full_addr = {2'b00, sample_addr};
`RP_WAVE_MEDIUM: full_addr = {2'b01, sample_addr};
`RP_WAVE_LONG: full_addr = {1'b1, segment_select[0], sample_addr};
default: full_addr = 13'd0; // RP_WAVE_RESERVED — read-zero region
endcase
end
// -----------------------------------------------------------------------
// BRAM read block — sync-only, sync reset (REQP-1839/1840). Single stage:
// ref_i / ref_q valid 1 clk after mem_request, matching legacy timing.
// -----------------------------------------------------------------------
always @(posedge clk) begin
if (!reset_n) begin
ref_i <= 16'd0;
ref_q <= 16'd0;
end else if (mem_request) begin
ref_i <= mem_i[full_addr];
ref_q <= mem_q[full_addr];
end
end
// -----------------------------------------------------------------------
// Control register — async-resettable. mem_ready follows mem_request by
// 1 clk to match the BRAM read latency.
// -----------------------------------------------------------------------
always @(posedge clk or negedge reset_n) begin
if (!reset_n)
mem_ready <= 1'b0;
else
mem_ready <= mem_request;
end
endmodule