mirror of
https://github.com/NawfalMotii79/PLFM_RADAR.git
synced 2026-06-10 15:31:21 +00:00
merge(wave2): manual resolution of 6 shared files — fft-2048 × p0 audit
Hand-merged files modified on both fix/pre-bringup-audit-p0 and feat/fft-2048-upgrade. Wave 1 (commit60e49c7) took 20 files from fft verbatim; this wave resolves the overlap. - run_regression.sh: 3-way merge. Adopts fft's ${RECEIVER_RTL[@]} array refactor and drops the self-blessing golden pair from p0. Skip count bumped to 5. - usb_data_interface.v (FT601/200T): p0 FSM + clock-loss watchdog kept wholesale; widened stream_control 3 -> 6 bits to carry fft's extended mode bits through the CDC sync chain and the 0xFF status word. - mti_canceller.v: fft's BRAM-inferred 512-range-bin implementation as the base, with p0's F-6.3 saturation counter grafted onto the d1 pipeline stage. Overflow detection uses the top-two-bits disagreement on diff_{i,q}_full (DATA_WIDTH+1 signed). - radar_receiver_final.v: fft's 2048-pt / 512-bin structure + p0 diagnostic plumbing (ADC overrange sticky+CDC, DDC diagnostics, tx_frame_start edge detector replacing chirp_counter frame sync, mti_saturation_count, range_decim_watchdog). - radar_system_top.v: clean 3-way merge, orthogonal regions (+38 / -27). - usb_data_interface_ft2232h.v (FT2232H/50T): fft's per-frame bulk BRAM rewrite kept wholesale. Ported two p0 items that are orthogonal to the write FSM: * ft_clk-loss watchdog (heartbeat + 2FF ASYNC_REG sync + 16-bit timeout) ORed into a 2FF sync'd ft_effective_reset_n for the FSM. * rd_cmd_complete flag so RD_DEASSERT can distinguish a legitimate 3-byte completion from an ft_rxf_n abort that also zeros rd_byte_cnt. Deliberately NOT taken from2401f5f: cic_decimator_4x_enhanced.v and ddc_400m.v reset-strategy changes. Those conflict with p0's shipped registered-sync-reset + max_fanout=25 distribution, which is already timing-clean on the production build.
This commit is contained in:
@@ -19,25 +19,33 @@
|
||||
* mti_out_i[r] = current_i[r] - previous_i[r]
|
||||
* mti_out_q[r] = current_q[r] - previous_q[r]
|
||||
*
|
||||
* The previous chirp's 64 range bins are stored in a small BRAM.
|
||||
* The previous chirp's 512 range bins are stored in BRAM (inferred via
|
||||
* sync-only read/write always blocks — NO async reset on memory arrays).
|
||||
* On the very first chirp after reset (or enable), there is no previous
|
||||
* data — output is zero (muted) for that first chirp.
|
||||
*
|
||||
* When mti_enable=0, the module is a transparent pass-through with zero
|
||||
* latency penalty (data goes straight through combinationally registered).
|
||||
* When mti_enable=0, the module is a transparent pass-through.
|
||||
*
|
||||
* Resources:
|
||||
* - 2 BRAM18 (64 x 16-bit I + 64 x 16-bit Q) or distributed RAM
|
||||
* - ~30 LUTs (subtract + mux)
|
||||
* - ~40 FFs (pipeline + control)
|
||||
* BRAM inference note:
|
||||
* prev_i/prev_q arrays use dedicated sync-only always blocks for read
|
||||
* and write. This ensures Vivado infers BRAM (RAMB18) instead of fabric
|
||||
* FFs + mux trees. The registered read adds 1 cycle of latency, which
|
||||
* is compensated by a pipeline stage on the input data path.
|
||||
*
|
||||
* Resources (target):
|
||||
* - 2 BRAM18 (512 x 16-bit I + 512 x 16-bit Q)
|
||||
* - ~30 LUTs (subtract + mux + saturation)
|
||||
* - ~80 FFs (pipeline + control)
|
||||
* - 0 DSP48
|
||||
*
|
||||
* Clock domain: clk (100 MHz)
|
||||
*/
|
||||
|
||||
`include "radar_params.vh"
|
||||
|
||||
module mti_canceller #(
|
||||
parameter NUM_RANGE_BINS = 64,
|
||||
parameter DATA_WIDTH = 16
|
||||
parameter NUM_RANGE_BINS = `RP_NUM_RANGE_BINS, // 512
|
||||
parameter DATA_WIDTH = `RP_DATA_WIDTH // 16
|
||||
) (
|
||||
input wire clk,
|
||||
input wire reset_n,
|
||||
@@ -46,13 +54,13 @@ module mti_canceller #(
|
||||
input wire signed [DATA_WIDTH-1:0] range_i_in,
|
||||
input wire signed [DATA_WIDTH-1:0] range_q_in,
|
||||
input wire range_valid_in,
|
||||
input wire [5:0] range_bin_in,
|
||||
input wire [`RP_RANGE_BIN_BITS-1:0] range_bin_in, // 9-bit
|
||||
|
||||
// ========== OUTPUT (to Doppler processor) ==========
|
||||
output reg signed [DATA_WIDTH-1:0] range_i_out,
|
||||
output reg signed [DATA_WIDTH-1:0] range_q_out,
|
||||
output reg range_valid_out,
|
||||
output reg [5:0] range_bin_out,
|
||||
output reg [`RP_RANGE_BIN_BITS-1:0] range_bin_out, // 9-bit
|
||||
|
||||
// ========== CONFIGURATION ==========
|
||||
input wire mti_enable, // 1=MTI active, 0=pass-through
|
||||
@@ -67,30 +75,79 @@ module mti_canceller #(
|
||||
);
|
||||
|
||||
// ============================================================================
|
||||
// PREVIOUS CHIRP BUFFER (64 x 16-bit I, 64 x 16-bit Q)
|
||||
// PREVIOUS CHIRP BUFFER (512 x 16-bit I, 512 x 16-bit Q)
|
||||
// ============================================================================
|
||||
// Small enough for distributed RAM on XC7A200T (64 entries).
|
||||
// Using separate I/Q arrays for clean read/write.
|
||||
// BRAM-inferred on XC7A50T/200T (512 entries, sync-only read/write).
|
||||
// Using separate I/Q arrays for clean dual-port inference.
|
||||
|
||||
reg signed [DATA_WIDTH-1:0] prev_i [0:NUM_RANGE_BINS-1];
|
||||
reg signed [DATA_WIDTH-1:0] prev_q [0:NUM_RANGE_BINS-1];
|
||||
(* ram_style = "block" *) reg signed [DATA_WIDTH-1:0] prev_i [0:NUM_RANGE_BINS-1];
|
||||
(* ram_style = "block" *) reg signed [DATA_WIDTH-1:0] prev_q [0:NUM_RANGE_BINS-1];
|
||||
|
||||
// ============================================================================
|
||||
// INPUT PIPELINE STAGE (1 cycle delay to match BRAM read latency)
|
||||
// ============================================================================
|
||||
// Declarations must precede the BRAM write block that references them.
|
||||
|
||||
reg signed [DATA_WIDTH-1:0] range_i_d1, range_q_d1;
|
||||
reg range_valid_d1;
|
||||
reg [`RP_RANGE_BIN_BITS-1:0] range_bin_d1;
|
||||
reg mti_enable_d1;
|
||||
|
||||
always @(posedge clk or negedge reset_n) begin
|
||||
if (!reset_n) begin
|
||||
range_i_d1 <= {DATA_WIDTH{1'b0}};
|
||||
range_q_d1 <= {DATA_WIDTH{1'b0}};
|
||||
range_valid_d1 <= 1'b0;
|
||||
range_bin_d1 <= {`RP_RANGE_BIN_BITS{1'b0}};
|
||||
mti_enable_d1 <= 1'b0;
|
||||
end else begin
|
||||
range_i_d1 <= range_i_in;
|
||||
range_q_d1 <= range_q_in;
|
||||
range_valid_d1 <= range_valid_in;
|
||||
range_bin_d1 <= range_bin_in;
|
||||
mti_enable_d1 <= mti_enable;
|
||||
end
|
||||
end
|
||||
|
||||
// ============================================================================
|
||||
// BRAM WRITE PORT (sync only — NO async reset for BRAM inference)
|
||||
// ============================================================================
|
||||
// Writes the current chirp sample into prev_i/prev_q for next chirp's
|
||||
// subtraction. Uses the delayed (d1) signals so the write happens 1 cycle
|
||||
// after the read address is presented, avoiding RAW hazards.
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (range_valid_d1) begin
|
||||
prev_i[range_bin_d1] <= range_i_d1;
|
||||
prev_q[range_bin_d1] <= range_q_d1;
|
||||
end
|
||||
end
|
||||
|
||||
// ============================================================================
|
||||
// BRAM READ PORT (sync only — 1 cycle read latency)
|
||||
// ============================================================================
|
||||
// Address is always driven by range_bin_in (cycle 0). Read data appears
|
||||
// on prev_i_rd / prev_q_rd at cycle 1, aligned with the d1 pipeline stage.
|
||||
|
||||
reg signed [DATA_WIDTH-1:0] prev_i_rd, prev_q_rd;
|
||||
|
||||
always @(posedge clk) begin
|
||||
prev_i_rd <= prev_i[range_bin_in];
|
||||
prev_q_rd <= prev_q[range_bin_in];
|
||||
end
|
||||
|
||||
// Track whether we have valid previous data
|
||||
reg has_previous;
|
||||
|
||||
// ============================================================================
|
||||
// MTI PROCESSING
|
||||
// MTI PROCESSING (operates on d1 pipeline stage + BRAM read data)
|
||||
// ============================================================================
|
||||
|
||||
// Read previous chirp data (combinational)
|
||||
wire signed [DATA_WIDTH-1:0] prev_i_rd = prev_i[range_bin_in];
|
||||
wire signed [DATA_WIDTH-1:0] prev_q_rd = prev_q[range_bin_in];
|
||||
|
||||
// Compute difference with saturation
|
||||
// Subtraction can produce DATA_WIDTH+1 bits; saturate back to DATA_WIDTH.
|
||||
wire signed [DATA_WIDTH:0] diff_i_full = {range_i_in[DATA_WIDTH-1], range_i_in}
|
||||
wire signed [DATA_WIDTH:0] diff_i_full = {range_i_d1[DATA_WIDTH-1], range_i_d1}
|
||||
- {prev_i_rd[DATA_WIDTH-1], prev_i_rd};
|
||||
wire signed [DATA_WIDTH:0] diff_q_full = {range_q_in[DATA_WIDTH-1], range_q_in}
|
||||
wire signed [DATA_WIDTH:0] diff_q_full = {range_q_d1[DATA_WIDTH-1], range_q_d1}
|
||||
- {prev_q_rd[DATA_WIDTH-1], prev_q_rd};
|
||||
|
||||
// Saturate to DATA_WIDTH bits
|
||||
@@ -115,20 +172,21 @@ wire diff_i_overflow = (diff_i_full[DATA_WIDTH] != diff_i_full[DATA_WIDTH-1]);
|
||||
wire diff_q_overflow = (diff_q_full[DATA_WIDTH] != diff_q_full[DATA_WIDTH-1]);
|
||||
|
||||
// ============================================================================
|
||||
// MAIN LOGIC
|
||||
// MAIN OUTPUT LOGIC (operates on d1 pipeline stage)
|
||||
// ============================================================================
|
||||
always @(posedge clk or negedge reset_n) begin
|
||||
if (!reset_n) begin
|
||||
range_i_out <= {DATA_WIDTH{1'b0}};
|
||||
range_q_out <= {DATA_WIDTH{1'b0}};
|
||||
range_valid_out <= 1'b0;
|
||||
range_bin_out <= 6'd0;
|
||||
range_bin_out <= {`RP_RANGE_BIN_BITS{1'b0}};
|
||||
has_previous <= 1'b0;
|
||||
mti_first_chirp <= 1'b1;
|
||||
mti_saturation_count <= 8'd0;
|
||||
end else begin
|
||||
// Count saturated MTI-active samples (F-6.3). Clamp at 0xFF.
|
||||
if (range_valid_in && mti_enable && has_previous
|
||||
// Uses d1 pipeline stage to align with diff_i_full/diff_q_full.
|
||||
if (range_valid_d1 && mti_enable_d1 && has_previous
|
||||
&& (diff_i_overflow || diff_q_overflow)
|
||||
&& (mti_saturation_count != 8'hFF)) begin
|
||||
mti_saturation_count <= mti_saturation_count + 8'd1;
|
||||
@@ -136,18 +194,14 @@ always @(posedge clk or negedge reset_n) begin
|
||||
// Default: no valid output
|
||||
range_valid_out <= 1'b0;
|
||||
|
||||
if (range_valid_in) begin
|
||||
// Always store current sample as "previous" for next chirp
|
||||
prev_i[range_bin_in] <= range_i_in;
|
||||
prev_q[range_bin_in] <= range_q_in;
|
||||
if (range_valid_d1) begin
|
||||
// Output path — range_bin is from the delayed pipeline
|
||||
range_bin_out <= range_bin_d1;
|
||||
|
||||
// Output path
|
||||
range_bin_out <= range_bin_in;
|
||||
|
||||
if (!mti_enable) begin
|
||||
if (!mti_enable_d1) begin
|
||||
// Pass-through mode: no MTI processing
|
||||
range_i_out <= range_i_in;
|
||||
range_q_out <= range_q_in;
|
||||
range_i_out <= range_i_d1;
|
||||
range_q_out <= range_q_d1;
|
||||
range_valid_out <= 1'b1;
|
||||
// Reset first-chirp state when MTI is disabled
|
||||
has_previous <= 1'b0;
|
||||
@@ -161,7 +215,7 @@ always @(posedge clk or negedge reset_n) begin
|
||||
range_valid_out <= 1'b1;
|
||||
|
||||
// After last range bin of first chirp, mark previous as valid
|
||||
if (range_bin_in == NUM_RANGE_BINS - 1) begin
|
||||
if (range_bin_d1 == NUM_RANGE_BINS - 1) begin
|
||||
has_previous <= 1'b1;
|
||||
mti_first_chirp <= 1'b0;
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user