Files
b210-k7-fpga/lib/rfnoc/blocks/rfnoc_block_fft/rfnoc_block_fft.sv
T
Wade Fife 81f150213f fpga: rfnoc: Add new FFT block
This replaces the legacy RFNoC FFT block with a new one of the same
name that includes a superset of the original features. New features
include cyclic prefix insertion or removal and FFT sizes up to 64k.


Original-commit: 25fffb006755b2e91f0138eb4af3ab9e16b8690c
2024-12-17 22:13:42 +01:00

352 lines
16 KiB
Systemverilog

//
// Copyright 2024 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: rfnoc_block_fft
//
// Description:
//
// RFNoC block for multichannel FFT/IFFT plus cyclic prefix insertion/removal.
//
// User Parameters:
//
// THIS_PORTID : Control crossbar port to which this block is connected
// CHDR_W : AXIS-CHDR data bus width
// MTU : Log2 of maximum transmission unit
// NUM_PORTS : Total number of FFT channels
// NUM_CORES : Number of individual cores to instantiate.
// Setting to 1 means all ports use a shared core
// and therefore all ports share the same control
// logic and all ports must be used simultaneously.
// Setting to NUM_PORTS means that each port will
// use its own core, and therefore each port can
// be configured and used independently. NUM_PORTS
// must be a multiple of NUM_CORES.
// MAX_FFT_SIZE_LOG2 : Log2 of maximum configurable FFT size. That is,
// the FFT size is exactly 2**fft_size_log2.
// MAX_CP_LIST_LEN_INS_LOG2 : Log2 of max length of cyclic prefix insertion
// list. Actual max is 2**MAX_CP_LIST_LEN_INS_LOG2.
// MAX_CP_LIST_LEN_REM_LOG2 : Log2 of max length of cyclic prefix removal
// list. Actual max is 2**MAX_CP_LIST_LEN_REM_LOG2.
// CP_INSERTION_REPEAT : Enable repeating the CP insertion list. When 1,
// the list repeats. When 0, CP insertion will
// stop when the list is finished.
// CP_REMOVAL_REPEAT : Enable repeating the CP removal list. When 1,
// the list repeats. When 0, CP removal will
// stop when the list is finished.
// EN_FFT_BYPASS : Controls whether to include the FFT bypass logic.
// EN_FFT_ORDER : Controls whether to include the FFT reorder logic.
// EN_MAGNITUDE : Controls whether to include the magnitude
// output calculation logic.
// EN_MAGNITUDE_SQ : Controls whether to include the
// magnitude-squared output calculation logic.
// USE_APPROX_MAG : Controls whether to use the low-resource
// approximate calculation (1) or the more exact
// and more resource-intensive calculation (0) for
// the magnitude calculation.
//
`default_nettype none
module rfnoc_block_fft #(
logic [9:0] THIS_PORTID = 10'd0,
int CHDR_W = 64,
logic [5:0] MTU = 6'd10,
int NUM_PORTS = 1,
int NUM_CORES = 1,
int MAX_FFT_SIZE_LOG2 = 12,
int MAX_CP_LIST_LEN_INS_LOG2 = 5,
int MAX_CP_LIST_LEN_REM_LOG2 = 5,
bit CP_INSERTION_REPEAT = 1,
bit CP_REMOVAL_REPEAT = 1,
bit EN_FFT_BYPASS = 1,
bit EN_FFT_ORDER = 1,
bit EN_MAGNITUDE = 1,
bit EN_MAGNITUDE_SQ = 1,
bit USE_APPROX_MAG = 1
) (
// RFNoC Framework Clocks and Resets
input wire rfnoc_chdr_clk,
input wire rfnoc_ctrl_clk,
input wire ce_clk,
// RFNoC Backend Interface
input wire [ 511:0] rfnoc_core_config,
output wire [ 511:0] rfnoc_core_status,
// AXIS-CHDR Input Ports (from framework)
input wire [CHDR_W*NUM_PORTS-1:0] s_rfnoc_chdr_tdata,
input wire [ NUM_PORTS-1:0] s_rfnoc_chdr_tlast,
input wire [ NUM_PORTS-1:0] s_rfnoc_chdr_tvalid,
output wire [ NUM_PORTS-1:0] s_rfnoc_chdr_tready,
// AXIS-CHDR Output Ports (to framework)
output wire [CHDR_W*NUM_PORTS-1:0] m_rfnoc_chdr_tdata,
output wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tlast,
output wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tvalid,
input wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tready,
// AXIS-Ctrl Input Port (from framework)
input wire [ 31:0] s_rfnoc_ctrl_tdata,
input wire s_rfnoc_ctrl_tlast,
input wire s_rfnoc_ctrl_tvalid,
output wire s_rfnoc_ctrl_tready,
// AXIS-Ctrl Output Port (to framework)
output wire [ 31:0] m_rfnoc_ctrl_tdata,
output wire m_rfnoc_ctrl_tlast,
output wire m_rfnoc_ctrl_tvalid,
input wire m_rfnoc_ctrl_tready
);
`include "usrp_utils.svh"
import ctrlport_pkg::*;
import rfnoc_chdr_utils_pkg::*;
import fft_core_regs_pkg::FFT_CORE_ADDR_W;
localparam ITEM_W = 32;
//---------------------------------------------------------------------------
// Signal Declarations
//---------------------------------------------------------------------------
// Clocks and Resets
logic ce_rst;
logic ctrlport_req_wr;
logic ctrlport_req_rd;
logic [CTRLPORT_ADDR_W-1:0] ctrlport_req_addr;
logic [CTRLPORT_DATA_W-1:0] ctrlport_req_data;
logic ctrlport_resp_ack;
logic [CTRLPORT_DATA_W-1:0] ctrlport_resp_data;
logic [ ITEM_W*NUM_PORTS-1:0] in_axis_tdata;
logic [ NUM_PORTS-1:0] in_axis_tkeep;
logic [ NUM_PORTS-1:0] in_axis_tlast;
logic [ NUM_PORTS-1:0] in_axis_tvalid;
logic [ NUM_PORTS-1:0] in_axis_tready;
logic [CHDR_TIMESTAMP_W*NUM_PORTS-1:0] in_axis_ttimestamp;
logic [ NUM_PORTS-1:0] in_axis_thas_time;
logic [ CHDR_LENGTH_W*NUM_PORTS-1:0] in_axis_tlength;
logic [ NUM_PORTS-1:0] in_axis_teov;
logic [ NUM_PORTS-1:0] in_axis_teob;
logic [ ITEM_W*NUM_PORTS-1:0] out_axis_tdata;
logic [ NUM_PORTS-1:0] out_axis_tkeep;
logic [ NUM_PORTS-1:0] out_axis_tlast;
logic [ NUM_PORTS-1:0] out_axis_tvalid;
logic [ NUM_PORTS-1:0] out_axis_tready;
logic [CHDR_TIMESTAMP_W*NUM_PORTS-1:0] out_axis_ttimestamp;
logic [ NUM_PORTS-1:0] out_axis_thas_time;
logic [ CHDR_LENGTH_W*NUM_PORTS-1:0] out_axis_tlength;
logic [ NUM_PORTS-1:0] out_axis_teov;
logic [ NUM_PORTS-1:0] out_axis_teob;
//---------------------------------------------------------------------------
// NoC Shell
//---------------------------------------------------------------------------
noc_shell_fft #(
.CHDR_W (CHDR_W),
.THIS_PORTID(THIS_PORTID),
.MTU (MTU),
.NUM_PORTS (NUM_PORTS)
) noc_shell_fft_i (
//---------------------
// Framework Interface
//---------------------
// Clock Inputs
.rfnoc_chdr_clk (rfnoc_chdr_clk),
.rfnoc_ctrl_clk (rfnoc_ctrl_clk),
.ce_clk (ce_clk),
// Reset Outputs
.rfnoc_chdr_rst (),
.rfnoc_ctrl_rst (),
.ce_rst (ce_rst),
// RFNoC Backend Interface
.rfnoc_core_config (rfnoc_core_config),
.rfnoc_core_status (rfnoc_core_status),
// CHDR Input Ports (from framework)
.s_rfnoc_chdr_tdata (s_rfnoc_chdr_tdata),
.s_rfnoc_chdr_tlast (s_rfnoc_chdr_tlast),
.s_rfnoc_chdr_tvalid (s_rfnoc_chdr_tvalid),
.s_rfnoc_chdr_tready (s_rfnoc_chdr_tready),
// CHDR Output Ports (to framework)
.m_rfnoc_chdr_tdata (m_rfnoc_chdr_tdata),
.m_rfnoc_chdr_tlast (m_rfnoc_chdr_tlast),
.m_rfnoc_chdr_tvalid (m_rfnoc_chdr_tvalid),
.m_rfnoc_chdr_tready (m_rfnoc_chdr_tready),
// AXIS-Ctrl Input Port (from framework)
.s_rfnoc_ctrl_tdata (s_rfnoc_ctrl_tdata),
.s_rfnoc_ctrl_tlast (s_rfnoc_ctrl_tlast),
.s_rfnoc_ctrl_tvalid (s_rfnoc_ctrl_tvalid),
.s_rfnoc_ctrl_tready (s_rfnoc_ctrl_tready),
// AXIS-Ctrl Output Port (to framework)
.m_rfnoc_ctrl_tdata (m_rfnoc_ctrl_tdata),
.m_rfnoc_ctrl_tlast (m_rfnoc_ctrl_tlast),
.m_rfnoc_ctrl_tvalid (m_rfnoc_ctrl_tvalid),
.m_rfnoc_ctrl_tready (m_rfnoc_ctrl_tready),
//---------------------
// Client Interface
//---------------------
// CtrlPort Clock and Reset
.ctrlport_clk (),
.ctrlport_rst (),
// CtrlPort Master
.m_ctrlport_req_wr (ctrlport_req_wr),
.m_ctrlport_req_rd (ctrlport_req_rd),
.m_ctrlport_req_addr (ctrlport_req_addr),
.m_ctrlport_req_data (ctrlport_req_data),
.m_ctrlport_resp_ack (ctrlport_resp_ack),
.m_ctrlport_resp_data (ctrlport_resp_data),
// AXI-Stream Clock and Reset
.axis_data_clk (),
.axis_data_rst (),
// Data Stream to User Logic: in
.m_in_axis_tdata (in_axis_tdata),
.m_in_axis_tkeep (in_axis_tkeep),
.m_in_axis_tlast (in_axis_tlast),
.m_in_axis_tvalid (in_axis_tvalid),
.m_in_axis_tready (in_axis_tready),
.m_in_axis_ttimestamp (in_axis_ttimestamp),
.m_in_axis_thas_time (in_axis_thas_time),
.m_in_axis_tlength (in_axis_tlength),
.m_in_axis_teov (in_axis_teov),
.m_in_axis_teob (in_axis_teob),
// Data Stream from User Logic: out
.s_out_axis_tdata (out_axis_tdata),
.s_out_axis_tkeep (out_axis_tkeep),
.s_out_axis_tlast (out_axis_tlast),
.s_out_axis_tvalid (out_axis_tvalid),
.s_out_axis_tready (out_axis_tready),
.s_out_axis_ttimestamp(out_axis_ttimestamp),
.s_out_axis_thas_time (out_axis_thas_time),
.s_out_axis_tlength (out_axis_tlength),
.s_out_axis_teov (out_axis_teov),
.s_out_axis_teob (out_axis_teob)
);
//---------------------------------------------------------------------------
// CtrlPort Splitter
//---------------------------------------------------------------------------
wire [ NUM_CORES-1:0] dec_ctrlport_req_wr;
wire [ NUM_CORES-1:0] dec_ctrlport_req_rd;
wire [CTRLPORT_ADDR_W*NUM_CORES-1:0] dec_ctrlport_req_addr;
wire [CTRLPORT_DATA_W*NUM_CORES-1:0] dec_ctrlport_req_data;
wire [ NUM_CORES-1:0] dec_ctrlport_resp_ack;
wire [CTRLPORT_DATA_W*NUM_CORES-1:0] dec_ctrlport_resp_data;
generate
if (NUM_CORES > 1) begin : gen_ctrlport_decoder
ctrlport_decoder #(
.NUM_SLAVES (NUM_CORES),
.BASE_ADDR (0),
.SLAVE_ADDR_W (FFT_CORE_ADDR_W)
) ctrlport_decoder_i (
.ctrlport_clk (ce_clk),
.ctrlport_rst (ce_rst),
.s_ctrlport_req_wr (ctrlport_req_wr),
.s_ctrlport_req_rd (ctrlport_req_rd),
.s_ctrlport_req_addr (ctrlport_req_addr),
.s_ctrlport_req_data (ctrlport_req_data),
.s_ctrlport_req_byte_en ('1),
.s_ctrlport_req_has_time ('0),
.s_ctrlport_req_time ('0),
.s_ctrlport_resp_ack (ctrlport_resp_ack),
.s_ctrlport_resp_status (),
.s_ctrlport_resp_data (ctrlport_resp_data),
.m_ctrlport_req_wr (dec_ctrlport_req_wr),
.m_ctrlport_req_rd (dec_ctrlport_req_rd),
.m_ctrlport_req_addr (dec_ctrlport_req_addr),
.m_ctrlport_req_data (dec_ctrlport_req_data),
.m_ctrlport_req_byte_en (),
.m_ctrlport_req_has_time (),
.m_ctrlport_req_time (),
.m_ctrlport_resp_ack (dec_ctrlport_resp_ack),
.m_ctrlport_resp_status ('0),
.m_ctrlport_resp_data (dec_ctrlport_resp_data)
);
end else begin : gen_no_decoder
assign dec_ctrlport_req_wr = ctrlport_req_wr;
assign dec_ctrlport_req_rd = ctrlport_req_rd;
assign dec_ctrlport_req_addr = {{CTRLPORT_DATA_W-FFT_CORE_ADDR_W{1'b0}},
ctrlport_req_addr[FFT_CORE_ADDR_W-1:0]};
assign dec_ctrlport_req_data = ctrlport_req_data;
assign ctrlport_resp_ack = dec_ctrlport_resp_ack;
assign ctrlport_resp_data = dec_ctrlport_resp_data;
end
endgenerate
//---------------------------------------------------------------------------
// FFT Core
//---------------------------------------------------------------------------
// Calculate the number of ports per core
localparam int NPPC = NUM_PORTS / NUM_CORES;
if (NUM_CORES * NPPC != NUM_PORTS) begin : check_num_ports_per_core
// We require each FFT core instance to have the same number of channels.
ERROR__NUM_PORTS_must_be_a_multiple_of_NUM_CORES();
end : check_num_ports_per_core
genvar core_i;
for (core_i = 0; core_i < NUM_CORES; core_i = core_i+1) begin : gen_fft_cores
fft_core #(
.NUM_CHAN (NPPC),
.NUM_CORES (NUM_CORES),
.MAX_FFT_SIZE_LOG2 (MAX_FFT_SIZE_LOG2),
.MAX_CP_LIST_LEN_INS_LOG2(MAX_CP_LIST_LEN_INS_LOG2),
.MAX_CP_LIST_LEN_REM_LOG2(MAX_CP_LIST_LEN_REM_LOG2),
.CP_INSERTION_REPEAT (CP_INSERTION_REPEAT),
.CP_REMOVAL_REPEAT (CP_REMOVAL_REPEAT),
.EN_FFT_BYPASS (EN_FFT_BYPASS),
.EN_FFT_ORDER (EN_FFT_ORDER),
.EN_MAGNITUDE (EN_MAGNITUDE),
.EN_MAGNITUDE_SQ (EN_MAGNITUDE_SQ),
.USE_APPROX_MAG (USE_APPROX_MAG)
) fft_core_i (
.ce_clk (ce_clk),
.ce_rst (ce_rst),
.s_ctrlport_req_wr (`BUS_I(dec_ctrlport_req_wr, 1, core_i)),
.s_ctrlport_req_rd (`BUS_I(dec_ctrlport_req_rd, 1, core_i)),
.s_ctrlport_req_addr (`BUS_I(dec_ctrlport_req_addr, CTRLPORT_ADDR_W, core_i)),
.s_ctrlport_req_data (`BUS_I(dec_ctrlport_req_data, CTRLPORT_DATA_W, core_i)),
.s_ctrlport_resp_ack (`BUS_I(dec_ctrlport_resp_ack, 1, core_i)),
.s_ctrlport_resp_data (`BUS_I(dec_ctrlport_resp_data, CTRLPORT_DATA_W, core_i)),
.s_in_axis_tdata (`BUS_I(in_axis_tdata, ITEM_W*NPPC, core_i)),
.s_in_axis_tkeep (`BUS_I(in_axis_tkeep, 1*NPPC, core_i)),
.s_in_axis_tlast (`BUS_I(in_axis_tlast, 1*NPPC, core_i)),
.s_in_axis_tvalid (`BUS_I(in_axis_tvalid, 1*NPPC, core_i)),
.s_in_axis_tready (`BUS_I(in_axis_tready, 1*NPPC, core_i)),
.s_in_axis_ttimestamp (`BUS_I(in_axis_ttimestamp, CHDR_TIMESTAMP_W*NPPC, core_i)),
.s_in_axis_thas_time (`BUS_I(in_axis_thas_time, 1*NPPC, core_i)),
.s_in_axis_tlength (`BUS_I(in_axis_tlength, CHDR_LENGTH_W*NPPC, core_i)),
.s_in_axis_teov (`BUS_I(in_axis_teov, 1*NPPC, core_i)),
.s_in_axis_teob (`BUS_I(in_axis_teob, 1*NPPC, core_i)),
.m_out_axis_tdata (`BUS_I(out_axis_tdata, ITEM_W*NPPC, core_i)),
.m_out_axis_tkeep (`BUS_I(out_axis_tkeep, 1*NPPC, core_i)),
.m_out_axis_tlast (`BUS_I(out_axis_tlast, 1*NPPC, core_i)),
.m_out_axis_tvalid (`BUS_I(out_axis_tvalid, 1*NPPC, core_i)),
.m_out_axis_tready (`BUS_I(out_axis_tready, 1*NPPC, core_i)),
.m_out_axis_ttimestamp(`BUS_I(out_axis_ttimestamp, CHDR_TIMESTAMP_W*NPPC, core_i)),
.m_out_axis_thas_time (`BUS_I(out_axis_thas_time, 1*NPPC, core_i)),
.m_out_axis_tlength (`BUS_I(out_axis_tlength, CHDR_LENGTH_W*NPPC, core_i)),
.m_out_axis_teov (`BUS_I(out_axis_teov, 1*NPPC, core_i)),
.m_out_axis_teob (`BUS_I(out_axis_teob, 1*NPPC, core_i))
);
end : gen_fft_cores
endmodule : rfnoc_block_fft
`default_nettype wire