diff --git a/9_Firmware/9_2_FPGA/fft_engine_axi_bridge.v b/9_Firmware/9_2_FPGA/fft_engine_axi_bridge.v index 5cc3a27..4fb34e6 100644 --- a/9_Firmware/9_2_FPGA/fft_engine_axi_bridge.v +++ b/9_Firmware/9_2_FPGA/fft_engine_axi_bridge.v @@ -20,10 +20,13 @@ // LogiCORE Pipelined Streaming ~N + ~150-cycle pipeline. Functional behavior // is identical from the chain's view. // -// AUDIT-C10/C-8: cfg_tdata carries SCALE_SCH+FWD/INV in scaled mode (24 bits). -// Schedule = `RP_FFT_SCALE_SCH (radar_params.vh) = >>1 per stage = total /N. -// Both the LogiCORE path and the iverilog fft_engine fallback honor the same -// schedule, so absolute output magnitudes match between sim and silicon. +// AUDIT-C10/C-8: cfg_tdata carries SCALE_SCH+FWD/INV in scaled mode. Layout +// is 16 bits per PG109 Pipelined Streaming I/O (12-bit SCALE_SCH + 1-bit +// FWD/INV + 3-bit padding); see radar_params.vh `RP_FFT_CFG_TDATA_W. +// Schedule = `RP_FFT_SCALE_SCH = total /N (LogiCORE pair-grouped 2'b10s on +// stages 2-11 + 2'b01 on stage 1; iverilog fft_engine applies >>>1 per stage +// for the same total). Absolute output magnitudes match between sim and +// silicon. // // PR-O.7 (2026-05-02): bridge widened to DATA_W=32 default and AXIS-data // 64-bit packed {Q[31:0], I[31:0]}. The matched-filter chain feeds the @@ -63,7 +66,7 @@ module fft_engine_axi_bridge #( // ============================================================================ localparam AXIS_W = 2 * DATA_W; // 64 when DATA_W=32 -reg [`RP_FFT_CFG_TDATA_W-1:0] cfg_tdata; // 24 bits: {pad, SCALE_SCH, FWD/INV} +reg [`RP_FFT_CFG_TDATA_W-1:0] cfg_tdata; // 16 bits: {3'b pad, 12'b SCALE_SCH, 1'b FWD/INV} reg cfg_tvalid; wire cfg_tready; @@ -155,8 +158,9 @@ always @(posedge clk or negedge reset_n) begin skid_valid <= 1'b0; if (start) begin inverse_latched <= inverse; - // {pad[0], SCALE_SCH[21:0], FWD/INV[0]}; ~inverse so FWD=1. - cfg_tdata <= {1'b0, `RP_FFT_SCALE_SCH, ~inverse}; + // {pad[2:0], SCALE_SCH[11:0], FWD/INV[0]}; ~inverse so FWD=1. + // PG109 Pipelined Streaming I/O cfg_tdata = 16 bits total. + cfg_tdata <= {3'b0, `RP_FFT_SCALE_SCH, ~inverse}; cfg_tvalid <= 1'b1; in_count <= 0; accept_count <= 0; diff --git a/9_Firmware/9_2_FPGA/ip/xfft_2048_ip/xfft_2048_ip.xci b/9_Firmware/9_2_FPGA/ip/xfft_2048_ip/xfft_2048_ip.xci index 2b08b04..55fd8c3 100644 --- a/9_Firmware/9_2_FPGA/ip/xfft_2048_ip/xfft_2048_ip.xci +++ b/9_Firmware/9_2_FPGA/ip/xfft_2048_ip/xfft_2048_ip.xci @@ -40,11 +40,11 @@ "model_parameters": { "C_XDEVICEFAMILY": [ { "value": "artix7", "resolve_type": "generated", "usage": "all" } ], "C_PART": [ { "value": "xc7a50tftg256-2", "resolve_type": "generated", "usage": "all" } ], - "C_S_AXIS_CONFIG_TDATA_WIDTH": [ { "value": "24", "resolve_type": "generated", "format": "long", "usage": "all" } ], + "C_S_AXIS_CONFIG_TDATA_WIDTH": [ { "value": "16", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_S_AXIS_DATA_TDATA_WIDTH": [ { "value": "64", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_M_AXIS_DATA_TDATA_WIDTH": [ { "value": "64", "resolve_type": "generated", "format": "long", "usage": "all" } ], - "C_M_AXIS_DATA_TUSER_WIDTH": [ { "value": "8", "resolve_type": "generated", "format": "long", "usage": "all" } ], - "C_M_AXIS_STATUS_TDATA_WIDTH": [ { "value": "8", "resolve_type": "generated", "format": "long", "usage": "all" } ], + "C_M_AXIS_DATA_TUSER_WIDTH": [ { "value": "1", "resolve_type": "generated", "format": "long", "usage": "all" } ], + "C_M_AXIS_STATUS_TDATA_WIDTH": [ { "value": "1", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_THROTTLE_SCHEME": [ { "value": "1", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_NSSR": [ { "value": "1", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_CHANNELS": [ { "value": "1", "resolve_type": "generated", "format": "long", "usage": "all" } ], @@ -103,7 +103,7 @@ "boundary": { "ports": { "aclk": [ { "direction": "in", "driver_value": "0x1" } ], - "s_axis_config_tdata": [ { "direction": "in", "size_left": "23", "size_right": "0" } ], + "s_axis_config_tdata": [ { "direction": "in", "size_left": "15", "size_right": "0" } ], "s_axis_config_tvalid": [ { "direction": "in" } ], "s_axis_config_tready": [ { "direction": "out" } ], "s_axis_data_tdata": [ { "direction": "in", "size_left": "63", "size_right": "0" } ], @@ -111,13 +111,9 @@ "s_axis_data_tready": [ { "direction": "out" } ], "s_axis_data_tlast": [ { "direction": "in" } ], "m_axis_data_tdata": [ { "direction": "out", "size_left": "63", "size_right": "0" } ], - "m_axis_data_tuser": [ { "direction": "out", "size_left": "7", "size_right": "0" } ], "m_axis_data_tvalid": [ { "direction": "out" } ], "m_axis_data_tready": [ { "direction": "in", "driver_value": "0x1" } ], "m_axis_data_tlast": [ { "direction": "out" } ], - "m_axis_status_tdata": [ { "direction": "out", "size_left": "7", "size_right": "0" } ], - "m_axis_status_tvalid": [ { "direction": "out" } ], - "m_axis_status_tready": [ { "direction": "in", "driver_value": "0x1" } ], "event_frame_started": [ { "direction": "out", "driver_value": "0x0" } ], "event_tlast_unexpected": [ { "direction": "out", "driver_value": "0x0" } ], "event_tlast_missing": [ { "direction": "out", "driver_value": "0x0" } ], @@ -269,31 +265,6 @@ "POLARITY": [ { "value": "ACTIVE_HIGH", "value_src": "constant", "usage": "all" } ] } }, - "M_AXIS_STATUS": { - "vlnv": "xilinx.com:interface:axis:1.0", - "abstraction_type": "xilinx.com:interface:axis_rtl:1.0", - "mode": "master", - "parameters": { - "TDATA_NUM_BYTES": [ { "value": "1", "resolve_type": "generated", "format": "long", "is_ips_inferred": true, "is_static_object": false } ], - "TDEST_WIDTH": [ { "value": "0", "value_src": "constant", "resolve_type": "generated", "format": "long", "is_ips_inferred": true, "is_static_object": false } ], - "TID_WIDTH": [ { "value": "0", "value_src": "constant", "resolve_type": "generated", "format": "long", "is_ips_inferred": true, "is_static_object": false } ], - "TUSER_WIDTH": [ { "value": "0", "value_src": "constant", "resolve_type": "generated", "format": "long", "is_ips_inferred": true, "is_static_object": false } ], - "HAS_TREADY": [ { "value": "1", "resolve_type": "generated", "format": "long", "is_ips_inferred": true, "is_static_object": false } ], - "HAS_TSTRB": [ { "value": "0", "value_src": "constant", "resolve_type": "generated", "format": "long", "is_ips_inferred": true, "is_static_object": false } ], - "HAS_TKEEP": [ { "value": "0", "value_src": "constant", "resolve_type": "generated", "format": "long", "is_ips_inferred": true, "is_static_object": false } ], - "HAS_TLAST": [ { "value": "0", "value_src": "constant", "resolve_type": "generated", "format": "long", "is_ips_inferred": true, "is_static_object": false } ], - "FREQ_HZ": [ { "value": "100000000", "resolve_type": "generated", "format": "long", "is_ips_inferred": true, "is_static_object": false } ], - "PHASE": [ { "value": "0.0", "resolve_type": "generated", "format": "float", "is_ips_inferred": true, "is_static_object": false } ], - "CLK_DOMAIN": [ { "value": "", "resolve_type": "generated", "is_ips_inferred": true, "is_static_object": false } ], - "LAYERED_METADATA": [ { "value": "undef", "resolve_type": "generated", "is_ips_inferred": true, "is_static_object": false } ], - "INSERT_VIP": [ { "value": "0", "resolve_type": "user", "format": "long", "usage": "simulation.rtl", "is_ips_inferred": true, "is_static_object": false } ] - }, - "port_maps": { - "TDATA": [ { "physical_name": "m_axis_status_tdata" } ], - "TREADY": [ { "physical_name": "m_axis_status_tready" } ], - "TVALID": [ { "physical_name": "m_axis_status_tvalid" } ] - } - }, "M_AXIS_DATA": { "vlnv": "xilinx.com:interface:axis:1.0", "abstraction_type": "xilinx.com:interface:axis_rtl:1.0", @@ -302,7 +273,7 @@ "TDATA_NUM_BYTES": [ { "value": "8", "value_src": "auto", "resolve_type": "generated", "format": "long", "is_ips_inferred": true, "is_static_object": false } ], "TDEST_WIDTH": [ { "value": "0", "value_src": "constant", "resolve_type": "generated", "format": "long", "is_ips_inferred": true, "is_static_object": false } ], "TID_WIDTH": [ { "value": "0", "value_src": "constant", "resolve_type": "generated", "format": "long", "is_ips_inferred": true, "is_static_object": false } ], - "TUSER_WIDTH": [ { "value": "8", "value_src": "auto", "resolve_type": "generated", "format": "long", "is_ips_inferred": true, "is_static_object": false } ], + "TUSER_WIDTH": [ { "value": "0", "resolve_type": "generated", "format": "long", "is_ips_inferred": true, "is_static_object": false } ], "HAS_TREADY": [ { "value": "1", "resolve_type": "generated", "format": "long", "is_ips_inferred": true, "is_static_object": false } ], "HAS_TSTRB": [ { "value": "0", "value_src": "constant", "resolve_type": "generated", "format": "long", "is_ips_inferred": true, "is_static_object": false } ], "HAS_TKEEP": [ { "value": "0", "value_src": "constant", "resolve_type": "generated", "format": "long", "is_ips_inferred": true, "is_static_object": false } ], @@ -317,7 +288,6 @@ "TDATA": [ { "physical_name": "m_axis_data_tdata" } ], "TLAST": [ { "physical_name": "m_axis_data_tlast" } ], "TREADY": [ { "physical_name": "m_axis_data_tready" } ], - "TUSER": [ { "physical_name": "m_axis_data_tuser" } ], "TVALID": [ { "physical_name": "m_axis_data_tvalid" } ] } }, @@ -326,7 +296,7 @@ "abstraction_type": "xilinx.com:interface:axis_rtl:1.0", "mode": "slave", "parameters": { - "TDATA_NUM_BYTES": [ { "value": "3", "resolve_type": "generated", "format": "long", "is_ips_inferred": true, "is_static_object": false } ], + "TDATA_NUM_BYTES": [ { "value": "2", "value_src": "auto", "resolve_type": "generated", "format": "long", "is_ips_inferred": true, "is_static_object": false } ], "TDEST_WIDTH": [ { "value": "0", "value_src": "constant", "resolve_type": "generated", "format": "long", "is_ips_inferred": true, "is_static_object": false } ], "TID_WIDTH": [ { "value": "0", "value_src": "constant", "resolve_type": "generated", "format": "long", "is_ips_inferred": true, "is_static_object": false } ], "TUSER_WIDTH": [ { "value": "0", "value_src": "constant", "resolve_type": "generated", "format": "long", "is_ips_inferred": true, "is_static_object": false } ], @@ -349,5 +319,5 @@ } } }, - "checksum": "6bf94ec5" + "checksum": "b2c20790" } \ No newline at end of file diff --git a/9_Firmware/9_2_FPGA/radar_params.vh b/9_Firmware/9_2_FPGA/radar_params.vh index f832aa5..21832a7 100644 --- a/9_Firmware/9_2_FPGA/radar_params.vh +++ b/9_Firmware/9_2_FPGA/radar_params.vh @@ -95,18 +95,33 @@ // chain output (FFT·conj(FFT)·IFFT) is /N², predictable and per-frame // constant, so CFAR alpha calibrated in iverilog matches silicon counts. // -// cfg_tdata layout per PG109 (1 channel, no CP, fixed NFFT, scaled): -// bit 0 = FWD/INV (1 = forward, 0 = inverse) -// bits[22:1] = SCALE_SCH (22 bits) -// bit 23 = byte-align padding (0) -// Total cfg_tdata width = 24 bits. +// cfg_tdata layout per PG109 (1 channel, no CP, fixed NFFT, scaled, +// Pipelined Streaming I/O architecture). The IP groups radix-2 stages +// into radix-4-style pairs for scheduling — each 2-bit field covers a +// pair of stages, so SCALE_SCH width = 2 * ceil(NFFT_MAX/2) = 12 bits +// for NFFT_MAX=11. (PR-O.2 originally used the 22-bit Burst-I/O +// layout — wrong for our Pipelined Streaming arch; corrected in +// PR-O.8 commit after Vivado IP regen reported cfg_tdata=16.) // -// The same schedule is replicated in fft_engine.v (iverilog fallback) by -// applying convergent-rounding >>>1 at every BF_WRITE stage so absolute -// counts agree between sim and silicon. -`define RP_FFT_CFG_TDATA_W 24 -`define RP_FFT_SCALE_SCH_W 22 -`define RP_FFT_SCALE_SCH 22'h155555 // [01,01,01,01,01,01,01,01,01,01,01] +// bit 0 = FWD/INV (1 = forward, 0 = inverse) +// bits[12:1] = SCALE_SCH (12 bits, LSB = stage 1 alone, then 5 pairs) +// bits[15:13] = byte-align padding (0) +// Total cfg_tdata width = 16 bits. +// +// SCALE_SCH = 12'hAA9 = 12'b10_10_10_10_10_01: +// stage 1 alone bits[1:0] = 2'b01 → >>1 +// stages 2..3 bits[3:2] = 2'b10 → >>2 (/4 across pair) +// stages 4..5 bits[5:4] = 2'b10 +// stages 6..7 bits[7:6] = 2'b10 +// stages 8..9 bits[9:8] = 2'b10 +// stages 10..11 bits[11:10] = 2'b10 +// Total shift = 1 + 5*2 = 11 = /N. The iverilog fft_engine.v fallback +// applies >>>1 at every BF_WRITE (= /N total too) so absolute output +// magnitudes match between sim and silicon for any /N-equivalent +// schedule. +`define RP_FFT_CFG_TDATA_W 16 +`define RP_FFT_SCALE_SCH_W 12 +`define RP_FFT_SCALE_SCH 12'hAA9 // 3-ladder waveform identity (replaces 1-bit use_long_chirp rail in PR-C onward) // `define RP_WAVE_ values are 2-bit waveform selectors carried on diff --git a/9_Firmware/9_2_FPGA/tb/tb_fft_engine_axi_bridge.v b/9_Firmware/9_2_FPGA/tb/tb_fft_engine_axi_bridge.v index 11fbd8b..e386087 100644 --- a/9_Firmware/9_2_FPGA/tb/tb_fft_engine_axi_bridge.v +++ b/9_Firmware/9_2_FPGA/tb/tb_fft_engine_axi_bridge.v @@ -319,14 +319,15 @@ endmodule // ============================================================================ // Stub xfft_2048 — replaces the production wrapper for this TB. -// AUDIT-C10/C-8: cfg_tdata is 24-bit in scaled mode; tuser dropped with BFP. +// AUDIT-C10/C-8 + PR-O.8: cfg_tdata is 16-bit in scaled mode (PG109 Pipelined +// Streaming I/O); tuser dropped with BFP. // PR-O.7: AXIS data widened to 64-bit packed {Q[31:0], I[31:0]} so the IFFT // can carry the conjugate-mult Q30 product end-to-end. // ============================================================================ module xfft_2048 ( input wire aclk, input wire aresetn, - input wire [23:0] s_axis_config_tdata, + input wire [15:0] s_axis_config_tdata, input wire s_axis_config_tvalid, output wire s_axis_config_tready, input wire [63:0] s_axis_data_tdata, diff --git a/9_Firmware/9_2_FPGA/tb/tb_xfft_2048_xsim.v b/9_Firmware/9_2_FPGA/tb/tb_xfft_2048_xsim.v index d1830de..dddf673 100644 --- a/9_Firmware/9_2_FPGA/tb/tb_xfft_2048_xsim.v +++ b/9_Firmware/9_2_FPGA/tb/tb_xfft_2048_xsim.v @@ -32,10 +32,12 @@ module tb_xfft_2048_xsim; reg aclk = 0; reg aresetn = 0; - // AUDIT-C10/C-8: cfg_tdata widened to 24 bits (scaled mode SCALE_SCH+FWD/INV). - // PR-O.7: data AXIS widened to 64-bit packed {Q[31:0], I[31:0]} — - // matches the regenerated xfft_2048_ip with input_width=32. - reg [23:0] cfg_tdata; + // AUDIT-C10/C-8 + PR-O.8: cfg_tdata is 16 bits (scaled mode + Pipelined + // Streaming I/O — SCALE_SCH width = 2*ceil(NFFT_MAX/2) = 12 bits + 1 bit + // FWD/INV + 3 bits padding). PR-O.7: data AXIS widened to 64-bit + // packed {Q[31:0], I[31:0]} — matches the Vivado-regenerated + // xfft_2048_ip with input_width=32. + reg [15:0] cfg_tdata; reg cfg_tvalid; wire cfg_tready; @@ -101,8 +103,8 @@ module tb_xfft_2048_xsim; input fwd; begin @(posedge aclk); - // {pad[0], SCALE_SCH[21:0], FWD/INV[0]} — see radar_params.vh - cfg_tdata <= {1'b0, `RP_FFT_SCALE_SCH, fwd}; + // {pad[2:0], SCALE_SCH[11:0], FWD/INV[0]} — see radar_params.vh + cfg_tdata <= {3'b0, `RP_FFT_SCALE_SCH, fwd}; cfg_tvalid <= 1'b1; @(posedge aclk); while (!cfg_tready) @(posedge aclk); diff --git a/9_Firmware/9_2_FPGA/xfft_2048.v b/9_Firmware/9_2_FPGA/xfft_2048.v index a47c432..65c9e37 100644 --- a/9_Firmware/9_2_FPGA/xfft_2048.v +++ b/9_Firmware/9_2_FPGA/xfft_2048.v @@ -25,11 +25,11 @@ // >>15+saturate that crushed chirp/DC/impulse autocorrelations to zero under // deterministic /N scaling — see project_mf_chain_dynrange_defect_2026-05-02. // -// Config tdata layout (24-bit, scaled mode — see AUDIT-C10/C-8 in +// Config tdata layout (16-bit, scaled mode — see AUDIT-C10/C-8 in // radar_params.vh `RP_FFT_SCALE_SCH): // bit 0 = FWD/INV (1 = forward, 0 = inverse) -// bits[22:1] = SCALE_SCH (22 bits, fixed schedule from RP_FFT_SCALE_SCH) -// bit 23 = byte-align padding +// bits[12:1] = SCALE_SCH (12 bits, fixed schedule from RP_FFT_SCALE_SCH) +// bits[15:13]= byte-align padding // // Scaled mode replaces the previous Block-Floating-Point setting. BFP returned // a per-frame BLK_EXP on m_axis_data_tuser that the bridge dropped — sim and @@ -42,9 +42,10 @@ module xfft_2048 ( input wire aclk, input wire aresetn, - // Configuration channel (AXI-Stream slave). 24-bit tdata carries - // {pad, SCALE_SCH[21:0], FWD/INV}. - input wire [23:0] s_axis_config_tdata, + // Configuration channel (AXI-Stream slave). 16-bit tdata carries + // {pad[2:0], SCALE_SCH[11:0], FWD/INV} per PG109 Pipelined Streaming I/O + // (PR-O.8: SCALE_SCH width is 2*ceil(NFFT_MAX/2)=12, not 2*NFFT_MAX). + input wire [15:0] s_axis_config_tdata, input wire s_axis_config_tvalid, output wire s_axis_config_tready,