From bafa9d95453387814ef25e6b6256ba8db2df612f Mon Sep 17 00:00:00 2001 From: Martin Braun Date: Thu, 23 Jan 2020 16:10:22 -0800 Subject: Merge FPGA repository back into UHD repository MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The FPGA codebase was removed from the UHD repository in 2014 to reduce the size of the repository. However, over the last half-decade, the split between the repositories has proven more burdensome than it has been helpful. By merging the FPGA code back, it will be possible to create atomic commits that touch both FPGA and UHD codebases. Continuous integration testing is also simplified by merging the repositories, because it was previously difficult to automatically derive the correct UHD branch when testing a feature branch on the FPGA repository. This commit also updates the license files and paths therein. We are therefore merging the repositories again. Future development for FPGA code will happen in the same repository as the UHD host code and MPM code. == Original Codebase and Rebasing == The original FPGA repository will be hosted for the foreseeable future at its original local location: https://github.com/EttusResearch/fpga/ It can be used for bisecting, reference, and a more detailed history. The final commit from said repository to be merged here is 05003794e2da61cabf64dd278c45685a7abad7ec. This commit is tagged as v4.0.0.0-pre-uhd-merge. If you have changes in the FPGA repository that you want to rebase onto the UHD repository, simply run the following commands: - Create a directory to store patches (this should be an empty directory): mkdir ~/patches - Now make sure that your FPGA codebase is based on the same state as the code that was merged: cd src/fpga # Or wherever your FPGA code is stored git rebase v4.0.0.0-pre-uhd-merge Note: The rebase command may look slightly different depending on what exactly you're trying to rebase. - Create a patch set for your changes versus v4.0.0.0-pre-uhd-merge: git format-patch v4.0.0.0-pre-uhd-merge -o ~/patches Note: Make sure that only patches are stored in your output directory. It should otherwise be empty. Make sure that you picked the correct range of commits, and only commits you wanted to rebase were exported as patch files. - Go to the UHD repository and apply the patches: cd src/uhd # Or wherever your UHD repository is stored git am --directory fpga ~/patches/* rm -rf ~/patches # This is for cleanup == Contributors == The following people have contributed mainly to these files (this list is not complete): Co-authored-by: Alex Williams Co-authored-by: Andrej Rode Co-authored-by: Ashish Chaudhari Co-authored-by: Ben Hilburn Co-authored-by: Ciro Nishiguchi Co-authored-by: Daniel Jepson Co-authored-by: Derek Kozel Co-authored-by: EJ Kreinar Co-authored-by: Humberto Jimenez Co-authored-by: Ian Buckley Co-authored-by: Jörg Hofrichter Co-authored-by: Jon Kiser Co-authored-by: Josh Blum Co-authored-by: Jonathon Pendlum Co-authored-by: Martin Braun Co-authored-by: Matt Ettus Co-authored-by: Michael West Co-authored-by: Moritz Fischer Co-authored-by: Nick Foster Co-authored-by: Nicolas Cuervo Co-authored-by: Paul Butler Co-authored-by: Paul David Co-authored-by: Ryan Marlow Co-authored-by: Sugandha Gupta Co-authored-by: Sylvain Munaut Co-authored-by: Trung Tran Co-authored-by: Vidush Vishwanath Co-authored-by: Wade Fife --- fpga/usrp3/lib/rfnoc/fft_shift.v | 198 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 fpga/usrp3/lib/rfnoc/fft_shift.v (limited to 'fpga/usrp3/lib/rfnoc/fft_shift.v') diff --git a/fpga/usrp3/lib/rfnoc/fft_shift.v b/fpga/usrp3/lib/rfnoc/fft_shift.v new file mode 100644 index 000000000..453da8050 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/fft_shift.v @@ -0,0 +1,198 @@ +// +// Copyright 2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Arranges FFT output AXI stream packets so zero frequency bin is centered. Expects i_tuser to have FFT index. +// Intended to complement Xilinx Coregen AXI-stream FFT, but should work with any core with similar output. +// Works with natural and bit/digit reversed order. +// +// When using Xilinx FFT core, use bit/digit reversed order (versus natural order) to save resources +// +// Config bits: +// 0: Reverse output so positive frequencies are sent first +// 1: Bypass fft shift + +module fft_shift #( + parameter MAX_FFT_SIZE_LOG2 = 11, + parameter WIDTH = 32) +( + input clk, input reset, + input [1:0] config_tdata, input config_tvalid, output config_tready, + input [$clog2(MAX_FFT_SIZE_LOG2+1)-1:0] fft_size_log2_tdata, input fft_size_log2_tvalid, output fft_size_log2_tready, + input [WIDTH-1:0] i_tdata, input i_tlast, input i_tvalid, output i_tready, input [MAX_FFT_SIZE_LOG2-1:0] i_tuser, + output [WIDTH-1:0] o_tdata, output o_tlast, output o_tvalid, input o_tready +); + + reg ping_pong; + reg loading_pkt; + reg [2:0] reconfig_stall; + reg reverse, bypass; + reg [$clog2(MAX_FFT_SIZE_LOG2+1)-1:0] fft_size_log2_reg; + reg [MAX_FFT_SIZE_LOG2:0] fft_size; + reg [MAX_FFT_SIZE_LOG2-1:0] fft_size_minus_1, fft_shift_mask; + wire [WIDTH-1:0] ping_rd_data, pong_rd_data; + reg [MAX_FFT_SIZE_LOG2-1:0] ping_rd_addr, pong_rd_addr; + // t_user is the FFT index, this XOR is how the natural order FFT output is flipped to + // center the zero frequency bin in the middle. This is essentially adding half the FFT length to + // the write address without carrying, causing the upper half addresses to wrap around to the lower half + // and vice versa. + wire [MAX_FFT_SIZE_LOG2-1:0] ping_wr_addr = fft_shift_mask ^ i_tuser; + wire [MAX_FFT_SIZE_LOG2-1:0] pong_wr_addr = fft_shift_mask ^ i_tuser; + wire ping_wr_en = ping_pong ? i_tvalid & i_tready : 1'b0; + wire pong_wr_en = ping_pong ? 1'b0 : i_tvalid & i_tready; + // Always reads when loading ping/pong RAM so first word falls through. Avoids a bubble state. + wire ping_rd_en = ping_pong ? 1'b1 : o_tvalid & o_tready; + wire pong_rd_en = ping_pong ? o_tvalid & o_tready : 1'b1; + reg ping_loaded, pong_loaded; + // Only fill ping (or pong) RAM if it is empty and fft size has propagated through + assign i_tready = (ping_pong ? ~ping_loaded : ~pong_loaded) & ~reconfig_stall[2]; + reg ping_tlast, pong_tlast; + // Dump data in ping RAM (but only if it has been loaded!) while also loading in pong RAM and vice versa + assign o_tvalid = ping_pong ? pong_loaded : ping_loaded; + assign o_tlast = ping_pong ? pong_tlast : ping_tlast; + assign o_tdata = ping_pong ? pong_rd_data : ping_rd_data; + + // Prevent reconfiguration from occurring except at valid times. If the user violates tvalid rules + // (i.e. deasserts tvalid during the middle of a packet), could cause next output packet to have + // the wrong size. + assign config_tready = ~ping_loaded & ~pong_loaded & ~loading_pkt; + assign fft_size_log2_tready = config_tready; + + ram_2port #( + .DWIDTH(WIDTH), + .AWIDTH(MAX_FFT_SIZE_LOG2)) + ping_ram_2port ( + .clka(clk),.ena(1'b1),.wea(ping_wr_en),.addra(ping_wr_addr),.dia(i_tdata),.doa(), + .clkb(clk),.enb(ping_rd_en),.web(1'b0),.addrb(ping_rd_addr),.dib({WIDTH{1'b0}}),.dob(ping_rd_data)); + + ram_2port #( + .DWIDTH(WIDTH), + .AWIDTH(MAX_FFT_SIZE_LOG2)) + pong_ram_2port ( + .clka(clk),.ena(1'b1),.wea(pong_wr_en),.addra(pong_wr_addr),.dia(i_tdata),.doa(), + .clkb(clk),.enb(pong_rd_en),.web(1'b0),.addrb(pong_rd_addr),.dib({WIDTH{1'b0}}),.dob(pong_rd_data)); + + always @(posedge clk) begin + if (reset) begin + ping_pong <= 1'b1; + ping_loaded <= 1'b0; + pong_loaded <= 1'b0; + ping_rd_addr <= 0; + pong_rd_addr <= 0; + ping_tlast <= 1'b0; + pong_tlast <= 1'b0; + fft_shift_mask <= 0; + fft_size_minus_1 <= 0; + fft_size <= 0; + fft_size_log2_reg <= 0; + bypass <= 1'b0; + reverse <= 1'b0; + reconfig_stall <= 3'd0; + loading_pkt <= 1'b0; + end else begin + fft_size_minus_1 <= fft_size-1; + fft_size <= 1 << fft_size_log2_reg; + // Configure FFT shift mask such that the output order is either + // unaffected (bypass), positive frequencies first (reverse), or + // negative frequencies first + if (bypass) begin + fft_shift_mask <= 'd0; + end else if (reverse) begin + fft_shift_mask <= (fft_size-1) >> 1; + end else begin + fft_shift_mask <= fft_size >> 1; + end + + // Restrict updating + if (config_tready & config_tvalid) begin + reverse <= config_tdata[0]; + bypass <= config_tdata[1]; + reconfig_stall <= 3'b100; + end + // Restrict updating FFT size to valid times + // Also, deassert i_tready until updated fft size has propagated through + if (fft_size_log2_tready & fft_size_log2_tvalid) begin + fft_size_log2_reg <= fft_size_log2_tdata[$clog2(MAX_FFT_SIZE_LOG2)-1:0]; + reconfig_stall <= 3'b111; + end + if (~(config_tready & config_tvalid) & ~(fft_size_log2_tready & fft_size_log2_tvalid)) begin + reconfig_stall[0] <= 1'b0; + reconfig_stall[2:1] <= reconfig_stall[1:0]; + end + + // Used to disable reconfiguration when we are receiving a packet + if (i_tvalid & i_tready & ~i_tlast & ~loading_pkt) begin + loading_pkt <= 1'b1; + end else if (i_tvalid & i_tready & i_tlast & loading_pkt) begin + loading_pkt <= 1'b0; + end + + // Logic to simultaneously load ping RAM and unload pong RAM. Note, write address for ping RAM handled with i_tuser, + // so we only look for i_tlast instead of maintaining a write address counter. + if (ping_pong) begin + // Unload pong RAM + if (pong_loaded & o_tready & o_tvalid) begin + // i.e. pong_rd_addr == fft_size-1, more efficient to use tlast + if (pong_tlast) begin + // Special case: ping RAM loaded before pong RAM emptied + if (ping_loaded | (i_tvalid & i_tready & i_tlast)) begin + ping_pong <= ~ping_pong; + end + pong_tlast <= 1'b0; + pong_loaded <= 1'b0; + pong_rd_addr <= 0; + end else begin + pong_rd_addr <= pong_rd_addr + 1; + end + if (pong_rd_addr == fft_size_minus_1) begin + pong_tlast <= 1'b1; + end + end + // Ping RAM done loading + if (i_tvalid & i_tready & i_tlast) begin + // Value at addr 0 already loaded (see first word fall through and avoiding a bubble state comment above) + ping_rd_addr <= 1; + ping_loaded <= 1'b1; + // We can switch to the pong RAM only if it is empty (or about to be empty) + if (~pong_loaded) begin + ping_pong <= ~ping_pong; + end + end + // Special case: Ping and pong RAM loaded, wait until pong RAM unloaded. + if (ping_loaded & (pong_loaded & o_tvalid & o_tlast)) begin + ping_pong <= ~ping_pong; + end + // Same as above, just ping / pong switched + end else begin + if (ping_loaded & o_tready & o_tvalid) begin + if (ping_tlast) begin + if (pong_loaded | (i_tvalid & i_tready & i_tlast)) begin + ping_pong <= ~ping_pong; + end + ping_tlast <= 1'b0; + ping_loaded <= 1'b0; + ping_rd_addr <= 0; + end else begin + ping_rd_addr <= ping_rd_addr + 1; + end + if (ping_rd_addr == fft_size_minus_1) begin + ping_tlast <= 1'b1; + end + end + if (i_tvalid & i_tready & i_tlast) begin + pong_rd_addr <= 1; + pong_loaded <= 1'b1; + if (~ping_loaded | (ping_loaded & o_tvalid & o_tlast)) begin + ping_pong <= ~ping_pong; + end + end + if (pong_loaded & (ping_loaded & o_tvalid & o_tlast)) begin + ping_pong <= ~ping_pong; + end + end + end + end + +endmodule -- cgit v1.2.3