diff options
author | Martin Braun <martin.braun@ettus.com> | 2020-01-23 16:10:22 -0800 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2020-01-28 09:35:36 -0800 |
commit | bafa9d95453387814ef25e6b6256ba8db2df612f (patch) | |
tree | 39ba24b5b67072d354775272e687796bb511848d /fpga/usrp3/lib/rfnoc/fft_shift.v | |
parent | 3075b981503002df3115d5f1d0b97d2619ba30f2 (diff) | |
download | uhd-bafa9d95453387814ef25e6b6256ba8db2df612f.tar.gz uhd-bafa9d95453387814ef25e6b6256ba8db2df612f.tar.bz2 uhd-bafa9d95453387814ef25e6b6256ba8db2df612f.zip |
Merge FPGA repository back into UHD repository
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 <alex.williams@ni.com>
Co-authored-by: Andrej Rode <andrej.rode@ettus.com>
Co-authored-by: Ashish Chaudhari <ashish@ettus.com>
Co-authored-by: Ben Hilburn <ben.hilburn@ettus.com>
Co-authored-by: Ciro Nishiguchi <ciro.nishiguchi@ni.com>
Co-authored-by: Daniel Jepson <daniel.jepson@ni.com>
Co-authored-by: Derek Kozel <derek.kozel@ettus.com>
Co-authored-by: EJ Kreinar <ej@he360.com>
Co-authored-by: Humberto Jimenez <humberto.jimenez@ni.com>
Co-authored-by: Ian Buckley <ian.buckley@gmail.com>
Co-authored-by: Jörg Hofrichter <joerg.hofrichter@ni.com>
Co-authored-by: Jon Kiser <jon.kiser@ni.com>
Co-authored-by: Josh Blum <josh@joshknows.com>
Co-authored-by: Jonathon Pendlum <jonathan.pendlum@ettus.com>
Co-authored-by: Martin Braun <martin.braun@ettus.com>
Co-authored-by: Matt Ettus <matt@ettus.com>
Co-authored-by: Michael West <michael.west@ettus.com>
Co-authored-by: Moritz Fischer <moritz.fischer@ettus.com>
Co-authored-by: Nick Foster <nick@ettus.com>
Co-authored-by: Nicolas Cuervo <nicolas.cuervo@ettus.com>
Co-authored-by: Paul Butler <paul.butler@ni.com>
Co-authored-by: Paul David <paul.david@ettus.com>
Co-authored-by: Ryan Marlow <ryan.marlow@ettus.com>
Co-authored-by: Sugandha Gupta <sugandha.gupta@ettus.com>
Co-authored-by: Sylvain Munaut <tnt@246tNt.com>
Co-authored-by: Trung Tran <trung.tran@ettus.com>
Co-authored-by: Vidush Vishwanath <vidush.vishwanath@ettus.com>
Co-authored-by: Wade Fife <wade.fife@ettus.com>
Diffstat (limited to 'fpga/usrp3/lib/rfnoc/fft_shift.v')
-rw-r--r-- | fpga/usrp3/lib/rfnoc/fft_shift.v | 198 |
1 files changed, 198 insertions, 0 deletions
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 |