mirror of
https://github.com/NawfalMotii79/PLFM_RADAR.git
synced 2026-06-10 23:41:18 +00:00
PR-AB.b expanded commit 5: Beam-ready handshake (RTL + MCU + GUI)
Wire a per-frame MCU→FPGA "beam pattern ready" handshake so the chirp scheduler can stall between 48-chirp frames until the MCU finishes writing the next ADAR1000 pattern. The legacy unused stm32_new_chirp input on PD8 is repurposed as stm32_beam_ready; chirp_scheduler.v gets a new S_BEAM_WAIT state entered after each frame_pulse and an 80 ms watchdog so a missed MCU toggle degrades to wall-clock cadence with a sticky telemetry bit rather than stalling the radar. Cold-reset defaults the handshake off (host_handshake_enable=0, new opcode 0x1A); the GUI opts in once the MCU PD8 wiring is verified on the bench. Both the FT601 and FT2232H status word 4 paths get the new beam_handshake_watchdog_fired sticky at bit [1] (reclaimed from the range_mode retirement in commit 1). RTL: - chirp_scheduler.v: 2-FF ASYNC_REG sync on beam_ready_async; 1-cycle edge detect (any transition, MCU side uses HAL_GPIO_TogglePin); new S_BEAM_WAIT state entered at frame_pulse when host_handshake_enable=1; 23-bit beam_watchdog counter with BEAM_WATCHDOG_MAX = 8_000_000 (~80 ms at 100 MHz, ~10 nominal frames); beam_handshake_watchdog_fired output sticky across mixers_enable cycles, cleared only by reset_n; mid-wait disable releases the FSM so dropping the opcode never strands the radar between frames. - radar_receiver_final.v: thread stm32_beam_ready_async + host_handshake_enable + beam_handshake_watchdog_fired through the scheduler instance. - radar_system_top.v: rename input port stm32_new_chirp → stm32_beam_ready; add host_handshake_enable register (cold-reset = 1'b0); opcode 0x1A dispatch (value[0]); add rx_beam_handshake_watchdog wire; pack into status_words[4][1] in both USB paths. - radar_system_top_50t.v: rename wrapper port + sub-instance wiring. - usb_data_interface.v + usb_data_interface_ft2232h.v: add status_beam_handshake_watchdog input + 2-FF level CDC (same convention as F-6.4 / F-1.2 stickies); refresh word-4 layout doc comment; pack beam_handshake_wd_sync_1 into status_words[4][1]. XDC: - xc7a50t_ftg256.xdc + xc7a200t_fbg484.xdc: rename stm32_new_chirp port references to stm32_beam_ready (same PD8 pin, F13 on 50T / L18 on 200T). MCU: - main.h: add FPGA_BEAM_READY_Pin = GPIO_PIN_8 + FPGA_BEAM_READY_GPIO_Port = GPIOD alongside the existing FPGA_FRAME_PULSE alias. - main.cpp:runRadarPulseSequence: insert HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_8) after each setCustomBeamPattern16(RX) — once after the per-azimuth broadside (vector_0), once after matrix1, once after matrix2 — between the SPI burst completion and waitForFramePulse. GUI: - radar_protocol.py: Opcode.HANDSHAKE_ENABLE = 0x1A; StatusResponse.beam_handshake_watchdog = 0 default; parse word 4 bit [1] in parse_status_packet; update word-4 layout comment. - test_GUI_V65_Tk.py: add beam_handshake_watchdog kwarg to _make_status_packet (sets bit [1] of word 4); refresh test_parse_status_word4_layout_co_spec to cover the new bit (used+9=32); add test_parse_status_beam_handshake_watchdog round-trip; test_handshake_enable_opcode pins 0x1A; defaults / chirps_mismatch / agc-coexist tests gain a watchdog==0 assertion; bump test_all_rtl_opcodes_present expected set to include 0x17/0x18/0x19/0x1A. TB: - new tb_chirp_scheduler_handshake.v (16 checks): legacy open-loop, edge exit (rising + falling), 200-cycle idle hold, watchdog auto-advance via force on dut.beam_watchdog, sticky-survives-mixers_disable, mid-wait disable release, reset_n clears sticky. - run_regression.sh: register the new TB in PHASE 1. - tb_radar_receiver_final.v: tie the 3 new receiver ports off (beam_ready_async=0, handshake_enable=0, watchdog unconnected). - tb_system_mechanics.v / tb_system_opcodes.v: explicit .stm32_beam_ready(1'b0) connection (the cold-reset host_handshake_enable=0 keeps the FSM out of S_BEAM_WAIT). - tb_usb_data_interface.v / tb_usb_protocol_v2.v / tb_e2e_dsp_to_host.v / tb_ft2232h_frame_drop.v: tie .status_beam_handshake_watchdog(1'b0). Ride-along ruff sweep (14 → 0 across the repo): - tb/cosim/compare_independent.py: RUF003 — '5×' → 'at least 5x'. - tb/cosim/gen_e2e_expected.py: noqa: E402 on the post-sys.path import; drop unused EXPECTED_RANGE_BIN + EXPECTED_DOPPLER_BIN_PER_SF imports; fold the detect-class slot if/else into a ternary (SIM108). - tb/cosim/gen_e2e_stimulus.py: drop int() wrapping round() at four call sites (RUF046 — round() already returns int in Python 3); rewrite the range-bin derivation comment block from code-like `# range_bin = ...` to prose (ERA001); strip stray f from placeholder-free error string (F541). - tb/cosim/tb_e2e_dsp_to_host_parse.py: open(path, 'r') → open(path) (UP015). - v7/dashboard.py: '3×' → '3x' (RUF003); drop quotes from 'StatusResponse | None' annotation (UP037, file already has `from __future__ import annotations`). CI summary (all suites green pre-commit): - ruff: All checks passed! - FPGA regression (iverilog): 43 / 0 / 0 (incl. new handshake TB 16/16). - MCU tests: 51 / 0 + 34 / 0 + 13 / 13 ADAR1000_AGC. - GUI Tk (test_GUI_V65_Tk): 120 / 0. - GUI v7 (test_v7): 152 / 0. Production rollout note: bitstream cold-resets with host_handshake_enable=0 so existing flashes keep their open-loop cadence until the GUI sends opcode 0x1A=1. Once enabled, the per-pattern dwell tracks both the chirp ladder (PD14 frame_pulse from commit-3 work) and the MCU pattern-write completion (PD8 toggle from this commit), eliminating drift from the SPI burst timing.
This commit is contained in:
@@ -24,6 +24,15 @@
|
||||
* Pulse outputs (chirp_pulse, subframe_pulse, frame_pulse) are 1-cycle
|
||||
* positive pulses, not toggles.
|
||||
*
|
||||
* PR-AB.b expanded commit 5 — beam-ready handshake: when
|
||||
* host_handshake_enable=1, the FSM enters S_BEAM_WAIT after frame_pulse
|
||||
* and only fires the next frame's first chirp once it observes an edge on
|
||||
* beam_ready_async (MCU PD8 toggle) or the ~80 ms watchdog expires.
|
||||
* Watchdog timeout sets the sticky output beam_handshake_watchdog_fired
|
||||
* (cleared only by reset_n) so the host can spot patterns falling behind
|
||||
* the chirp ladder. host_handshake_enable=0 preserves the legacy
|
||||
* always-on chirp cadence.
|
||||
*
|
||||
* Clock domain: clk (100 MHz), async-low reset.
|
||||
*/
|
||||
|
||||
@@ -55,6 +64,15 @@ module chirp_scheduler (
|
||||
// TX-side cdc_async_fifo before mixers come up.
|
||||
input wire mixers_enable,
|
||||
|
||||
// PR-AB.b expanded commit 5: beam-ready handshake. beam_ready_async is the
|
||||
// raw MCU PD8 GPIO toggle (CDC-synchronized inside this module on `clk`).
|
||||
// host_handshake_enable gates whether the FSM stalls in S_BEAM_WAIT after
|
||||
// each frame_pulse. Cold-reset default at the top level is 1'b0 (legacy
|
||||
// open-loop cadence) — host enables via opcode 0x1A once the MCU's PD8
|
||||
// toggle wiring is in place.
|
||||
input wire beam_ready_async,
|
||||
input wire host_handshake_enable,
|
||||
|
||||
// ====== Outputs ======
|
||||
output reg [1:0] wave_sel, // canonical waveform identity
|
||||
output reg chirp_pulse, // 1-cycle pulse: chirp begins this clk
|
||||
@@ -66,7 +84,11 @@ module chirp_scheduler (
|
||||
// Currently selected timing for the in-flight chirp (PR-E TX async FIFO)
|
||||
output wire [15:0] cfg_chirp_cycles,
|
||||
output wire [15:0] cfg_listen_cycles,
|
||||
output wire [15:0] cfg_guard_cycles
|
||||
output wire [15:0] cfg_guard_cycles,
|
||||
|
||||
// PR-AB.b expanded commit 5: sticky handshake watchdog flag, cleared
|
||||
// only by reset_n. Plumbed into status_words[4][1] at the top level.
|
||||
output reg beam_handshake_watchdog_fired
|
||||
);
|
||||
|
||||
// ============================================================================
|
||||
@@ -140,17 +162,46 @@ assign cfg_chirp_cycles = sel_chirp_cycles;
|
||||
assign cfg_listen_cycles = sel_listen_cycles;
|
||||
assign cfg_guard_cycles = host_guard_cycles;
|
||||
|
||||
// ============================================================================
|
||||
// Beam-ready CDC + edge detection (PR-AB.b expanded commit 5).
|
||||
// beam_ready_async is a slow MCU GPIO toggle (PD8). Two ASYNC_REG flops bring
|
||||
// it into clk, then a one-cycle delay lets us detect any transition (rising or
|
||||
// falling) — the MCU drives via HAL_GPIO_TogglePin once per beam pattern, so
|
||||
// successive frames see alternating polarities.
|
||||
// ============================================================================
|
||||
(* ASYNC_REG = "TRUE" *) reg [1:0] beam_ready_sync;
|
||||
reg beam_ready_q_prev;
|
||||
always @(posedge clk or negedge reset_n) begin
|
||||
if (!reset_n) begin
|
||||
beam_ready_sync <= 2'b00;
|
||||
beam_ready_q_prev <= 1'b0;
|
||||
end else begin
|
||||
beam_ready_sync <= {beam_ready_sync[0], beam_ready_async};
|
||||
beam_ready_q_prev <= beam_ready_sync[1];
|
||||
end
|
||||
end
|
||||
wire beam_ready_q = beam_ready_sync[1];
|
||||
wire beam_ready_edge = (beam_ready_q != beam_ready_q_prev);
|
||||
|
||||
// ============================================================================
|
||||
// Main FSM — auto-scan over enabled sub-frames.
|
||||
// ============================================================================
|
||||
localparam S_IDLE = 3'd0;
|
||||
localparam S_CHIRP = 3'd1;
|
||||
localparam S_LISTEN = 3'd2;
|
||||
localparam S_GUARD = 3'd3;
|
||||
localparam S_ADVANCE = 3'd4;
|
||||
localparam S_IDLE = 3'd0;
|
||||
localparam S_CHIRP = 3'd1;
|
||||
localparam S_LISTEN = 3'd2;
|
||||
localparam S_GUARD = 3'd3;
|
||||
localparam S_ADVANCE = 3'd4;
|
||||
localparam S_BEAM_WAIT = 3'd5;
|
||||
|
||||
// Beam-ready watchdog: 23 bits at 100 MHz → ~83.9 ms = ~8 nominal frames
|
||||
// (frame ≈ 8.05 ms full 3-PRI ladder, less when subframes are masked). Long
|
||||
// enough to absorb MCU SPI bursts + scheduling jitter without auto-advancing,
|
||||
// short enough to keep the radar moving when a pattern write actually drops.
|
||||
localparam [22:0] BEAM_WATCHDOG_MAX = 23'd8_000_000;
|
||||
|
||||
reg [2:0] state;
|
||||
reg [16:0] timer; // 17 bits cover LONG+listen+guard worst case
|
||||
reg [22:0] beam_watchdog; // counts clk cycles while in S_BEAM_WAIT
|
||||
|
||||
// Pre-computed wires used inside the FSM advance logic so non-blocking
|
||||
// updates to subframe_id / wave_sel see the correct next value in the same
|
||||
@@ -168,14 +219,19 @@ always @(posedge clk or negedge reset_n) begin
|
||||
frame_pulse <= 1'b0;
|
||||
chirp_counter <= 6'd0;
|
||||
subframe_id <= 2'd0;
|
||||
beam_watchdog <= 23'd0;
|
||||
beam_handshake_watchdog_fired <= 1'b0;
|
||||
end else if (!mixers_enable) begin
|
||||
// Master disable — quiesce the FSM so chirp_pulse never asserts and
|
||||
// the TX side stays at idle.
|
||||
// the TX side stays at idle. beam_handshake_watchdog_fired is sticky
|
||||
// across mixers_enable cycles so the host can see late patterns even
|
||||
// after a soft restart.
|
||||
state <= S_IDLE;
|
||||
timer <= 17'd0;
|
||||
chirp_pulse <= 1'b0;
|
||||
subframe_pulse <= 1'b0;
|
||||
frame_pulse <= 1'b0;
|
||||
beam_watchdog <= 23'd0;
|
||||
end else begin
|
||||
// Pulses default low — set high for one cycle on relevant transitions.
|
||||
chirp_pulse <= 1'b0;
|
||||
@@ -219,10 +275,39 @@ always @(posedge clk or negedge reset_n) begin
|
||||
subframe_pulse <= 1'b1;
|
||||
subframe_id <= next_sf;
|
||||
wave_sel <= subframe_to_wave(next_sf);
|
||||
if (next_sf == first_sf)
|
||||
if (next_sf == first_sf) begin
|
||||
// Frame wrap — emit frame_pulse and (if enabled) stall
|
||||
// in S_BEAM_WAIT until the MCU acknowledges via PD8.
|
||||
frame_pulse <= 1'b1;
|
||||
chirp_pulse <= 1'b1;
|
||||
state <= S_CHIRP;
|
||||
if (host_handshake_enable) begin
|
||||
beam_watchdog <= 23'd0;
|
||||
state <= S_BEAM_WAIT;
|
||||
end else begin
|
||||
chirp_pulse <= 1'b1;
|
||||
state <= S_CHIRP;
|
||||
end
|
||||
end else begin
|
||||
chirp_pulse <= 1'b1;
|
||||
state <= S_CHIRP;
|
||||
end
|
||||
end
|
||||
end
|
||||
S_BEAM_WAIT: begin
|
||||
// Wait for an MCU PD8 toggle (any edge) OR the watchdog.
|
||||
// host_handshake_enable can drop mid-wait — release the FSM in
|
||||
// that case so disabling the handshake never strands the
|
||||
// radar between frames.
|
||||
if (beam_ready_edge || !host_handshake_enable) begin
|
||||
beam_watchdog <= 23'd0;
|
||||
chirp_pulse <= 1'b1;
|
||||
state <= S_CHIRP;
|
||||
end else if (beam_watchdog >= BEAM_WATCHDOG_MAX) begin
|
||||
beam_handshake_watchdog_fired <= 1'b1;
|
||||
beam_watchdog <= 23'd0;
|
||||
chirp_pulse <= 1'b1;
|
||||
state <= S_CHIRP;
|
||||
end else begin
|
||||
beam_watchdog <= beam_watchdog + 23'd1;
|
||||
end
|
||||
end
|
||||
default: state <= S_IDLE;
|
||||
|
||||
Reference in New Issue
Block a user