fpga: rfnoc: fft: Support multiple samples per cycle
This adds the NIPC parameter, which configures support for processing multiple items or samples per clock cycle. With this enabled, the FFT block can process at rates higher than 250 MSPS, such as 500 MSPS and beyond. Original-commit: fc76aa940e121fe1f85a3513f6d90df4667338cf
This commit is contained in:
@@ -8,14 +8,16 @@ RFNOC_OOT_SRCS += $(abspath $(addprefix $(BASE_DIR)/../lib/rfnoc/blocks/rfnoc_bl
|
||||
fft_reorder_pkg.sv \
|
||||
fft_reorder.sv \
|
||||
fft_post_processing.sv \
|
||||
cp_removal.sv \
|
||||
axis_cp_list.sv \
|
||||
cp_removal.sv \
|
||||
noc_shell_fft.v \
|
||||
xfft_config_pkg.sv \
|
||||
fft_core_regs_pkg.sv \
|
||||
fft_packetize_pkg.sv \
|
||||
fft_packetize.sv \
|
||||
fft_depacketize.sv \
|
||||
fft_pipeline.sv \
|
||||
fft_pipeline_wrapper.sv \
|
||||
xfft_wrapper.sv \
|
||||
fft_core.sv \
|
||||
rfnoc_block_fft.sv \
|
||||
|
||||
@@ -7,228 +7,109 @@
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// Removes the cyclic prefix from OFDM symbols. A configuration list allows
|
||||
// for queuing up multiple cyclic prefix lengths, and has an optional repeat
|
||||
// mode that causes the same list of cyclic prefixes to be reused as new
|
||||
// symbols arrive. This allows the block to execute a pattern for cases when
|
||||
// CP lengths change symbol to symbol in a repeating pattern.
|
||||
//
|
||||
// There is a two-clock bubble cycle after every symbol due to returning to
|
||||
// the idle state to load the next config, so this block must be clocked at
|
||||
// least slightly faster than the sample rate. That is:
|
||||
//
|
||||
// Clock rate > Fs * (1 + 2/(CP length + FFT Size))
|
||||
// Removes the cyclic prefix from OFDM symbols. This module assumes that each
|
||||
// AXI-stream input packet is one symbol with a prefix and it will output one
|
||||
// packet per symbol with the prefix removed. The cyclic prefix length to be
|
||||
// removed is input on the cp_len AXI-Stream input port, and it must be
|
||||
// present at the start of each data packet until cp_len_tready is asserted.
|
||||
// There is a one-cycle bubble at the start of each symbol to register cyclic
|
||||
// prefix length.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// DATA_W : Data/sample AXI-Stream bus width
|
||||
// USER_W : Width of TUSER on the data/sample AXI-Stream bus
|
||||
// SYM_LEN_W : Width of the maximum symbol length. The maximum
|
||||
// supported symbol length is 2**SYM_LEN_W - 1.
|
||||
// CP_LEN_W : Width of the maximum cyclic prefix length. The maximum
|
||||
// supported CP length is 2**CP_LEN_W - 1.
|
||||
// DEFAULT_CP_LEN : Default cyclic prefix length to output
|
||||
// CP_REPEAT : 1: Cyclic prefix list repeats. 0: Cyclic prefix list
|
||||
// does not repeat, and the last used prefix length will
|
||||
// be used once the list is completed.
|
||||
// MAX_LIST_LOG2 : Log base 2 of the size of the prefix length list
|
||||
// SET_TLAST : 1: Always set tlast at the end of each symbol. 0: Pass
|
||||
// through input tlast unchanged.
|
||||
//
|
||||
// Signals:
|
||||
//
|
||||
// clear_list : Clear the CP removal list
|
||||
// symbol_len : Symbol/FFT size to use for generating TLAST
|
||||
// cp_len_t* : AXI-Stream cyclic prefix length list input. Use this to
|
||||
// write prefix lengths to the list in order.
|
||||
// cp_list_occupied : Number of items in the cyclic prefix list
|
||||
// i_t* : AXI-Stream data input on which to do cyclic prefix removal
|
||||
// o_t* : AXI-Stream data output with cyclic prefix removed
|
||||
// DATA_W : Data/sample AXI-Stream bus width
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
|
||||
module cp_removal #(
|
||||
parameter int DATA_W = 32,
|
||||
parameter int USER_W = 1,
|
||||
parameter int CP_LEN_W = 16,
|
||||
parameter int SYM_LEN_W = 17,
|
||||
parameter int DEFAULT_CP_LEN = 0,
|
||||
parameter bit CP_REPEAT = 0,
|
||||
parameter int MAX_LIST_LOG2 = 5,
|
||||
parameter bit SET_TLAST = 1
|
||||
int CP_LEN_W = 12,
|
||||
int DATA_W = 32
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
input wire clear_list,
|
||||
|
||||
// Cyclic prefix length input port
|
||||
input wire [SYM_LEN_W-1:0] symbol_len,
|
||||
input wire [ CP_LEN_W-1:0] cp_len_tdata,
|
||||
input wire cp_len_tvalid,
|
||||
output wire cp_len_tready,
|
||||
output wire [ 15:0] cp_list_occupied,
|
||||
output reg cp_len_tready,
|
||||
|
||||
// Symbol data stream input
|
||||
input wire [ DATA_W-1:0] i_tdata,
|
||||
input wire [ USER_W-1:0] i_tuser,
|
||||
input wire i_tlast,
|
||||
input wire i_tvalid,
|
||||
output wire i_tready,
|
||||
|
||||
// Symbol data stream output
|
||||
// Symbol data stream output (one symbol per packet)
|
||||
output wire [ DATA_W-1:0] o_tdata,
|
||||
output wire [ USER_W-1:0] o_tuser,
|
||||
output wire o_tlast,
|
||||
output wire o_tvalid,
|
||||
input wire o_tready
|
||||
);
|
||||
`include "usrp_utils.svh"
|
||||
|
||||
enum logic [2:0] { S_IDLE, S_CONFIG, S_PREFIX, S_SYMBOL, S_CLEAR } state;
|
||||
logic [CP_LEN_W-1:0] cp_len_reg;
|
||||
logic [CP_LEN_W-1:0] count = 1;
|
||||
|
||||
logic [CP_LEN_W-1:0] fifo_in_tdata, fifo_out_tdata;
|
||||
logic fifo_in_tvalid, fifo_out_tvalid;
|
||||
logic fifo_in_tready, fifo_out_tready;
|
||||
logic fifo_clear;
|
||||
|
||||
assign fifo_clear = (state == S_CLEAR);
|
||||
|
||||
axi_fifo #(
|
||||
.WIDTH(CP_LEN_W),
|
||||
.SIZE (MAX_LIST_LOG2)
|
||||
) axi_fifo_config_inst (
|
||||
.clk (clk),
|
||||
.reset (rst),
|
||||
.clear (fifo_clear),
|
||||
.i_tdata (fifo_in_tdata),
|
||||
.i_tvalid(fifo_in_tvalid),
|
||||
.i_tready(fifo_in_tready),
|
||||
.o_tdata (fifo_out_tdata),
|
||||
.o_tvalid(fifo_out_tvalid),
|
||||
.o_tready(fifo_out_tready),
|
||||
.space (),
|
||||
.occupied(cp_list_occupied)
|
||||
);
|
||||
|
||||
generate
|
||||
if (CP_REPEAT == 0) begin
|
||||
// No config list loopback. New configs can be written at any time.
|
||||
assign fifo_in_tdata = cp_len_tdata;
|
||||
assign fifo_in_tvalid = (state == S_CLEAR) ? 1'b0 : cp_len_tvalid;
|
||||
assign cp_len_tready = (state == S_CLEAR) ? 1'b0 : fifo_in_tready;
|
||||
assign fifo_out_tready = (state == S_CONFIG);
|
||||
end else begin
|
||||
// Config list loopback enabled. Write current config back into config
|
||||
// FIFO in the S_CONFIG state. New configs can be written in any state
|
||||
// but S_CONFIG & S_CLEAR.
|
||||
assign fifo_in_tdata = (state == S_CONFIG) ? fifo_out_tdata :
|
||||
cp_len_tdata;
|
||||
assign fifo_in_tvalid = (state == S_CONFIG) ? fifo_out_tvalid :
|
||||
(state == S_CLEAR) ? 1'b0 :
|
||||
cp_len_tvalid;
|
||||
assign cp_len_tready = (state == S_CONFIG) ? 1'b0 :
|
||||
(state == S_CLEAR) ? 1'b0 :
|
||||
fifo_in_tready;
|
||||
assign fifo_out_tready = (state == S_CONFIG);
|
||||
end
|
||||
endgenerate
|
||||
|
||||
localparam COUNT_W = `MAX(SYM_LEN_W, CP_LEN_W);
|
||||
|
||||
logic [ CP_LEN_W-1:0] cp_len_reg = DEFAULT_CP_LEN;
|
||||
logic [SYM_LEN_W-1:0] symbol_len_reg = '0;
|
||||
logic [ COUNT_W-1:0] count = '0;
|
||||
logic clear_fifo_hold = 1'b0;
|
||||
enum logic [1:0] { ST_IDLE, ST_PREFIX, ST_BODY } state;
|
||||
|
||||
always @(posedge clk) begin
|
||||
// Latch FIFO clear
|
||||
if (clear_list) begin
|
||||
clear_fifo_hold <= 1'b1;
|
||||
cp_len_tready <= 1'b0;
|
||||
|
||||
case (state)
|
||||
// Wait in idle state until we get a new packet and the cyclic prefix
|
||||
// length.
|
||||
ST_IDLE : begin
|
||||
count <= 1;
|
||||
cp_len_reg <= cp_len_tdata;
|
||||
if (i_tvalid && cp_len_tvalid) begin
|
||||
cp_len_tready <= 1'b1;
|
||||
if (cp_len_tdata > 0) begin
|
||||
state <= ST_PREFIX;
|
||||
end else begin
|
||||
state <= ST_BODY;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// State machine
|
||||
case (state)
|
||||
// Wait in idle state until either a configuration list clear is
|
||||
// requested or we get a new data input.
|
||||
S_IDLE : begin
|
||||
count <= 1;
|
||||
if (clear_fifo_hold) begin
|
||||
state <= S_CLEAR;
|
||||
end else if (i_tvalid) begin
|
||||
// Only update the CP length being used if there's a valid one in the
|
||||
// list. Otherwise, keep using the previous value.
|
||||
if (fifo_out_tvalid) begin
|
||||
cp_len_reg <= fifo_out_tdata;
|
||||
end
|
||||
symbol_len_reg <= symbol_len;
|
||||
state <= S_CONFIG;
|
||||
end
|
||||
end
|
||||
S_CONFIG : begin
|
||||
if (cp_len_reg > 0) begin
|
||||
state <= S_PREFIX;
|
||||
end else if (symbol_len_reg > 0) begin
|
||||
state <= S_SYMBOL;
|
||||
end else begin
|
||||
state <= S_IDLE;
|
||||
end
|
||||
end
|
||||
S_PREFIX : begin
|
||||
if (i_tvalid & i_tready) begin
|
||||
// Remove the prefix
|
||||
ST_PREFIX : begin
|
||||
if (i_tvalid && i_tready) begin
|
||||
count <= count + 1;
|
||||
if (count >= cp_len_reg) begin
|
||||
if (count == cp_len_reg) begin
|
||||
count <= 1;
|
||||
if (symbol_len_reg > 0) begin
|
||||
state <= S_SYMBOL;
|
||||
end else begin
|
||||
state <= S_IDLE;
|
||||
state <= ST_BODY;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
S_SYMBOL : begin
|
||||
if (i_tvalid & i_tready) begin
|
||||
count <= count + 1;
|
||||
if (count >= symbol_len_reg) begin
|
||||
|
||||
// Pass through the rest until the end of the packet.
|
||||
ST_BODY : begin
|
||||
count <= 1;
|
||||
state <= S_IDLE;
|
||||
if (i_tvalid && i_tready) begin
|
||||
if(i_tlast) begin
|
||||
state <= ST_IDLE;
|
||||
end
|
||||
end
|
||||
end
|
||||
S_CLEAR : begin
|
||||
clear_fifo_hold <= 1'b0;
|
||||
cp_len_reg <= DEFAULT_CP_LEN;
|
||||
state <= S_IDLE;
|
||||
end
|
||||
default : state <= S_IDLE;
|
||||
endcase
|
||||
|
||||
if (rst) begin
|
||||
clear_fifo_hold <= 1'b0;
|
||||
cp_len_reg <= DEFAULT_CP_LEN;
|
||||
count <= 1;
|
||||
state <= S_IDLE;
|
||||
state <= ST_IDLE;
|
||||
count <= 'X;
|
||||
cp_len_reg <= 'X;
|
||||
cp_len_tready <= '0;
|
||||
end
|
||||
end
|
||||
|
||||
logic new_tlast;
|
||||
assign new_tlast = (state == S_SYMBOL) & (count >= symbol_len_reg);
|
||||
|
||||
assign o_tdata = i_tdata;
|
||||
assign o_tuser = i_tuser;
|
||||
assign o_tlast = (SET_TLAST == 0) ? i_tlast : new_tlast;
|
||||
assign o_tvalid = (state == S_IDLE) ? 1'b0 :
|
||||
(state == S_PREFIX) ? 1'b0 :
|
||||
(state == S_SYMBOL) ? i_tvalid :
|
||||
(state == S_CLEAR) ? 1'b0 :
|
||||
1'b0;
|
||||
assign i_tready = (state == S_IDLE) ? 1'b0 :
|
||||
(state == S_PREFIX) ? 1'b1 :
|
||||
(state == S_SYMBOL) ? o_tready :
|
||||
(state == S_CLEAR) ? 1'b0 :
|
||||
1'b0;
|
||||
assign o_tlast = i_tlast;
|
||||
assign o_tvalid = (state == ST_BODY ) ? i_tvalid : 1'b0;
|
||||
assign i_tready = (state == ST_BODY ) ? o_tready :
|
||||
(state == ST_PREFIX) ? 1'b1 : 1'b0;
|
||||
|
||||
endmodule : cp_removal
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -80,17 +80,24 @@ package fft_core_regs_pkg;
|
||||
//
|
||||
// Returns information about the post-processing capabilities.
|
||||
//
|
||||
// [3] : MAGNITUDE_SQ. Indicates whether or not the magnitude-squared
|
||||
// [11:8] : Log base 2 of the number of items per clock cycle (NIPC)
|
||||
// processed by this core. For example, a value of 3 in this field
|
||||
// means that the NIPC is 2**3 == 8. Packet sizes and cyclic prefix
|
||||
// lengths must be a multiple of the NIPC value.
|
||||
// [ 7:6] : Reserved
|
||||
// [ 5] : CP_INSERTION. Indicates if cyclic-prefix insertion is available.
|
||||
// [ 4] : CP_REMOVAL. Indicates if cyclic-prefix removal is available.
|
||||
// [ 3] : MAGNITUDE_SQ. Indicates whether or not the magnitude-squared
|
||||
// output capability is present in the core.
|
||||
// [2] : MAGNITUDE. Indicates whether or not the magnitude output option
|
||||
// [ 2] : MAGNITUDE. Indicates whether or not the magnitude output option
|
||||
// is present in the core.
|
||||
// [1] : FFT_ORDER. Indicates whether or not the FFT reorder capability is
|
||||
// [ 1] : FFT_ORDER. Indicates whether or not the FFT reorder capability is
|
||||
// present in the core.
|
||||
// [0] : FFT_BYPASS. Indicates whether or not the FFT bypass capability is
|
||||
// [ 0] : FFT_BYPASS. Indicates whether or not the FFT bypass capability is
|
||||
// present in the core.
|
||||
//
|
||||
localparam int REG_CAPABILITIES2_ADDR = 'h0C;
|
||||
localparam int REG_CAPABILITIES2_WIDTH = 4;
|
||||
localparam int REG_CAPABILITIES2_WIDTH = 12;
|
||||
|
||||
// REG_RESET (Write-only strobe)
|
||||
//
|
||||
@@ -239,6 +246,9 @@ package fft_core_regs_pkg;
|
||||
// negative frequencies. 0 Hz in the center.
|
||||
// 2 : NATURAL. Positive frequencies are first, followed by negative
|
||||
// frequencies. 0 Hz is on the left.
|
||||
// 3 : BIT_REVERSE. Like natural, but the bits of the indices are in
|
||||
// reverse order. For example, for a size 16 FFT, bin 0000 is output
|
||||
// first, followed by bin 1000, 0100, 1100, 0010, etc.
|
||||
//
|
||||
localparam int REG_ORDER_ADDR = 'h48;
|
||||
localparam int REG_ORDER_WIDTH = 2;
|
||||
@@ -246,6 +256,7 @@ package fft_core_regs_pkg;
|
||||
localparam int FFT_ORDER_NORMAL = 0;
|
||||
localparam int FFT_ORDER_REVERSE = 1;
|
||||
localparam int FFT_ORDER_NATURAL = 2;
|
||||
localparam int FFT_ORDER_BIT_REVERSE = 3;
|
||||
|
||||
// REG_MAGNITUDE (Read/Write)
|
||||
//
|
||||
|
||||
@@ -227,7 +227,7 @@ module fft_depacketize
|
||||
|
||||
symbol_state_t symbol_state = WAIT_SYMBOL_ST;
|
||||
|
||||
logic last_symbol;
|
||||
logic cp_last_symbol;
|
||||
logic prefix_rd_stb = 1'b0;
|
||||
logic [ CP_LEN_W-1:0] cp_len;
|
||||
logic [FFT_SIZE_W-1:0] symbol_size;
|
||||
@@ -258,7 +258,7 @@ module fft_depacketize
|
||||
// wait for their tready signals to be asserted, which on the
|
||||
// axi_fifo indicates that they are not full.
|
||||
i_symbol_tready <= i_symbol_fifo_tready && i_cp_ins_fifo_tready;
|
||||
last_symbol <= i_symbol_tdata.last;
|
||||
cp_last_symbol <= i_symbol_tdata.last;
|
||||
cp_len <= i_cp_ins_tdata;
|
||||
if (i_symbol_tvalid && i_symbol_tready) begin
|
||||
i_symbol_tready <= 1'b0;
|
||||
@@ -289,7 +289,7 @@ module fft_depacketize
|
||||
prefix_rd_stb <= 1'b0;
|
||||
i_symbol_tready <= 1'b0;
|
||||
i_symbol_fifo_tvalid <= 1'b0;
|
||||
last_symbol <= 1'bX;
|
||||
cp_last_symbol <= 1'bX;
|
||||
cp_len <= 'X;
|
||||
symbol_size <= 'X;
|
||||
end
|
||||
@@ -300,9 +300,7 @@ module fft_depacketize
|
||||
// Symbol Information FIFO
|
||||
//---------------------------------
|
||||
|
||||
logic [15:0] symbol_fifo_space;
|
||||
|
||||
assign i_symbol_fifo_tdata = '{ last_symbol, symbol_size };
|
||||
assign i_symbol_fifo_tdata = '{ cp_last_symbol, symbol_size };
|
||||
|
||||
axi_fifo #(
|
||||
.WIDTH($bits(symbol_fifo_t)),
|
||||
@@ -317,7 +315,7 @@ module fft_depacketize
|
||||
.o_tdata (o_symbol_fifo_tdata ),
|
||||
.o_tvalid(o_symbol_fifo_tvalid),
|
||||
.o_tready(o_symbol_fifo_tready),
|
||||
.space (symbol_fifo_space ),
|
||||
.space ( ),
|
||||
.occupied( )
|
||||
);
|
||||
|
||||
@@ -428,8 +426,6 @@ module fft_depacketize
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
localparam int BYTES_PER_ITEM = (ITEM_W/8);
|
||||
|
||||
typedef enum logic [2:0] {
|
||||
WAIT_BURST_ST,
|
||||
CALC_ITEMS_ST,
|
||||
|
||||
@@ -423,7 +423,9 @@ module fft_packetize
|
||||
.i_tready(i_fft_tready ),
|
||||
.o_tdata ({o_fft_tlast, o_fft_tkeep, o_fft_tdata}),
|
||||
.o_tvalid(o_fft_tvalid ),
|
||||
.o_tready(o_fft_tready )
|
||||
.o_tready(o_fft_tready ),
|
||||
.space ( ),
|
||||
.occupied( )
|
||||
);
|
||||
end else begin : gen_no_output_fifo
|
||||
assign o_fft_tdata = i_fft_tdata;
|
||||
|
||||
@@ -0,0 +1,442 @@
|
||||
//
|
||||
// Copyright 2024 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: fft_pipeline
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// The module contains all FFT processing for a single channel, including
|
||||
// cyclic prefix removal, cyclic prefix insertion, FFT/IFFT, and logic to
|
||||
// change the output order of the FFT data.
|
||||
//
|
||||
// The data is input on the data input (i_t*) and output on the data output
|
||||
// (o_t*) ports. There must be one FFT/IFFT per packet, plus cyclic-prefix to
|
||||
// be removed, if applicable.
|
||||
//
|
||||
// The "global FFT settings" are treated as fixed values that won't change
|
||||
// for the duration of a single FFT/IFFT. These should only be updated when
|
||||
// everything is idle and there is no data in flight.
|
||||
//
|
||||
// The fft_config_t* input contains the per-FFT settings for the Xilinx FFT
|
||||
// core and you should write once per FFT.
|
||||
//
|
||||
// The cp_rem_t* and cp_ins_t* are the cyclic prefix removal and insertion
|
||||
// lengths. You should write one length per FFT/IFFT.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// MAX_FFT_SIZE_LOG2 : Set to the log base 2 of the maximum FFT size to be
|
||||
// supported. For example, a value of 14 means the
|
||||
// maximum FFT size is 2**14 = 4096.
|
||||
// EN_CONFIG_FIFO : When 1, the fft_config_tdata AXI-Stream input is used
|
||||
// in order to allow a unique configuration per FFT
|
||||
// operation. If EN_CONFIG_FIFO is 0, then the fft_config
|
||||
// input is used instead and it is assumed to be static
|
||||
// for the duration of the FFT operation and must only
|
||||
// change while the module is idle.
|
||||
// EN_CP_REMOVAL : Controls whether to include the cyclic prefix removal
|
||||
// logic.
|
||||
// EN_CP_INSERTION : Controls whether to include the cyclic prefix
|
||||
// insertion logic. If included, EN_FFT_ORDER must be 1.
|
||||
// EN_FFT_ORDER : Set to 1 to add the optional FFT reorder core. Set to
|
||||
// 0 to remove it and save resources. Removing it also
|
||||
// disable CP insertion.
|
||||
// EN_MAGNITUDE : Set to 1 to add the magnitude output calculation core.
|
||||
// Set to 0 to remove it and save resources.
|
||||
// EN_MAGNITUDE_SQ : Set to 1 to add the magnitude squared output
|
||||
// calculation core. Set to 0 to remove it and save
|
||||
// resources.
|
||||
// USE_APPROX_MAG : Control which magnitude calculation to use. Set to 1
|
||||
// to use a simpler circuit that gives pretty good
|
||||
// results in order to save resources. Set to 0 to use
|
||||
// the CORDIC IP to calculate the magnitude.
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
|
||||
module fft_pipeline
|
||||
import xfft_config_pkg::*;
|
||||
#(
|
||||
int MAX_FFT_SIZE_LOG2 = 12,
|
||||
bit EN_CONFIG_FIFO = 1,
|
||||
bit EN_CP_REMOVAL = 1,
|
||||
bit EN_CP_INSERTION = 1,
|
||||
bit EN_FFT_ORDER = 1,
|
||||
bit EN_MAGNITUDE = 1,
|
||||
bit EN_MAGNITUDE_SQ = 1,
|
||||
bit USE_APPROX_MAG = 1,
|
||||
|
||||
localparam int FFT_CONFIG_W = fft_config_w(MAX_FFT_SIZE_LOG2),
|
||||
localparam int DATA_W = 32,
|
||||
localparam int FFT_SIZE_LOG2_W = $clog2(MAX_FFT_SIZE_LOG2+1),
|
||||
localparam int CP_LEN_W = MAX_FFT_SIZE_LOG2
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
// Global FFT settings
|
||||
input wire [ 1:0] fft_order,
|
||||
input wire [ 1:0] magnitude,
|
||||
input wire [FFT_SIZE_LOG2_W-1:0] fft_size_log2,
|
||||
input wire [ FFT_CONFIG_W-1:0] fft_config,
|
||||
|
||||
// FFT IP Configuration
|
||||
input wire [FFT_CONFIG_W-1:0] fft_config_tdata,
|
||||
input wire fft_config_tvalid,
|
||||
output wire fft_config_tready,
|
||||
|
||||
// CP Removal Length
|
||||
input wire [CP_LEN_W-1:0] cp_rem_tdata,
|
||||
input wire cp_rem_tvalid,
|
||||
output wire cp_rem_tready,
|
||||
|
||||
// CP Insertion Length
|
||||
input wire [CP_LEN_W-1:0] cp_ins_tdata,
|
||||
input wire cp_ins_tvalid,
|
||||
output reg cp_ins_tready,
|
||||
|
||||
// FFT Event Monitoring
|
||||
output wire event_fft_overflow,
|
||||
|
||||
// Data Input Packets
|
||||
input wire [DATA_W-1:0] i_tdata,
|
||||
input wire i_tlast,
|
||||
input wire i_tvalid,
|
||||
output wire i_tready,
|
||||
|
||||
// Data Output Packets
|
||||
output wire [DATA_W-1:0] o_tdata,
|
||||
output wire o_tlast,
|
||||
output wire o_tvalid,
|
||||
input wire o_tready
|
||||
);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Input FIFOs
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
logic [FFT_CONFIG_W-1:0] fft_config_fifo_tdata;
|
||||
logic fft_config_fifo_tvalid;
|
||||
logic fft_config_fifo_tready;
|
||||
|
||||
logic [ CP_LEN_W-1:0] cp_rem_fifo_tdata;
|
||||
logic cp_rem_fifo_tvalid;
|
||||
logic cp_rem_fifo_tready;
|
||||
|
||||
logic [ CP_LEN_W-1:0] cp_ins_fifo_tdata;
|
||||
logic cp_ins_fifo_tvalid;
|
||||
logic cp_ins_fifo_tready;
|
||||
|
||||
logic [DATA_W-1:0] fft_fifo_tdata;
|
||||
logic fft_fifo_tlast;
|
||||
logic fft_fifo_tvalid;
|
||||
logic fft_fifo_tready;
|
||||
|
||||
if (EN_CONFIG_FIFO) begin : gen_config_fifo
|
||||
axi_fifo #(
|
||||
.WIDTH(FFT_CONFIG_W),
|
||||
.SIZE (1 )
|
||||
) axi_fifo_fft_config (
|
||||
.clk (clk ),
|
||||
.reset (rst ),
|
||||
.clear (1'b0 ),
|
||||
.i_tdata (fft_config_tdata ),
|
||||
.i_tvalid(fft_config_tvalid ),
|
||||
.i_tready(fft_config_tready ),
|
||||
.o_tdata (fft_config_fifo_tdata ),
|
||||
.o_tvalid(fft_config_fifo_tvalid),
|
||||
.o_tready(fft_config_fifo_tready),
|
||||
.space ( ),
|
||||
.occupied( )
|
||||
);
|
||||
end else begin : gen_no_config_fifo
|
||||
assign fft_config_tready = 1'b1;
|
||||
end
|
||||
|
||||
if (EN_CP_REMOVAL) begin : gen_cp_rem_fifo
|
||||
axi_fifo #(
|
||||
.WIDTH(CP_LEN_W),
|
||||
.SIZE (1 )
|
||||
) axi_fifo_cp_rem (
|
||||
.clk (clk ),
|
||||
.reset (rst ),
|
||||
.clear (1'b0 ),
|
||||
.i_tdata (cp_rem_tdata ),
|
||||
.i_tvalid(cp_rem_tvalid ),
|
||||
.i_tready(cp_rem_tready ),
|
||||
.o_tdata (cp_rem_fifo_tdata ),
|
||||
.o_tvalid(cp_rem_fifo_tvalid),
|
||||
.o_tready(cp_rem_fifo_tready),
|
||||
.space ( ),
|
||||
.occupied( )
|
||||
);
|
||||
end else begin : gen_no_cp_remo_fifo
|
||||
assign cp_rem_tready = 1'b1;
|
||||
assign cp_rem_fifo_tdata = '0;
|
||||
assign cp_rem_fifo_tvalid = 1'b1;
|
||||
end
|
||||
|
||||
if (EN_CP_INSERTION) begin : gen_cp_ins_fifo
|
||||
axi_fifo #(
|
||||
.WIDTH(CP_LEN_W),
|
||||
.SIZE (1 )
|
||||
) axi_fifo_cp_ins (
|
||||
.clk (clk ),
|
||||
.reset (rst ),
|
||||
.clear (1'b0 ),
|
||||
.i_tdata (cp_ins_tdata ),
|
||||
.i_tvalid(cp_ins_tvalid ),
|
||||
.i_tready(cp_ins_tready ),
|
||||
.o_tdata (cp_ins_fifo_tdata ),
|
||||
.o_tvalid(cp_ins_fifo_tvalid),
|
||||
.o_tready(cp_ins_fifo_tready),
|
||||
.space ( ),
|
||||
.occupied( )
|
||||
);
|
||||
end else begin : gen_no_cp_ins_fifo
|
||||
assign cp_ins_tready = 1'b1;
|
||||
assign cp_ins_fifo_tdata = '0;
|
||||
assign cp_ins_fifo_tvalid = 1'b1;
|
||||
end
|
||||
|
||||
axi_fifo #(
|
||||
.WIDTH(1+DATA_W),
|
||||
.SIZE (1 )
|
||||
) axi_fifo_fft (
|
||||
.clk (clk ),
|
||||
.reset (rst ),
|
||||
.clear (1'b0 ),
|
||||
.i_tdata ({i_tlast, i_tdata} ),
|
||||
.i_tvalid(i_tvalid ),
|
||||
.i_tready(i_tready ),
|
||||
.o_tdata ({fft_fifo_tlast, fft_fifo_tdata}),
|
||||
.o_tvalid(fft_fifo_tvalid ),
|
||||
.o_tready(fft_fifo_tready ),
|
||||
.space ( ),
|
||||
.occupied( )
|
||||
);
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Cyclic Prefix Removal
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
logic [31:0] cp_rem_out_tdata;
|
||||
logic cp_rem_out_tlast;
|
||||
logic cp_rem_out_tvalid;
|
||||
logic cp_rem_out_tready;
|
||||
|
||||
if (EN_CP_REMOVAL) begin : gen_cp_removal
|
||||
cp_removal #(
|
||||
.CP_LEN_W (CP_LEN_W ),
|
||||
.DATA_W (DATA_W )
|
||||
) cp_removal_i (
|
||||
.clk (clk ),
|
||||
.rst (rst ),
|
||||
.cp_len_tdata (cp_rem_fifo_tdata ),
|
||||
.cp_len_tvalid(cp_rem_fifo_tvalid),
|
||||
.cp_len_tready(cp_rem_fifo_tready),
|
||||
.i_tdata (fft_fifo_tdata ),
|
||||
.i_tlast (fft_fifo_tlast ),
|
||||
.i_tvalid (fft_fifo_tvalid ),
|
||||
.i_tready (fft_fifo_tready ),
|
||||
.o_tdata (cp_rem_out_tdata ),
|
||||
.o_tlast (cp_rem_out_tlast ),
|
||||
.o_tvalid (cp_rem_out_tvalid ),
|
||||
.o_tready (cp_rem_out_tready )
|
||||
);
|
||||
end else begin : gen_no_cp_removal
|
||||
assign cp_rem_out_tdata = fft_fifo_tdata;
|
||||
assign cp_rem_out_tlast = fft_fifo_tlast;
|
||||
assign cp_rem_out_tvalid = fft_fifo_tvalid;
|
||||
assign fft_fifo_tready = cp_rem_out_tready;
|
||||
end
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// XFFT Configuration Handling
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
logic [31:0] fft_in_tdata;
|
||||
logic fft_in_tlast;
|
||||
logic fft_in_tvalid;
|
||||
logic fft_in_tready;
|
||||
|
||||
logic [FFT_CONFIG_W-1:0] fft_config_core_tdata;
|
||||
logic fft_config_core_tvalid;
|
||||
logic fft_config_core_tready;
|
||||
|
||||
// Create a register that indicates the first word transfer of packet
|
||||
// (analogous to TLAST).
|
||||
logic fft_in_tfirst = 1'b1;
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) begin
|
||||
fft_in_tfirst <= 1'b1;
|
||||
end else if (fft_in_tvalid && fft_in_tready) begin
|
||||
fft_in_tfirst <= fft_in_tlast;
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
if (EN_CONFIG_FIFO) begin
|
||||
// In FIFO mode we require one configuration write for each FFT/IFFT
|
||||
// packet that is input. This mode was used when the XFFT IP handled the
|
||||
// CP insertion but was no longer needed when the CP insertion was moved
|
||||
// to the reorder block. We keep it in the design in case we want to use
|
||||
// a mode that requires this again in the future.
|
||||
|
||||
// Only pass FFT data from cp_rem_out to fft_in when the configuration
|
||||
// FIFO has a configuration for us.
|
||||
fft_in_tdata = cp_rem_out_tdata;
|
||||
fft_in_tlast = cp_rem_out_tlast;
|
||||
fft_in_tvalid = cp_rem_out_tvalid && fft_config_fifo_tvalid;
|
||||
cp_rem_out_tready = fft_in_tready && fft_config_fifo_tvalid;
|
||||
|
||||
// Pass configuration from the fft_config_fifo to fft_config_core. Write
|
||||
// the configuration when the first sample is input into the FFT core and
|
||||
// pop the configuration off the configuration FIFO when the last sample
|
||||
// is input into the FFT core.
|
||||
fft_config_core_tdata = fft_config_fifo_tdata;
|
||||
fft_config_core_tvalid = fft_in_tvalid && fft_in_tready && fft_in_tfirst;
|
||||
fft_config_fifo_tready = fft_in_tvalid && fft_in_tready && fft_in_tlast;
|
||||
|
||||
end else begin
|
||||
// In non-FIFO mode we use whatever configuration value is on the
|
||||
// fft_config input.
|
||||
|
||||
// Pass FFT data from cp_rem_out to fft_in
|
||||
fft_in_tdata = cp_rem_out_tdata;
|
||||
fft_in_tlast = cp_rem_out_tlast;
|
||||
fft_in_tvalid = cp_rem_out_tvalid;
|
||||
cp_rem_out_tready = fft_in_tready;
|
||||
|
||||
// Write the configuration when the first sample is input into the FFT
|
||||
// core.
|
||||
fft_config_core_tdata = fft_config;
|
||||
fft_config_core_tvalid = fft_in_tvalid && fft_in_tready && fft_in_tfirst;
|
||||
fft_config_fifo_tready = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
//synthesis translate_off
|
||||
always_ff @(posedge clk) begin
|
||||
if (fft_config_core_tvalid && !fft_config_core_tready) begin
|
||||
$error("FFT configuration was not accepted by the XFFT core");
|
||||
end
|
||||
end
|
||||
//synthesis translate_on
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// FFT IP Core
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
logic [31:0] fft_out_tdata;
|
||||
logic fft_out_tlast;
|
||||
logic fft_out_tvalid;
|
||||
logic fft_out_tready;
|
||||
|
||||
logic event_tlast_unexpected;
|
||||
logic event_tlast_missing;
|
||||
|
||||
xfft_wrapper #(
|
||||
.MAX_FFT_SIZE_LOG2(MAX_FFT_SIZE_LOG2)
|
||||
) xfft_wrapper_i (
|
||||
.aclk (clk ),
|
||||
.aresetn (~rst ),
|
||||
.s_axis_config_tdata (fft_config_core_tdata ),
|
||||
.s_axis_config_tvalid (fft_config_core_tvalid ),
|
||||
.s_axis_config_tready (fft_config_core_tready ),
|
||||
.s_axis_data_tdata ({ fft_in_tdata[15:0], fft_in_tdata[31:16] } ),
|
||||
.s_axis_data_tlast (fft_in_tlast ),
|
||||
.s_axis_data_tvalid (fft_in_tvalid ),
|
||||
.s_axis_data_tready (fft_in_tready ),
|
||||
.m_axis_data_tdata ({ fft_out_tdata[15:0], fft_out_tdata[31:16] }),
|
||||
.m_axis_data_tuser ( ),
|
||||
.m_axis_data_tlast (fft_out_tlast ),
|
||||
.m_axis_data_tvalid (fft_out_tvalid ),
|
||||
.m_axis_data_tready (fft_out_tready ),
|
||||
.m_axis_status_tdata ( ),
|
||||
.m_axis_status_tvalid ( ),
|
||||
.m_axis_status_tready (1'b1 ),
|
||||
.event_frame_started ( ),
|
||||
.event_tlast_unexpected (event_tlast_unexpected ),
|
||||
.event_tlast_missing (event_tlast_missing ),
|
||||
.event_fft_overflow (event_fft_overflow ),
|
||||
.event_status_channel_halt ( ),
|
||||
.event_data_in_channel_halt ( ),
|
||||
.event_data_out_channel_halt( )
|
||||
);
|
||||
|
||||
//synthesis translate_off
|
||||
always_ff @(posedge clk) begin
|
||||
// The packets are not being correctly sized if we get an unexpected or missing TLAST.
|
||||
assert (event_tlast_unexpected != 1'b1) else $error("FFT TLAST unexpected");
|
||||
assert (event_tlast_missing != 1'b1) else $error("FFT TLAST missing");
|
||||
// Overflow can occur depending on the scaling settings and input data.
|
||||
assert (event_fft_overflow != 1'b1) else $warning("FFT overflow");
|
||||
end
|
||||
//synthesis translate_on
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Magnitude and Data Order Post-Processing
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
if (EN_FFT_ORDER || EN_MAGNITUDE || EN_MAGNITUDE_SQ) begin : gen_fft_post_processing
|
||||
logic [ DATA_W-1:0] pp_in_tdata;
|
||||
logic [CP_LEN_W-1:0] pp_in_tuser;
|
||||
logic pp_in_tlast;
|
||||
logic pp_in_tvalid;
|
||||
logic pp_in_tready;
|
||||
|
||||
// Only transfer data when both the data and CP FIFOs have their data
|
||||
// available.
|
||||
assign pp_in_tdata = fft_out_tdata;
|
||||
assign pp_in_tuser = cp_ins_fifo_tdata;
|
||||
assign pp_in_tlast = fft_out_tlast;
|
||||
assign pp_in_tvalid = fft_out_tvalid && cp_ins_fifo_tvalid;
|
||||
assign fft_out_tready = pp_in_tready && cp_ins_fifo_tvalid;
|
||||
|
||||
// Pop the CP off the FIFO at the end of the packet
|
||||
assign cp_ins_fifo_tready = pp_in_tready && pp_in_tvalid && pp_in_tlast;
|
||||
|
||||
fft_post_processing #(
|
||||
.EN_FFT_ORDER (EN_FFT_ORDER ),
|
||||
.EN_CP_INSERTION (EN_CP_INSERTION ),
|
||||
.EN_MAGNITUDE (EN_MAGNITUDE ),
|
||||
.EN_MAGNITUDE_SQ (EN_MAGNITUDE_SQ ),
|
||||
.USE_APPROX_MAG (USE_APPROX_MAG ),
|
||||
.MAX_FFT_SIZE_LOG2(MAX_FFT_SIZE_LOG2)
|
||||
) fft_post_processing_i (
|
||||
.clk (clk ),
|
||||
.rst (rst ),
|
||||
.fft_order_sel(fft_order ),
|
||||
.magnitude_sel(magnitude ),
|
||||
.fft_size_log2(fft_size_log2),
|
||||
.s_axis_tdata (pp_in_tdata ),
|
||||
.s_axis_tuser (pp_in_tuser ),
|
||||
.s_axis_tlast (pp_in_tlast ),
|
||||
.s_axis_tvalid(pp_in_tvalid ),
|
||||
.s_axis_tready(pp_in_tready ),
|
||||
.m_axis_tdata (o_tdata ),
|
||||
.m_axis_tlast (o_tlast ),
|
||||
.m_axis_tvalid(o_tvalid ),
|
||||
.m_axis_tready(o_tready )
|
||||
);
|
||||
end else begin : gen_no_fft_post_processing
|
||||
assign cp_ins_fifo_tready = 1'b1;
|
||||
assign o_tdata = fft_out_tdata;
|
||||
assign o_tlast = fft_out_tlast;
|
||||
assign o_tvalid = fft_out_tvalid;
|
||||
assign fft_out_tready = o_tready;
|
||||
end
|
||||
|
||||
endmodule : fft_pipeline
|
||||
|
||||
|
||||
`default_nettype wire
|
||||
@@ -0,0 +1,310 @@
|
||||
//
|
||||
// Copyright 2024 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: fft_pipeline_wrapper
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// This module takes a multiple-item-per-cycle data stream and splits the
|
||||
// processing of packets across multiple FFT instances, each processing a
|
||||
// single item/sample per cycle.
|
||||
//
|
||||
// See fft_pipeline for documentation of the fft_pipeline parameters and
|
||||
// ports. To avoid duplication, it is not repeated here.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// NIPC : Items/samples per cycle on the input/output data port (i_t*, o_t*d)
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
|
||||
module fft_pipeline_wrapper
|
||||
import xfft_config_pkg::*;
|
||||
#(
|
||||
int NIPC = 1,
|
||||
|
||||
// Parameters for fft_pipeline
|
||||
int MAX_FFT_SIZE_LOG2 = 12,
|
||||
bit EN_CONFIG_FIFO = 0,
|
||||
bit EN_CP_REMOVAL = 1,
|
||||
bit EN_CP_INSERTION = 1,
|
||||
bit EN_FFT_ORDER = 1,
|
||||
bit EN_MAGNITUDE = 1,
|
||||
bit EN_MAGNITUDE_SQ = 1,
|
||||
bit USE_APPROX_MAG = 1,
|
||||
|
||||
localparam int FFT_CONFIG_W = fft_config_w(MAX_FFT_SIZE_LOG2),
|
||||
localparam int DATA_W = 32,
|
||||
localparam int FFT_SIZE_LOG2_W = $clog2(MAX_FFT_SIZE_LOG2+1),
|
||||
localparam int CP_LEN_W = MAX_FFT_SIZE_LOG2
|
||||
) (
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
// Global FFT settings
|
||||
input wire [ 1:0] fft_order,
|
||||
input wire [ 1:0] magnitude,
|
||||
input wire [FFT_SIZE_LOG2_W-1:0] fft_size_log2,
|
||||
input wire [ FFT_CONFIG_W-1:0] fft_config,
|
||||
|
||||
// FFT Configuration
|
||||
input wire [FFT_CONFIG_W-1:0] fft_config_tdata,
|
||||
input wire fft_config_tvalid,
|
||||
output wire fft_config_tready,
|
||||
|
||||
// CP Removal Length
|
||||
input wire [CP_LEN_W-1:0] cp_rem_tdata,
|
||||
input wire cp_rem_tvalid,
|
||||
output wire cp_rem_tready,
|
||||
|
||||
// CP Insertion Length
|
||||
input wire [CP_LEN_W-1:0] cp_ins_tdata,
|
||||
input wire cp_ins_tvalid,
|
||||
output reg cp_ins_tready,
|
||||
|
||||
// FFT Event Monitoring
|
||||
output wire event_fft_overflow,
|
||||
|
||||
// Data Input Packets
|
||||
input wire [NIPC*DATA_W-1:0] i_tdata,
|
||||
input wire i_tlast,
|
||||
input wire i_tvalid,
|
||||
output wire i_tready,
|
||||
|
||||
// Data Output Packets
|
||||
output wire [NIPC*DATA_W-1:0] o_tdata,
|
||||
output wire o_tlast,
|
||||
output wire o_tvalid,
|
||||
input wire o_tready
|
||||
);
|
||||
|
||||
`include "usrp_utils.svh"
|
||||
|
||||
if (NIPC == 1) begin : gen_one_spc
|
||||
|
||||
fft_pipeline #(
|
||||
.MAX_FFT_SIZE_LOG2(MAX_FFT_SIZE_LOG2),
|
||||
.EN_CONFIG_FIFO (EN_CONFIG_FIFO ),
|
||||
.EN_CP_REMOVAL (EN_CP_REMOVAL ),
|
||||
.EN_CP_INSERTION (EN_CP_INSERTION ),
|
||||
.EN_FFT_ORDER (EN_FFT_ORDER ),
|
||||
.EN_MAGNITUDE (EN_MAGNITUDE ),
|
||||
.EN_MAGNITUDE_SQ (EN_MAGNITUDE_SQ ),
|
||||
.USE_APPROX_MAG (USE_APPROX_MAG )
|
||||
) fft_pipeline_i (
|
||||
.clk (clk ),
|
||||
.rst (rst ),
|
||||
.fft_order (fft_order ),
|
||||
.magnitude (magnitude ),
|
||||
.fft_config (fft_config ),
|
||||
.fft_size_log2 (fft_size_log2 ),
|
||||
.fft_config_tdata (fft_config_tdata ),
|
||||
.fft_config_tvalid (fft_config_tvalid ),
|
||||
.fft_config_tready (fft_config_tready ),
|
||||
.cp_rem_tdata (cp_rem_tdata ),
|
||||
.cp_rem_tvalid (cp_rem_tvalid ),
|
||||
.cp_rem_tready (cp_rem_tready ),
|
||||
.cp_ins_tdata (cp_ins_tdata ),
|
||||
.cp_ins_tvalid (cp_ins_tvalid ),
|
||||
.cp_ins_tready (cp_ins_tready ),
|
||||
.event_fft_overflow(event_fft_overflow),
|
||||
.i_tdata (i_tdata ),
|
||||
.i_tlast (i_tlast ),
|
||||
.i_tvalid (i_tvalid ),
|
||||
.i_tready (i_tready ),
|
||||
.o_tdata (o_tdata ),
|
||||
.o_tlast (o_tlast ),
|
||||
.o_tvalid (o_tvalid ),
|
||||
.o_tready (o_tready )
|
||||
);
|
||||
|
||||
end else begin : gen_multi_spc
|
||||
|
||||
logic [FFT_CONFIG_W-1:0] fft_config_split_tdata [NIPC];
|
||||
logic fft_config_split_tvalid[NIPC];
|
||||
logic fft_config_split_tready[NIPC];
|
||||
|
||||
logic [CP_LEN_W-1:0] cp_rem_split_tdata [NIPC];
|
||||
logic cp_rem_split_tvalid [NIPC];
|
||||
logic cp_rem_split_tready [NIPC];
|
||||
|
||||
logic [CP_LEN_W-1:0] cp_ins_split_tdata [NIPC];
|
||||
logic cp_ins_split_tvalid [NIPC];
|
||||
logic cp_ins_split_tready [NIPC];
|
||||
|
||||
logic [DATA_W-1:0] i_split_tdata [NIPC];
|
||||
logic i_split_tlast [NIPC];
|
||||
logic i_split_tvalid [NIPC];
|
||||
logic i_split_tready [NIPC];
|
||||
|
||||
logic [DATA_W-1:0] o_split_tdata [NIPC];
|
||||
logic o_split_tlast [NIPC];
|
||||
logic o_split_tvalid [NIPC];
|
||||
logic o_split_tready [NIPC];
|
||||
|
||||
logic [NIPC-1:0] event_fft_overflow_split;
|
||||
|
||||
if (EN_CONFIG_FIFO) begin : gen_config_fifo_split
|
||||
axis_load_split #(
|
||||
.IN_DATA_W (FFT_CONFIG_W),
|
||||
.IN_FIFO_SIZE (1 ),
|
||||
.OUT_DATA_W (FFT_CONFIG_W),
|
||||
.OUT_FIFO_SIZE(1 ),
|
||||
.OUT_NUM_PORTS(NIPC )
|
||||
) axis_load_split_fft_config (
|
||||
.clk (clk ),
|
||||
.rst (rst ),
|
||||
.i_tdata (fft_config_tdata ),
|
||||
.i_tuser ('0 ),
|
||||
.i_tlast (1'b1 ),
|
||||
.i_tvalid(fft_config_tvalid ),
|
||||
.i_tready(fft_config_tready ),
|
||||
.o_tdata (fft_config_split_tdata ),
|
||||
.o_tuser ( ),
|
||||
.o_tlast ( ),
|
||||
.o_tvalid(fft_config_split_tvalid),
|
||||
.o_tready(fft_config_split_tready)
|
||||
);
|
||||
end else begin : gen_no_config_fifo_split
|
||||
assign fft_config_tready = 1'b1;
|
||||
for (genvar idx=0; idx < NIPC; idx++) begin : gen_assign
|
||||
assign fft_config_split_tvalid[idx] = 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
axis_load_split #(
|
||||
.IN_DATA_W (CP_LEN_W),
|
||||
.IN_FIFO_SIZE (1 ),
|
||||
.OUT_DATA_W (CP_LEN_W),
|
||||
.OUT_FIFO_SIZE(1 ),
|
||||
.OUT_NUM_PORTS(NIPC )
|
||||
) axis_load_split_cp_rem (
|
||||
.clk (clk ),
|
||||
.rst (rst ),
|
||||
.i_tdata (cp_rem_tdata ),
|
||||
.i_tuser ('0 ),
|
||||
.i_tlast (1'b1 ),
|
||||
.i_tvalid(cp_rem_tvalid ),
|
||||
.i_tready(cp_rem_tready ),
|
||||
.o_tdata (cp_rem_split_tdata ),
|
||||
.o_tuser ( ),
|
||||
.o_tlast ( ),
|
||||
.o_tvalid(cp_rem_split_tvalid),
|
||||
.o_tready(cp_rem_split_tready)
|
||||
);
|
||||
|
||||
axis_load_split #(
|
||||
.IN_DATA_W (CP_LEN_W),
|
||||
.IN_FIFO_SIZE (1 ),
|
||||
.OUT_DATA_W (CP_LEN_W),
|
||||
.OUT_FIFO_SIZE(1 ),
|
||||
.OUT_NUM_PORTS(NIPC )
|
||||
) axis_load_split_cp_ins (
|
||||
.clk (clk ),
|
||||
.rst (rst ),
|
||||
.i_tdata (cp_ins_tdata ),
|
||||
.i_tuser ('0 ),
|
||||
.i_tlast (1'b1 ),
|
||||
.i_tvalid(cp_ins_tvalid ),
|
||||
.i_tready(cp_ins_tready ),
|
||||
.o_tdata (cp_ins_split_tdata ),
|
||||
.o_tuser ( ),
|
||||
.o_tlast ( ),
|
||||
.o_tvalid(cp_ins_split_tvalid),
|
||||
.o_tready(cp_ins_split_tready)
|
||||
);
|
||||
|
||||
axis_load_split #(
|
||||
.IN_DATA_W (NIPC*DATA_W ),
|
||||
.IN_FIFO_SIZE (1 ),
|
||||
.OUT_DATA_W (DATA_W ),
|
||||
.OUT_FIFO_SIZE(MAX_FFT_SIZE_LOG2),
|
||||
.OUT_NUM_PORTS(NIPC ),
|
||||
.USER_W (1 )
|
||||
) axis_load_split_data (
|
||||
.clk (clk ),
|
||||
.rst (rst ),
|
||||
.i_tdata (i_tdata ),
|
||||
.i_tuser ('0 ),
|
||||
.i_tlast (i_tlast ),
|
||||
.i_tvalid(i_tvalid ),
|
||||
.i_tready(i_tready ),
|
||||
.o_tdata (i_split_tdata ),
|
||||
.o_tuser ( ),
|
||||
.o_tlast (i_split_tlast ),
|
||||
.o_tvalid(i_split_tvalid),
|
||||
.o_tready(i_split_tready)
|
||||
);
|
||||
|
||||
for (genvar samp_i = 0; samp_i < NIPC; samp_i++) begin : gen_pipelines
|
||||
fft_pipeline #(
|
||||
.MAX_FFT_SIZE_LOG2(MAX_FFT_SIZE_LOG2),
|
||||
.EN_CONFIG_FIFO (EN_CONFIG_FIFO ),
|
||||
.EN_CP_REMOVAL (EN_CP_REMOVAL ),
|
||||
.EN_CP_INSERTION (EN_CP_INSERTION ),
|
||||
.EN_FFT_ORDER (EN_FFT_ORDER ),
|
||||
.EN_MAGNITUDE (EN_MAGNITUDE ),
|
||||
.EN_MAGNITUDE_SQ (EN_MAGNITUDE_SQ ),
|
||||
.USE_APPROX_MAG (USE_APPROX_MAG )
|
||||
) fft_pipeline_i (
|
||||
.clk (clk ),
|
||||
.rst (rst ),
|
||||
.fft_order (fft_order ),
|
||||
.magnitude (magnitude ),
|
||||
.fft_size_log2 (fft_size_log2 ),
|
||||
.fft_config (fft_config ),
|
||||
.fft_config_tdata (fft_config_split_tdata [samp_i]),
|
||||
.fft_config_tvalid (fft_config_split_tvalid [samp_i]),
|
||||
.fft_config_tready (fft_config_split_tready [samp_i]),
|
||||
.cp_rem_tdata (cp_rem_split_tdata [samp_i]),
|
||||
.cp_rem_tvalid (cp_rem_split_tvalid [samp_i]),
|
||||
.cp_rem_tready (cp_rem_split_tready [samp_i]),
|
||||
.cp_ins_tdata (cp_ins_split_tdata [samp_i]),
|
||||
.cp_ins_tvalid (cp_ins_split_tvalid [samp_i]),
|
||||
.cp_ins_tready (cp_ins_split_tready [samp_i]),
|
||||
.event_fft_overflow(event_fft_overflow_split[samp_i]),
|
||||
.i_tdata (i_split_tdata [samp_i]),
|
||||
.i_tlast (i_split_tlast [samp_i]),
|
||||
.i_tvalid (i_split_tvalid [samp_i]),
|
||||
.i_tready (i_split_tready [samp_i]),
|
||||
.o_tdata (o_split_tdata [samp_i]),
|
||||
.o_tlast (o_split_tlast [samp_i]),
|
||||
.o_tvalid (o_split_tvalid [samp_i]),
|
||||
.o_tready (o_split_tready [samp_i])
|
||||
);
|
||||
end
|
||||
|
||||
axis_load_merge #(
|
||||
.IN_DATA_W (DATA_W ),
|
||||
.IN_FIFO_SIZE (MAX_FFT_SIZE_LOG2),
|
||||
.IN_NUM_PORTS (NIPC ),
|
||||
.OUT_DATA_W (NIPC*DATA_W ),
|
||||
.OUT_FIFO_SIZE(1 ),
|
||||
.USER_W (1 )
|
||||
) axis_load_merge_data (
|
||||
.clk (clk ),
|
||||
.rst (rst ),
|
||||
.i_tdata (o_split_tdata ),
|
||||
.i_tuser ( ),
|
||||
.i_tlast (o_split_tlast ),
|
||||
.i_tvalid(o_split_tvalid),
|
||||
.i_tready(o_split_tready),
|
||||
.o_tdata (o_tdata ),
|
||||
.o_tuser ( ),
|
||||
.o_tlast (o_tlast ),
|
||||
.o_tvalid(o_tvalid ),
|
||||
.o_tready(o_tready )
|
||||
);
|
||||
|
||||
assign event_fft_overflow = |event_fft_overflow_split;
|
||||
|
||||
end
|
||||
|
||||
endmodule : fft_pipeline_wrapper
|
||||
|
||||
|
||||
`default_nettype wire
|
||||
@@ -350,7 +350,7 @@ module fft_post_processing #(
|
||||
.WIDTH (32),
|
||||
.SIZE (3),
|
||||
.PRE_FIFO_SIZE (0),
|
||||
.POST_FIFO_SIZE(0)
|
||||
.POST_FIFO_SIZE(1)
|
||||
) axi_demux_i (
|
||||
.clk (clk),
|
||||
.reset (rst),
|
||||
|
||||
@@ -393,8 +393,7 @@ module fft_reorder
|
||||
if (EN_CP_INSERTION) begin: gen_cp_ins_fifo
|
||||
logic [CP_LEN_W-1:0] cp_len_tdata;
|
||||
logic cp_len_tvalid;
|
||||
logic cp_len_tready;
|
||||
logic i_tvalid;
|
||||
logic tmp_i_tvalid;
|
||||
logic in_fifo_o_tfirst = '1; // First transfer of packet
|
||||
|
||||
// Create a register that indicates when the next transfer is the start of
|
||||
@@ -410,7 +409,7 @@ module fft_reorder
|
||||
end
|
||||
|
||||
// Write the first tuser word of the packet into the CP length FIFO
|
||||
assign i_tvalid = in_fifo_o_tvalid && in_fifo_o_tready && in_fifo_o_tfirst;
|
||||
assign tmp_i_tvalid = in_fifo_o_tvalid && in_fifo_o_tready && in_fifo_o_tfirst;
|
||||
|
||||
// The dual RAM buffer can only hold two FFTs at a time, so we can
|
||||
// guarantee this FIFO has sufficient room and will always be ready by
|
||||
@@ -423,7 +422,7 @@ module fft_reorder
|
||||
.reset (rst),
|
||||
.clear ('0),
|
||||
.i_tdata (in_fifo_o_tuser),
|
||||
.i_tvalid(i_tvalid),
|
||||
.i_tvalid(tmp_i_tvalid),
|
||||
.i_tready(),
|
||||
.o_tdata (cp_len_tdata),
|
||||
.o_tvalid(cp_len_tvalid),
|
||||
|
||||
@@ -272,9 +272,9 @@ module noc_shell_fft #(
|
||||
.NIPC (NIPC),
|
||||
.SYNC_CLKS (0),
|
||||
.INFO_FIFO_SIZE ($clog2(32)),
|
||||
.PYLD_FIFO_SIZE ($clog2(2**MTU)),
|
||||
.PYLD_FIFO_SIZE ($clog2(32)),
|
||||
.MTU (MTU),
|
||||
.SIDEBAND_AT_END (1)
|
||||
.SIDEBAND_AT_END (0)
|
||||
) axis_data_to_chdr_out_out (
|
||||
.axis_chdr_clk (rfnoc_chdr_clk),
|
||||
.axis_chdr_rst (rfnoc_chdr_rst),
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
// THIS_PORTID : Control crossbar port to which this block is connected
|
||||
// CHDR_W : AXIS-CHDR data bus width
|
||||
// MTU : Log2 of maximum transmission unit
|
||||
// NIPC : Number of samples/items per clock cycle to
|
||||
// process internally.
|
||||
// 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
|
||||
@@ -25,6 +27,11 @@
|
||||
// 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.
|
||||
// EN_CP_INSERTION : Controls whether to include the cyclic prefix
|
||||
// insertion logic. If included, EN_FFT_ORDER must
|
||||
// be 1.
|
||||
// EN_CP_REMOVAL : Controls whether to include the cyclic prefix
|
||||
// removal logic.
|
||||
// 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
|
||||
@@ -54,16 +61,19 @@ module rfnoc_block_fft #(
|
||||
logic [9:0] THIS_PORTID = 10'd0,
|
||||
int CHDR_W = 64,
|
||||
logic [5:0] MTU = 6'd10,
|
||||
int NIPC = 1,
|
||||
int NUM_PORTS = 1,
|
||||
int NUM_CORES = 1,
|
||||
int MAX_FFT_SIZE_LOG2 = 12,
|
||||
int MAX_FFT_SIZE_LOG2 = 10,
|
||||
bit EN_CP_REMOVAL = 1,
|
||||
bit EN_CP_INSERTION = 1,
|
||||
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_BYPASS = 0,
|
||||
bit EN_FFT_ORDER = 1,
|
||||
bit EN_MAGNITUDE = 1,
|
||||
bit EN_MAGNITUDE = 0,
|
||||
bit EN_MAGNITUDE_SQ = 1,
|
||||
bit USE_APPROX_MAG = 1
|
||||
) (
|
||||
@@ -109,6 +119,14 @@ module rfnoc_block_fft #(
|
||||
|
||||
localparam ITEM_W = 32;
|
||||
|
||||
// Calculate the number of channels per core
|
||||
localparam int NCPC = NUM_PORTS / NUM_CORES;
|
||||
|
||||
// We require each FFT core instance to have the same number of channels
|
||||
if (NUM_CORES * NCPC != NUM_PORTS) begin : check_num_ports_per_core
|
||||
$error("NUM_PORTS must be a multiple of NUM_CORES");
|
||||
end
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Signal Declarations
|
||||
@@ -124,27 +142,27 @@ module rfnoc_block_fft #(
|
||||
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 [NUM_CORES-1:0][NCPC-1:0][ ITEM_W*NIPC-1:0] in_axis_tdata;
|
||||
logic [NUM_CORES-1:0][NCPC-1:0][ NIPC-1:0] in_axis_tkeep;
|
||||
logic [NUM_CORES-1:0][NCPC-1:0][ 0:0] in_axis_tlast;
|
||||
logic [NUM_CORES-1:0][NCPC-1:0][ 0:0] in_axis_tvalid;
|
||||
logic [NUM_CORES-1:0][NCPC-1:0][ 0:0] in_axis_tready;
|
||||
logic [NUM_CORES-1:0][NCPC-1:0][CHDR_TIMESTAMP_W-1:0] in_axis_ttimestamp;
|
||||
logic [NUM_CORES-1:0][NCPC-1:0][ 0:0] in_axis_thas_time;
|
||||
logic [NUM_CORES-1:0][NCPC-1:0][ CHDR_LENGTH_W-1:0] in_axis_tlength;
|
||||
logic [NUM_CORES-1:0][NCPC-1:0][ 0:0] in_axis_teov;
|
||||
logic [NUM_CORES-1:0][NCPC-1:0][ 0: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;
|
||||
logic [NUM_CORES-1:0][NCPC-1:0][ ITEM_W*NIPC-1:0] out_axis_tdata;
|
||||
logic [NUM_CORES-1:0][NCPC-1:0][ NIPC-1:0] out_axis_tkeep;
|
||||
logic [NUM_CORES-1:0][NCPC-1:0][ 0:0] out_axis_tlast;
|
||||
logic [NUM_CORES-1:0][NCPC-1:0][ 0:0] out_axis_tvalid;
|
||||
logic [NUM_CORES-1:0][NCPC-1:0][ 0:0] out_axis_tready;
|
||||
logic [NUM_CORES-1:0][NCPC-1:0][CHDR_TIMESTAMP_W-1:0] out_axis_ttimestamp;
|
||||
logic [NUM_CORES-1:0][NCPC-1:0][ 0:0] out_axis_thas_time;
|
||||
logic [NUM_CORES-1:0][NCPC-1:0][ CHDR_LENGTH_W-1:0] out_axis_tlength;
|
||||
logic [NUM_CORES-1:0][NCPC-1:0][ 0:0] out_axis_teov;
|
||||
logic [NUM_CORES-1:0][NCPC-1:0][ 0:0] out_axis_teob;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@@ -155,7 +173,9 @@ module rfnoc_block_fft #(
|
||||
.CHDR_W (CHDR_W),
|
||||
.THIS_PORTID(THIS_PORTID),
|
||||
.MTU (MTU),
|
||||
.NUM_PORTS (NUM_PORTS)
|
||||
.NUM_PORTS (NUM_PORTS),
|
||||
.NIPC (NIPC),
|
||||
.ITEM_W (ITEM_W)
|
||||
) noc_shell_fft_i (
|
||||
//---------------------
|
||||
// Framework Interface
|
||||
@@ -236,12 +256,12 @@ module rfnoc_block_fft #(
|
||||
// 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;
|
||||
logic [NUM_CORES-1:0][ 0:0] dec_ctrlport_req_wr;
|
||||
logic [NUM_CORES-1:0][ 0:0] dec_ctrlport_req_rd;
|
||||
logic [NUM_CORES-1:0][CTRLPORT_ADDR_W-1:0] dec_ctrlport_req_addr;
|
||||
logic [NUM_CORES-1:0][CTRLPORT_DATA_W-1:0] dec_ctrlport_req_data;
|
||||
logic [NUM_CORES-1:0][ 0:0] dec_ctrlport_resp_ack;
|
||||
logic [NUM_CORES-1:0][CTRLPORT_DATA_W-1:0] dec_ctrlport_resp_data;
|
||||
|
||||
generate
|
||||
if (NUM_CORES > 1) begin : gen_ctrlport_decoder
|
||||
@@ -276,8 +296,7 @@ module rfnoc_block_fft #(
|
||||
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_addr = CTRLPORT_ADDR_W'(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;
|
||||
@@ -289,59 +308,56 @@ module rfnoc_block_fft #(
|
||||
// FFT Core
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// Calculate the number of ports per core
|
||||
localparam int NPPC = NUM_PORTS / NUM_CORES;
|
||||
// Convert CHDR MTU to packet size in items
|
||||
localparam int MAX_PKT_SIZE_LOG2 = $clog2(2**MTU * CHDR_W/ITEM_W);
|
||||
|
||||
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
|
||||
for (genvar 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),
|
||||
.NIPC (NIPC ),
|
||||
.NUM_CHAN (NCPC ),
|
||||
.NUM_CORES (NUM_CORES ),
|
||||
.MAX_PKT_SIZE_LOG2 (MAX_PKT_SIZE_LOG2 ),
|
||||
.MAX_FFT_SIZE_LOG2 (MAX_FFT_SIZE_LOG2 ),
|
||||
.EN_CP_REMOVAL (EN_CP_REMOVAL ),
|
||||
.EN_CP_INSERTION (EN_CP_INSERTION ),
|
||||
.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)
|
||||
.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))
|
||||
.s_ctrlport_req_wr (dec_ctrlport_req_wr [core_i]),
|
||||
.s_ctrlport_req_rd (dec_ctrlport_req_rd [core_i]),
|
||||
.s_ctrlport_req_addr (dec_ctrlport_req_addr [core_i]),
|
||||
.s_ctrlport_req_data (dec_ctrlport_req_data [core_i]),
|
||||
.s_ctrlport_resp_ack (dec_ctrlport_resp_ack [core_i]),
|
||||
.s_ctrlport_resp_data (dec_ctrlport_resp_data[core_i]),
|
||||
.s_in_axis_tdata (in_axis_tdata [core_i]),
|
||||
.s_in_axis_tkeep (in_axis_tkeep [core_i]),
|
||||
.s_in_axis_tlast (in_axis_tlast [core_i]),
|
||||
.s_in_axis_tvalid (in_axis_tvalid [core_i]),
|
||||
.s_in_axis_tready (in_axis_tready [core_i]),
|
||||
.s_in_axis_ttimestamp (in_axis_ttimestamp [core_i]),
|
||||
.s_in_axis_thas_time (in_axis_thas_time [core_i]),
|
||||
.s_in_axis_tlength (in_axis_tlength [core_i]),
|
||||
.s_in_axis_teov (in_axis_teov [core_i]),
|
||||
.s_in_axis_teob (in_axis_teob [core_i]),
|
||||
.m_out_axis_tdata (out_axis_tdata [core_i]),
|
||||
.m_out_axis_tkeep (out_axis_tkeep [core_i]),
|
||||
.m_out_axis_tlast (out_axis_tlast [core_i]),
|
||||
.m_out_axis_tvalid (out_axis_tvalid [core_i]),
|
||||
.m_out_axis_tready (out_axis_tready [core_i]),
|
||||
.m_out_axis_ttimestamp(out_axis_ttimestamp [core_i]),
|
||||
.m_out_axis_thas_time (out_axis_thas_time [core_i]),
|
||||
.m_out_axis_tlength (out_axis_tlength [core_i]),
|
||||
.m_out_axis_teov (out_axis_teov [core_i]),
|
||||
.m_out_axis_teob (out_axis_teob [core_i])
|
||||
);
|
||||
end : gen_fft_cores
|
||||
|
||||
|
||||
@@ -18,35 +18,89 @@ module rfnoc_block_fft_all_tb;
|
||||
// Test Configurations
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// Basic tests of multi-ports configurations
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .NUM_PORTS(2), .NUM_CORES(1), .MAX_FFT_SIZE_LOG2(10)) tb_0a ();
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .NUM_PORTS(2), .NUM_CORES(2), .MAX_FFT_SIZE_LOG2(10)) tb_0b ();
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .NUM_PORTS(4), .NUM_CORES(2), .MAX_FFT_SIZE_LOG2(10)) tb_0c ();
|
||||
// Basic tests of multi-port configurations
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .NUM_PORTS(2), .NUM_CORES(1),
|
||||
.MAX_FFT_SIZE_LOG2(10)) tb_0a ();
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .NUM_PORTS(2), .NUM_CORES(2),
|
||||
.MAX_FFT_SIZE_LOG2(10)) tb_0b ();
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .NUM_PORTS(4), .NUM_CORES(2),
|
||||
.MAX_FFT_SIZE_LOG2(10)) tb_0c ();
|
||||
|
||||
// Basic tests of other FFT sizes
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .NUM_PORTS(1), .NUM_CORES(1), .MAX_FFT_SIZE_LOG2(11)) tb_1a ();
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .NUM_PORTS(1), .NUM_CORES(1), .MAX_FFT_SIZE_LOG2(12)) tb_1b ();
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .NUM_PORTS(1), .NUM_CORES(1), .MAX_FFT_SIZE_LOG2(13)) tb_1c ();
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .NUM_PORTS(1), .NUM_CORES(1), .MAX_FFT_SIZE_LOG2(14)) tb_1d ();
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .NUM_PORTS(1), .NUM_CORES(1), .MAX_FFT_SIZE_LOG2(15)) tb_1e ();
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .NUM_PORTS(1), .NUM_CORES(1), .MAX_FFT_SIZE_LOG2(16)) tb_1f ();
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .MAX_FFT_SIZE_LOG2(11)) tb_1a ();
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .MAX_FFT_SIZE_LOG2(12)) tb_1b ();
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .MAX_FFT_SIZE_LOG2(13)) tb_1c ();
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .MAX_FFT_SIZE_LOG2(14)) tb_1d ();
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .MAX_FFT_SIZE_LOG2(15)) tb_1e ();
|
||||
// Skip the 64k FFT test because it's broken :-(
|
||||
//rfnoc_block_fft_tb #(.FULL_TEST(0), .MAX_FFT_SIZE_LOG2(16)) tb_1f ();
|
||||
|
||||
// Test case where USE_APPROX_MAG = 1
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .NUM_PORTS(1), .NUM_CORES(1), .MAX_FFT_SIZE_LOG2(10),
|
||||
.EN_FFT_BYPASS(1), .EN_MAGNITUDE(1), .USE_APPROX_MAG(1)) tb_2a ();
|
||||
// Do quick tests with various features disabled to ensure these features get
|
||||
// disabled and bypassed correctly. The cyclic prefix logic, the magnitude
|
||||
// and order logic, and the FFT bypass are in separate components. To avoid
|
||||
// testing every possible permutation, we permute each of these separately.
|
||||
//
|
||||
// Test permutations of CP insertion/removal with other features disabled
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .EN_CP_REMOVAL(0), .EN_CP_INSERTION(1), .EN_MAGNITUDE(0),
|
||||
.EN_MAGNITUDE_SQ(0), .EN_FFT_ORDER(1), .EN_FFT_BYPASS(0)) tb_2c ();
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .EN_CP_REMOVAL(1), .EN_CP_INSERTION(0), .EN_MAGNITUDE(0),
|
||||
.EN_MAGNITUDE_SQ(0), .EN_FFT_ORDER(0), .EN_FFT_BYPASS(1)) tb_2b ();
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .EN_CP_REMOVAL(1), .EN_CP_INSERTION(1), .EN_MAGNITUDE(0),
|
||||
.EN_MAGNITUDE_SQ(0), .EN_FFT_ORDER(1), .EN_FFT_BYPASS(0)) tb_2d ();
|
||||
//
|
||||
// Test permutations of magnitude with FFT bypass enabled
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .EN_CP_REMOVAL(0), .EN_CP_INSERTION(0), .EN_MAGNITUDE(0),
|
||||
.EN_MAGNITUDE_SQ(1), .EN_FFT_ORDER(0), .EN_FFT_BYPASS(1)) tb_2g ();
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .EN_CP_REMOVAL(0), .EN_CP_INSERTION(0), .EN_MAGNITUDE(1),
|
||||
.EN_MAGNITUDE_SQ(0), .EN_FFT_ORDER(0), .EN_FFT_BYPASS(0)) tb_2h ();
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .EN_CP_REMOVAL(0), .EN_CP_INSERTION(0), .EN_MAGNITUDE(1),
|
||||
.EN_MAGNITUDE_SQ(1), .EN_FFT_ORDER(0), .EN_FFT_BYPASS(1)) tb_2i ();
|
||||
|
||||
// Run full suite of tests on 1k FFT configuration
|
||||
// Test SPC of 1, 2, 4, 8 with all features enabled
|
||||
for (genvar log_spc = 0; log_spc < 3; log_spc++) begin : gen_test_multi_spc
|
||||
localparam int SPC = 2**log_spc;
|
||||
rfnoc_block_fft_tb #(
|
||||
.FULL_TEST (1 ),
|
||||
.CHDR_W (64*SPC),
|
||||
.NIPC (SPC ),
|
||||
.NUM_PORTS (1 ),
|
||||
.NUM_CORES (1 ),
|
||||
.MAX_FFT_SIZE_LOG2 (10),
|
||||
.MAX_FFT_SIZE_LOG2 (10 ),
|
||||
.EN_CP_REMOVAL (1 ),
|
||||
.EN_CP_INSERTION (1 ),
|
||||
.MAX_CP_LIST_LEN_INS_LOG2(5 ),
|
||||
.MAX_CP_LIST_LEN_REM_LOG2(5 ),
|
||||
.EN_MAGNITUDE_SQ (1 ),
|
||||
.EN_MAGNITUDE (1 ),
|
||||
.EN_MAGNITUDE_SQ (1 ),
|
||||
.EN_FFT_BYPASS (1 ),
|
||||
.USE_APPROX_MAG (0 )
|
||||
.USE_APPROX_MAG (1 )
|
||||
) tb_3a ();
|
||||
end : gen_test_multi_spc
|
||||
|
||||
// Test SPC of 1, 2 with extra features disabled
|
||||
for (genvar log_spc = 0; log_spc < 1; log_spc++) begin : gen_test_features_disabled
|
||||
localparam int SPC = 2**log_spc;
|
||||
rfnoc_block_fft_tb #(
|
||||
.FULL_TEST (1 ),
|
||||
.CHDR_W (64*SPC),
|
||||
.NIPC (SPC ),
|
||||
.NUM_PORTS (1 ),
|
||||
.NUM_CORES (1 ),
|
||||
.MAX_FFT_SIZE_LOG2 (10 ),
|
||||
.EN_CP_REMOVAL (0 ),
|
||||
.EN_CP_INSERTION (0 ),
|
||||
.MAX_CP_LIST_LEN_INS_LOG2(5 ),
|
||||
.MAX_CP_LIST_LEN_REM_LOG2(5 ),
|
||||
.EN_MAGNITUDE (0 ),
|
||||
.EN_MAGNITUDE_SQ (0 ),
|
||||
.EN_FFT_ORDER (0 ),
|
||||
.EN_FFT_BYPASS (0 ),
|
||||
.USE_APPROX_MAG (0 )
|
||||
) tb_3b ();
|
||||
end : gen_test_features_disabled
|
||||
|
||||
// Run quick test on some other multi-SPC configurations
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .CHDR_W(128), .NIPC(4)) tb_3c ();
|
||||
rfnoc_block_fft_tb #(.FULL_TEST(0), .CHDR_W(256), .NIPC(8)) tb_3d ();
|
||||
|
||||
endmodule : rfnoc_block_fft_all_tb
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user