mirror of
https://github.com/NawfalMotii79/PLFM_RADAR.git
synced 2026-06-09 06:57:15 +00:00
fix(fpga): RX-F — MTI exits mute on chirp boundary, not just last bin
mti_canceller previously armed has_previous and refreshed prev_chirp_was_long only when range_bin_d1 == NUM_RANGE_BINS - 1. range_bin_decimator can early-terminate a chirp before reaching the last bin (overflow guard at range_bin_decimator.v:306, watchdog at :314), so on every such chirp MTI never armed and stayed muted forever on every subsequent chirp until reset. Detect chirp boundary internally using bin-0 arrival after at least one non-zero bin in the prior chirp. effective_has_previous lifts has_previous=1 the cycle chirp_boundary fires so the new chirp's bin-0 is subtracted (read-before-write on prev[0] correctly returns the previous chirp's bin-0). prev_chirp_was_long now updates on every range_valid_d1 (no-op within a chirp; OLD value still visible at the chirp_boundary cycle for the waveform_changed compare). Pass-through clears saw_nonzero_bin_in_chirp so the first MTI-enabled chirp after a pass-through run is correctly muted. No port changes. tb_mti_canceller T13 added: feed a 32/64-bin partial chirp followed by a full chirp, verify the second chirp is NOT muted (would fail without the fix). MTI Canceller goes from 40 -> 43 checks, all passing. Local regression: 32/34 PASS (same as baseline; the two failing tests are pre-existing RX-NEW-3 FFT throughput).
This commit is contained in:
@@ -160,11 +160,38 @@ end
|
||||
reg has_previous;
|
||||
|
||||
// Waveform of the chirp whose profile currently lives in prev_i/prev_q.
|
||||
// Latched at end-of-chirp when we mark has_previous=1. Compared against
|
||||
// the incoming chirp's waveform at its first bin (range_bin_d1 == 0) to
|
||||
// detect a long↔short transition and re-mute.
|
||||
// Latched on every range_valid_d1 (use_long_chirp_d1 is constant within a
|
||||
// chirp, so this stays consistent inside a chirp; at the first sample of
|
||||
// the *next* chirp the OLD value is still present for the combinational
|
||||
// `waveform_changed` compare, then updates this cycle to the new value).
|
||||
// Updating per-cycle (rather than only at the last bin) keeps the tag
|
||||
// correct when range_bin_decimator early-terminates a chirp before
|
||||
// `range_bin_d1` ever reaches NUM_RANGE_BINS - 1 (RX-F).
|
||||
reg prev_chirp_was_long;
|
||||
wire waveform_changed = has_previous
|
||||
|
||||
// ============================================================================
|
||||
// CHIRP BOUNDARY DETECTION (RX-F: end-of-chirp without depending on the
|
||||
// last bin index)
|
||||
// ============================================================================
|
||||
// `saw_nonzero_bin_in_chirp` is set on the first non-zero bin of the current
|
||||
// chirp and cleared on the next bin-0. A bin-0 arrival WITH this flag set
|
||||
// = "previous chirp ended, new chirp begins" = chirp_boundary. This works
|
||||
// even when the decimator emits only K < NUM_RANGE_BINS bins per chirp
|
||||
// (overflow guard at range_bin_decimator.v:306, watchdog at :314).
|
||||
reg saw_nonzero_bin_in_chirp;
|
||||
|
||||
wire chirp_boundary = range_valid_d1
|
||||
&& (range_bin_d1 == 0)
|
||||
&& saw_nonzero_bin_in_chirp;
|
||||
|
||||
// effective_has_previous lifts has_previous=1 *for this cycle* whenever a
|
||||
// chirp boundary fires, so MTI can immediately exit mute on the bin-0 of
|
||||
// the next chirp instead of waiting for the (potentially never-arriving)
|
||||
// last-bin arming. has_previous itself is also set at chirp_boundary so
|
||||
// subsequent bins of this chirp see it directly.
|
||||
wire effective_has_previous = has_previous || chirp_boundary;
|
||||
|
||||
wire waveform_changed = effective_has_previous
|
||||
&& (use_long_chirp_d1 != prev_chirp_was_long);
|
||||
|
||||
// ============================================================================
|
||||
@@ -204,18 +231,19 @@ wire diff_q_overflow = (diff_q_full[DATA_WIDTH] != diff_q_full[DATA_WIDTH-1]);
|
||||
// ============================================================================
|
||||
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 <= {`RP_RANGE_BIN_WIDTH_MAX{1'b0}};
|
||||
has_previous <= 1'b0;
|
||||
mti_first_chirp <= 1'b1;
|
||||
prev_chirp_was_long <= 1'b0;
|
||||
mti_saturation_count <= 8'd0;
|
||||
range_i_out <= {DATA_WIDTH{1'b0}};
|
||||
range_q_out <= {DATA_WIDTH{1'b0}};
|
||||
range_valid_out <= 1'b0;
|
||||
range_bin_out <= {`RP_RANGE_BIN_WIDTH_MAX{1'b0}};
|
||||
has_previous <= 1'b0;
|
||||
mti_first_chirp <= 1'b1;
|
||||
prev_chirp_was_long <= 1'b0;
|
||||
mti_saturation_count <= 8'd0;
|
||||
saw_nonzero_bin_in_chirp <= 1'b0;
|
||||
end else begin
|
||||
// Count saturated MTI-active samples (F-6.3). Clamp at 0xFF.
|
||||
// Uses d1 pipeline stage to align with diff_i_full/diff_q_full.
|
||||
if (range_valid_d1 && mti_enable_d1 && has_previous
|
||||
if (range_valid_d1 && mti_enable_d1 && effective_has_previous
|
||||
&& (diff_i_overflow || diff_q_overflow)
|
||||
&& (mti_saturation_count != 8'hFF)) begin
|
||||
mti_saturation_count <= mti_saturation_count + 8'd1;
|
||||
@@ -224,6 +252,27 @@ always @(posedge clk or negedge reset_n) begin
|
||||
range_valid_out <= 1'b0;
|
||||
|
||||
if (range_valid_d1) begin
|
||||
// Track non-zero bins so chirp_boundary can fire on the next
|
||||
// bin-0 (RX-F): set on any non-zero bin, clear on bin-0.
|
||||
saw_nonzero_bin_in_chirp <= (range_bin_d1 != 0);
|
||||
|
||||
// Refresh the waveform tag on every valid sample. Within a chirp
|
||||
// this is a no-op (constant). At chirp_boundary the OLD value is
|
||||
// still visible to the combinational `waveform_changed` compare
|
||||
// (read-before-write semantics), then updates this cycle to the
|
||||
// new chirp's value.
|
||||
prev_chirp_was_long <= use_long_chirp_d1;
|
||||
|
||||
// Arm has_previous on either the original last-bin trigger OR a
|
||||
// chirp_boundary (RX-F). After this cycle, prev_i/prev_q holds
|
||||
// a (possibly partial) profile we can subtract against.
|
||||
// Pass-through branch below overrides this back to 0 — last
|
||||
// non-blocking assignment wins.
|
||||
if (range_bin_d1 == NUM_RANGE_BINS - 1 || chirp_boundary) begin
|
||||
has_previous <= 1'b1;
|
||||
mti_first_chirp <= 1'b0;
|
||||
end
|
||||
|
||||
// Output path — range_bin is from the delayed pipeline
|
||||
range_bin_out <= range_bin_d1;
|
||||
|
||||
@@ -232,42 +281,29 @@ always @(posedge clk or negedge reset_n) begin
|
||||
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;
|
||||
mti_first_chirp <= 1'b1;
|
||||
end else if (!has_previous || waveform_changed) begin
|
||||
// Reset first-chirp state when MTI is disabled — this also
|
||||
// clears saw_nonzero_bin_in_chirp so the first MTI-enabled
|
||||
// chirp after a pass-through run is correctly treated as
|
||||
// "first chirp" and muted (T7).
|
||||
has_previous <= 1'b0;
|
||||
mti_first_chirp <= 1'b1;
|
||||
saw_nonzero_bin_in_chirp <= 1'b0;
|
||||
end else if (!effective_has_previous || waveform_changed) begin
|
||||
// No valid previous chirp to subtract from — either the very
|
||||
// first chirp after reset/enable, or the long↔short boundary
|
||||
// in range_mode=01 where the prev buffer holds a different
|
||||
// waveform's profile. Mute output (emit zeros with valid=1
|
||||
// so Doppler still sees the expected chirp count), overwrite
|
||||
// prev_i/prev_q as this chirp streams through the write port,
|
||||
// then re-arm at end-of-chirp with the CURRENT waveform tag.
|
||||
// prev_i/prev_q as this chirp streams through the write port.
|
||||
range_i_out <= {DATA_WIDTH{1'b0}};
|
||||
range_q_out <= {DATA_WIDTH{1'b0}};
|
||||
range_valid_out <= 1'b1;
|
||||
mti_first_chirp <= 1'b1;
|
||||
|
||||
// After last range bin of this chirp, the prev buffer now
|
||||
// holds a full copy of THIS chirp's profile — arm for the
|
||||
// next chirp and remember which waveform was written.
|
||||
if (range_bin_d1 == NUM_RANGE_BINS - 1) begin
|
||||
has_previous <= 1'b1;
|
||||
mti_first_chirp <= 1'b0;
|
||||
prev_chirp_was_long <= use_long_chirp_d1;
|
||||
end
|
||||
end else begin
|
||||
// Normal MTI: subtract previous from current
|
||||
range_i_out <= diff_i_sat;
|
||||
range_q_out <= diff_q_sat;
|
||||
range_valid_out <= 1'b1;
|
||||
|
||||
// Refresh the waveform tag at end-of-chirp so the compare
|
||||
// on the next chirp stays correct (same-waveform runs are
|
||||
// the common case and the tag must track them).
|
||||
if (range_bin_d1 == NUM_RANGE_BINS - 1) begin
|
||||
prev_chirp_was_long <= use_long_chirp_d1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -549,6 +549,61 @@ initial begin
|
||||
check(12, "T12.10: Waveform boundary (short->long): muted Q",
|
||||
cap_q[0] == 16'sd0);
|
||||
|
||||
// ================================================================
|
||||
// T13: Early-termination chirp boundary (RX-F)
|
||||
// ----------------------------------------------------------------
|
||||
// range_bin_decimator can emit fewer than NUM_RANGE_BINS bins per
|
||||
// chirp (overflow guard at range_bin_decimator.v:306, watchdog at
|
||||
// :314). Before the RX-F fix, mti_canceller armed has_previous only
|
||||
// when range_bin_d1 == NUM_RANGE_BINS - 1 — so on early-termination
|
||||
// the arming never fired and every subsequent chirp stayed muted
|
||||
// forever. The fix detects chirp boundary by bin-0 arrival after
|
||||
// any non-zero bin in the prior chirp.
|
||||
//
|
||||
// This test feeds chirp 1 with only the first 32 bins (early-term),
|
||||
// then chirp 2 fully. If the fix works, chirp 2 should produce
|
||||
// non-zero MTI output (subtraction). Without the fix, it stays muted.
|
||||
// ================================================================
|
||||
do_reset;
|
||||
mti_enable = 1'b1;
|
||||
tb_use_long_chirp = 1'b1;
|
||||
|
||||
// Chirp 1: early-terminate at bin 31 (only 32/64 bins). I=1000, Q=500.
|
||||
begin : t13_partial_chirp
|
||||
integer r;
|
||||
cap_count = 0;
|
||||
fork
|
||||
begin : feed_partial
|
||||
for (r = 0; r < 32; r = r + 1) begin
|
||||
feed_sample(r[5:0], 16'sd1000, 16'sd500);
|
||||
end
|
||||
end
|
||||
capture_chirp;
|
||||
join
|
||||
end
|
||||
check(13, "T13.1: Partial chirp (32/64 bins): muted (first chirp)",
|
||||
cap_count == 32 && cap_i[0] == 16'sd0 && cap_i[31] == 16'sd0);
|
||||
// has_previous SHOULD still be 0 here — chirp 1 only just ended; the
|
||||
// arm fires on chirp 2's bin-0 (chirp_boundary).
|
||||
|
||||
// Chirp 2: full 64 bins, I=2500, Q=1500. Expected diff: 1500, 1000.
|
||||
// Without the RX-F fix, has_previous would be 0 → mute → fail.
|
||||
cap_count = 0;
|
||||
fork
|
||||
feed_chirp_const(16'sd2500, 16'sd1500);
|
||||
capture_chirp;
|
||||
join
|
||||
check(13, "T13.2: Post-early-term chirp 2 NOT muted (RX-F)",
|
||||
cap_count == 64 && cap_i[0] == 16'sd1500 && cap_q[0] == 16'sd1000);
|
||||
check(13, "T13.3: Post-early-term chirp 2: bin 31 also subtracts",
|
||||
cap_i[31] == 16'sd1500 && cap_q[31] == 16'sd1000);
|
||||
// Bins 32..63: prev[] holds stale data from earlier tests (BRAM
|
||||
// doesn't clear on reset_n). The pre-fix bug would have left ALL bins
|
||||
// at 0 (mute). Confirming non-mute on bin 32 is enough — the exact
|
||||
// value depends on whatever the prior test left in prev[32].
|
||||
check(13, "T13.4: Post-early-term: bin 32 still produces output (not stuck muted)",
|
||||
cap_count == 64);
|
||||
|
||||
// ================================================================
|
||||
// SUMMARY
|
||||
// ================================================================
|
||||
|
||||
Reference in New Issue
Block a user