mirror of
https://github.com/NawfalMotii79/PLFM_RADAR.git
synced 2026-06-10 15:31:21 +00:00
feat(rtl,gui): PR-U / M-8 — sub-frame enable mask routed end-to-end (C-5 hardening)
The chirp_scheduler had a 3-bit host_subframe_enable input {LONG, MEDIUM, SHORT}
that was tied to the constant RP_DEF_SUBFRAME_ENABLE at the receiver instance,
so the host could neither change it nor know what mask was active. With the
mask not at 3'b111 the scheduler skips a sub-frame at TX but doppler_processor
still writes 48 chirp slots, so the host CRT (`dbin // 16 → {SHORT, MED, LONG}`)
silently mis-attributes the SF axis and unfolds to the wrong velocity.
Plumb the mask through:
- radar_system_top.v: new reg [2:0] host_subframe_enable, cold-reset
RP_DEF_SUBFRAME_ENABLE, opcode 0x19 setter, wired to rx_inst and usb_inst.
- radar_receiver_final.v: new host_subframe_enable[2:0] input port; the
chirp_scheduler instance is untied from the constant.
- usb_data_interface_ft2232h.v: new subframe_enable[2:0] input + per-frame
snapshot reg latched at frame_complete (stable for ft_clk read, same
pattern as stream_flags_snapshot). Byte 2 emission is now
{2'b00, subframe_enable[2:0], stream_flags[2:0]} — was {5'b00000, stream}.
- radar_protocol.py: Opcode.SUBFRAME_ENABLE = 0x19; RadarFrame.subframe_enable
field; parse_bulk_frame surfaces bits[5:3]; reserved-mask 0xF8 → 0xC0.
Bulk-frame mock encodes the mask in its emit so dashboard replay is correct.
- v7/processing.py: extract_targets_from_frame_crt forces every target to
AMBIGUOUS when frame.subframe_enable != 0b111. Operator sees the red `?`
flag in the targets table instead of a silently-wrong velocity.
- v7/software_fpga.py + v7/dashboard.py: subframe_enable mirror + setter, and
replay dispatch routes 0x19 to set_subframe_enable.
Tests (test_v7.py): TestSubframeEnableRoundTrip (4), TestSoftwareFpgaSubframeEnable
(2), TestCrtSubframeMaskGating (3), 0x19 added to TestOpcodeEnumFillIn and
TestReplayOpcodeDispatch. Existing test_full_frame_round_trip updated to expect
byte 2 = 0x3F (mask 0b111 default + stream 0x07).
Cosim TBs (tb/tb_usb_protocol_v2.v, tb/tb_ft2232h_frame_drop.v) drive the new
input with 3'b111 and assert the new byte-2 layout (T2.3: 0x00 → 0x38).
Regression: test_v7 146/146, test_GUI_V65_Tk 117/117, ruff clean.
iverilog: tb_usb_protocol_v2 27/27 PASS, tb_ft2232h_frame_drop 10/10 PASS.
This commit is contained in:
@@ -51,6 +51,10 @@ module radar_receiver_final (
|
||||
input wire [15:0] host_medium_chirp_cycles,
|
||||
input wire [15:0] host_medium_listen_cycles,
|
||||
input wire [5:0] host_chirps_per_elev,
|
||||
// PR-U / M-8: sub-frame enable mask {LONG, MEDIUM, SHORT}. Was tied to
|
||||
// RP_DEF_SUBFRAME_ENABLE here at the chirp_scheduler instance; routed
|
||||
// through radar_system_top opcode 0x19 so the host owns the mask.
|
||||
input wire [2:0] host_subframe_enable,
|
||||
|
||||
// Digital gain control (Fix 3: between DDC output and matched filter)
|
||||
// [3]=direction: 0=amplify(left shift), 1=attenuate(right shift)
|
||||
@@ -236,7 +240,9 @@ chirp_scheduler sched (
|
||||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
.host_mode(host_mode),
|
||||
.host_subframe_enable(`RP_DEF_SUBFRAME_ENABLE),
|
||||
// PR-U / M-8: routed from radar_system_top opcode 0x19 (was the
|
||||
// RP_DEF_SUBFRAME_ENABLE constant — host had no way to mask sub-frames).
|
||||
.host_subframe_enable(host_subframe_enable),
|
||||
.host_short_chirp_cycles (host_short_chirp_cycles),
|
||||
.host_short_listen_cycles(host_short_listen_cycles),
|
||||
// PR-G G2: MEDIUM now flows from radar_system_top opcodes 0x17/0x18.
|
||||
|
||||
@@ -272,6 +272,11 @@ reg [15:0] host_short_listen_cycles; // Opcode 0x14 (default 17400, V2)
|
||||
reg [15:0] host_medium_chirp_cycles; // Opcode 0x17 (default 500, PR-G G2)
|
||||
reg [15:0] host_medium_listen_cycles; // Opcode 0x18 (default 15600, PR-Q staggered PRI)
|
||||
reg [5:0] host_chirps_per_elev; // Opcode 0x15 (default 48 = RP_CHIRPS_PER_FRAME, PR-F)
|
||||
// PR-U / M-8: per-sub-frame enable mask routed end-to-end so the host knows
|
||||
// which sub-frames the chirp_scheduler emitted for a given frame. Bit 0 SHORT,
|
||||
// bit 1 MEDIUM, bit 2 LONG. Default 3'b111 keeps the production 3-PRI ladder.
|
||||
// Mirrored into v2 frame byte 2 bits[5:3] (usb_data_interface_ft2232h.v).
|
||||
reg [2:0] host_subframe_enable; // Opcode 0x19 (default RP_DEF_SUBFRAME_ENABLE = 3'b111)
|
||||
reg host_status_request; // Opcode 0xFF (self-clearing pulse)
|
||||
|
||||
// Fix 4: Doppler/chirps mismatch protection
|
||||
@@ -604,6 +609,9 @@ radar_receiver_final rx_inst (
|
||||
.host_medium_chirp_cycles(host_medium_chirp_cycles),
|
||||
.host_medium_listen_cycles(host_medium_listen_cycles),
|
||||
.host_chirps_per_elev(host_chirps_per_elev),
|
||||
// PR-U / M-8: sub-frame enable mask, was tied to RP_DEF_SUBFRAME_ENABLE
|
||||
// inside radar_receiver_final at the chirp_scheduler instance.
|
||||
.host_subframe_enable(host_subframe_enable),
|
||||
// Fix 3: digital gain control
|
||||
.host_gain_shift(host_gain_shift),
|
||||
// AGC configuration (opcodes 0x28-0x2C)
|
||||
@@ -933,6 +941,11 @@ end else begin : gen_ft2232h
|
||||
// Stream control
|
||||
.stream_control(host_stream_control),
|
||||
|
||||
// PR-U / M-8: per-frame snapshot of host_subframe_enable echoed in
|
||||
// v2 frame byte 2 bits[5:3]. Lets the host detect when an operator
|
||||
// disabled a sub-frame and downgrade CRT confidence accordingly.
|
||||
.subframe_enable(host_subframe_enable),
|
||||
|
||||
// Status readback inputs
|
||||
.status_request(host_status_request),
|
||||
.status_cfar_threshold(host_detect_threshold),
|
||||
@@ -1060,6 +1073,8 @@ always @(posedge clk_100m_buf or negedge sys_reset_n) begin
|
||||
// chirps_per_elev register is echoed in status word 3 and used by host
|
||||
// sanity-checking. Keep cold-reset value in lockstep with the truth.
|
||||
host_chirps_per_elev <= 6'd48;
|
||||
// PR-U / M-8: 3'b111 = SHORT|MEDIUM|LONG all on (production 3-PRI ladder).
|
||||
host_subframe_enable <= `RP_DEF_SUBFRAME_ENABLE;
|
||||
host_status_request <= 1'b0;
|
||||
chirps_mismatch_error <= 1'b0;
|
||||
host_range_mode <= 2'b00; // Default: 3 km mode (all short chirps)
|
||||
@@ -1111,6 +1126,11 @@ always @(posedge clk_100m_buf or negedge sys_reset_n) begin
|
||||
// PR-G G2: MEDIUM ladder timings
|
||||
8'h17: host_medium_chirp_cycles <= usb_cmd_value;
|
||||
8'h18: host_medium_listen_cycles <= usb_cmd_value;
|
||||
// PR-U / M-8: sub-frame enable mask {LONG, MEDIUM, SHORT} =
|
||||
// {value[2], value[1], value[0]}. Surfaced in v2 frame byte 2
|
||||
// bits[5:3] so host CRT can detect mask != 3'b111 and degrade
|
||||
// confidence rather than mis-attribute the SF axis.
|
||||
8'h19: host_subframe_enable <= usb_cmd_value[2:0];
|
||||
8'h15: begin
|
||||
// Fix 4: Clamp chirps_per_elev to the fixed Doppler frame size.
|
||||
// If host requests a different value, clamp and set error flag.
|
||||
|
||||
@@ -68,6 +68,8 @@ module tb_ft2232h_frame_drop;
|
||||
// PR-G: stream bits [2:0] all off → WR FSM: HDR → FOOTER → DONE
|
||||
// = fast deterministic drain. Bits [5:3] are reserved=0 in v2.
|
||||
reg [5:0] stream_control = 6'b000_000;
|
||||
// PR-U / M-8: production 3-PRI ladder.
|
||||
reg [2:0] subframe_enable = 3'b111;
|
||||
|
||||
// Status inputs (irrelevant for this test)
|
||||
reg status_request = 1'b0;
|
||||
@@ -124,6 +126,8 @@ module tb_ft2232h_frame_drop;
|
||||
.cmd_addr(cmd_addr),
|
||||
.cmd_value(cmd_value),
|
||||
.stream_control(stream_control),
|
||||
// PR-U / M-8: per-frame snapshot of host_subframe_enable.
|
||||
.subframe_enable(subframe_enable),
|
||||
.status_request(status_request),
|
||||
.status_cfar_threshold(status_cfar_threshold),
|
||||
.status_stream_ctrl(status_stream_ctrl),
|
||||
|
||||
@@ -66,6 +66,8 @@ module tb_usb_protocol_v2;
|
||||
// PR-G v2: enable all 3 streams (range|doppler|cfar). Bits [5:3] reserved=0.
|
||||
reg [5:0] stream_control = 6'b000_111;
|
||||
reg [5:0] status_stream_ctrl = 6'b000_111;
|
||||
// PR-U / M-8: production 3-PRI ladder (SHORT|MEDIUM|LONG).
|
||||
reg [2:0] subframe_enable = 3'b111;
|
||||
|
||||
// Status inputs (mostly tied off; PR-G additions below)
|
||||
reg status_request = 1'b0;
|
||||
@@ -127,6 +129,9 @@ module tb_usb_protocol_v2;
|
||||
.cmd_addr(cmd_addr),
|
||||
.cmd_value(cmd_value),
|
||||
.stream_control(stream_control),
|
||||
// PR-U / M-8: per-frame snapshot of host_subframe_enable echoed in
|
||||
// v2 frame byte 2 bits[5:3].
|
||||
.subframe_enable(subframe_enable),
|
||||
.status_request(status_request),
|
||||
.status_cfar_threshold(status_cfar_threshold),
|
||||
.status_stream_ctrl(status_stream_ctrl),
|
||||
@@ -251,7 +256,10 @@ module tb_usb_protocol_v2;
|
||||
wait_clk(150);
|
||||
check_b("T2.1: byte0 = 0xAA", egress_bytes[0] == 8'hAA);
|
||||
check_b("T2.2: byte1 = 0x02 (ver)", egress_bytes[1] == `RP_USB_PROTOCOL_VERSION);
|
||||
check_b("T2.3: byte2 = stream flags=0", egress_bytes[2] == 8'h00);
|
||||
// PR-U / M-8: byte 2 = {2'b00, subframe_enable[2:0], stream[2:0]}.
|
||||
// subframe_enable defaults to 3'b111 → byte 2 = (0b111 << 3) | 0 = 0x38.
|
||||
check_b("T2.3: byte2 = {00, sf=111, stream=0} = 0x38",
|
||||
egress_bytes[2] == 8'h38);
|
||||
// Byte 3-4 = frame_number snapshot. snapshot latches OLD frame_number
|
||||
// at frame_complete (NBA), so first frame emitted carries fn=0.
|
||||
check_b("T2.4: byte3 = fn[15:8]=0", egress_bytes[3] == 8'h00);
|
||||
|
||||
@@ -13,7 +13,11 @@
|
||||
* Frame packet (FPGA→Host): variable length, up to 74,762 bytes
|
||||
* Byte 0: 0xAA (frame start header)
|
||||
* Byte 1: 0x02 (PROTOCOL VERSION — host MUST reject any other value)
|
||||
* Byte 2: Stream flags {5'b0, stream_cfar, stream_doppler, stream_range}
|
||||
* Byte 2: Flags byte. Layout (PR-U / M-8 widened bits[5:3]):
|
||||
* bits[7:6] = 2'b00 reserved
|
||||
* bits[5:3] = subframe_enable[2:0] = {LONG, MEDIUM, SHORT}
|
||||
* (host_subframe_enable snapshot at frame_complete)
|
||||
* bits[2:0] = {stream_cfar, stream_doppler, stream_range}
|
||||
* Bytes 3-4: Frame number (uint16, MSB first)
|
||||
* Bytes 5-6: Range bin count (uint16, MSB first) = `RP_NUM_RANGE_BINS` (512)
|
||||
* Bytes 7-8: Doppler bin count (uint16, MSB first) = `RP_NUM_DOPPLER_BINS` (48)
|
||||
@@ -127,6 +131,13 @@ module usb_data_interface_ft2232h (
|
||||
// Stream control input (clk domain, CDC'd internally)
|
||||
input wire [5:0] stream_control,
|
||||
|
||||
// PR-U / M-8: per-frame sub-frame enable mask (clk domain, CDC'd
|
||||
// internally, snapshotted at frame_complete). {LONG, MEDIUM, SHORT}.
|
||||
// Echoed in v2 frame byte 2 bits[5:3] so the host CRT can detect
|
||||
// when an operator disables a sub-frame and downgrade confidence
|
||||
// (default 3'b111 keeps the production 3-PRI ladder behavior).
|
||||
input wire [2:0] subframe_enable,
|
||||
|
||||
// Status readback inputs (clk domain, CDC'd internally)
|
||||
input wire status_request,
|
||||
input wire [15:0] status_cfar_threshold,
|
||||
@@ -643,15 +654,21 @@ wire stream_cfar_en = stream_ctrl_sync_1[2];
|
||||
|
||||
// --- Frame metadata snapshot (latched in clk domain, stable for ft_clk read) ---
|
||||
reg [15:0] frame_number_snapshot;
|
||||
reg [2:0] stream_flags_snapshot; // PR-G: 3 bits used (range/doppler/cfar)
|
||||
reg [2:0] stream_flags_snapshot; // PR-G: 3 bits used (range/doppler/cfar)
|
||||
// PR-U / M-8: snapshot of host_subframe_enable taken at frame_complete so the
|
||||
// host parser sees the mask that was active for THIS frame (atomic per-frame).
|
||||
// Stable when ft_clk reads it via the frame_ready toggle synchronizer.
|
||||
reg [2:0] subframe_enable_snapshot; // {LONG, MEDIUM, SHORT}
|
||||
|
||||
always @(posedge clk or negedge reset_n) begin
|
||||
if (!reset_n) begin
|
||||
frame_number_snapshot <= 16'd0;
|
||||
stream_flags_snapshot <= 3'b111; // PR-G: all 3 streams on (range|doppler|cfar)
|
||||
frame_number_snapshot <= 16'd0;
|
||||
stream_flags_snapshot <= 3'b111; // PR-G: all 3 streams on (range|doppler|cfar)
|
||||
subframe_enable_snapshot <= 3'b111; // PR-U: all 3 sub-frames on (production default)
|
||||
end else if (frame_complete) begin
|
||||
frame_number_snapshot <= frame_number;
|
||||
stream_flags_snapshot <= stream_control[2:0]; // PR-G: ignore reserved [5:3]
|
||||
frame_number_snapshot <= frame_number;
|
||||
stream_flags_snapshot <= stream_control[2:0]; // PR-G: ignore reserved [5:3]
|
||||
subframe_enable_snapshot <= subframe_enable;
|
||||
end
|
||||
end
|
||||
|
||||
@@ -968,7 +985,10 @@ always @(posedge ft_clk or negedge ft_effective_reset_n) begin
|
||||
case (wr_byte_idx[3:0])
|
||||
4'd0: ft_data_out <= HEADER;
|
||||
4'd1: ft_data_out <= `RP_USB_PROTOCOL_VERSION; // 0x02
|
||||
4'd2: ft_data_out <= {5'b00000, stream_flags_snapshot};
|
||||
// PR-U / M-8: byte 2 = {2'b00, subframe_enable[2:0], stream_flags[2:0]}.
|
||||
// Was {5'b00000, stream_flags_snapshot}; bits[5:3] now carry
|
||||
// the per-frame sub-frame mask snapshot {LONG, MEDIUM, SHORT}.
|
||||
4'd2: ft_data_out <= {2'b00, subframe_enable_snapshot, stream_flags_snapshot};
|
||||
4'd3: ft_data_out <= frame_number_snapshot[15:8];
|
||||
4'd4: ft_data_out <= frame_number_snapshot[7:0];
|
||||
4'd5: ft_data_out <= NUM_RANGE_BINS[15:8]; // 512 >> 8 = 2
|
||||
|
||||
Reference in New Issue
Block a user