Files
b210-k7-fpga/lib/rfnoc/blocks/rfnoc_block_fft/cp_removal.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

236 lines
8.1 KiB
Systemverilog

//
// Copyright 2024 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: cp_removal
//
// 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))
//
// 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
//
`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
) (
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,
// 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
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] 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;
always @(posedge clk) begin
// Latch FIFO clear
if (clear_list) begin
clear_fifo_hold <= 1'b1;
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
count <= count + 1;
if (count >= cp_len_reg) begin
count <= 1;
if (symbol_len_reg > 0) begin
state <= S_SYMBOL;
end else begin
state <= S_IDLE;
end
end
end
end
S_SYMBOL : begin
if (i_tvalid & i_tready) begin
count <= count + 1;
if (count >= symbol_len_reg) begin
count <= 1;
state <= S_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;
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;
endmodule : cp_removal
`default_nettype wire