URL
https://opencores.org/ocsvn/dblclockfft/dblclockfft/trunk
Subversion Repositories dblclockfft
[/] [dblclockfft/] [trunk/] [rtl/] [laststage.v] - Rev 37
Go to most recent revision | Compare with Previous | Blame | View Log
//////////////////////////////////////////////////////////////////////////////// // // Filename: laststage.v // // Project: A General Purpose Pipelined FFT Implementation // // Purpose: This is part of an FPGA implementation that will process // the final stage of a decimate-in-frequency FFT, running // through the data at two samples per clock. If you notice from the // derivation of an FFT, the only time both even and odd samples are // used at the same time is in this stage. Therefore, other than this // stage and these twiddles, all of the other stages can run two stages // at a time at one sample per clock. // // Operation: // Given a stream of values, operate upon them as though they were // value pairs, x[2n] and x[2n+1]. The stream begins when n=0, and ends // when n=1. When the first x[0] value enters, the synchronization // input, i_sync, must be true as well. // // For this stream, produce outputs // y[2n ] = x[2n] + x[2n+1], and // y[2n+1] = x[2n] - x[2n+1] // // When y[0] is output, a synchronization bit o_sync will be true as // well, otherwise it will be zero. // // // In this implementation, the output is valid one clock after the input // is valid. The output also accumulates one bit above and beyond the // number of bits in the input. // // i_clk A system clock // i_reset A synchronous reset // i_ce Circuit enable--nothing happens unless this line is high // i_sync A synchronization signal, high once per FFT at the start // i_left The first (even) complex sample input. The higher order // bits contain the real portion, low order bits the // imaginary portion, all in two's complement. // i_right The next (odd) complex sample input, same format as // i_left. // o_left The first (even) complex output. // o_right The next (odd) complex output. // o_sync Output synchronization signal. // // Creator: Dan Gisselquist, Ph.D. // Gisselquist Technology, LLC // //////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2015-2018, Gisselquist Technology, LLC // // This program is free software (firmware): you can redistribute it and/or // modify it under the terms of the GNU General Public License as published // by the Free Software Foundation, either version 3 of the License, or (at // your option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License along // with this program. (It's in the $(ROOT)/doc directory, run make with no // target there if the PDF file isn't present.) If not, see // <http://www.gnu.org/licenses/> for a copy. // // License: GPL, v3, as defined and found on www.gnu.org, // http://www.gnu.org/licenses/gpl.html // // //////////////////////////////////////////////////////////////////////////////// // // `default_nettype none // module laststage(i_clk, i_reset, i_ce, i_sync, i_left, i_right, o_left, o_right, o_sync); parameter IWIDTH=16,OWIDTH=IWIDTH+1, SHIFT=0; input i_clk, i_reset, i_ce, i_sync; input [(2*IWIDTH-1):0] i_left, i_right; output reg [(2*OWIDTH-1):0] o_left, o_right; output reg o_sync; wire signed [(IWIDTH-1):0] i_in_0r, i_in_0i, i_in_1r, i_in_1i; assign i_in_0r = i_left[(2*IWIDTH-1):(IWIDTH)]; assign i_in_0i = i_left[(IWIDTH-1):0]; assign i_in_1r = i_right[(2*IWIDTH-1):(IWIDTH)]; assign i_in_1i = i_right[(IWIDTH-1):0]; wire [(OWIDTH-1):0] o_out_0r, o_out_0i, o_out_1r, o_out_1i; // Handle a potential rounding situation, when IWIDTH>=OWIDTH. // As with any register connected to the sync pulse, these must // have initial values and be reset on the i_reset signal. // Other data values need only restrict their updates to i_ce // enabled clocks, but sync's must obey resets and initial // conditions as well. reg rnd_sync, r_sync; initial rnd_sync = 1'b0; // Sync into rounding initial r_sync = 1'b0; // Sync coming out always @(posedge i_clk) if (i_reset) begin rnd_sync <= 1'b0; r_sync <= 1'b0; end else if (i_ce) begin rnd_sync <= i_sync; r_sync <= rnd_sync; end // As with other variables, these are really only updated when in // the processing pipeline, after the first i_sync. However, to // eliminate as much unnecessary logic as possible, we toggle // these any time the i_ce line is enabled, and don't reset. // them on i_reset. // Don't forget that we accumulate a bit by adding two values // together. Therefore our intermediate value must have one more // bit than the two originals. reg signed [(IWIDTH):0] rnd_in_0r, rnd_in_0i; reg signed [(IWIDTH):0] rnd_in_1r, rnd_in_1i; always @(posedge i_clk) if (i_ce) begin // rnd_in_0r <= i_in_0r + i_in_1r; rnd_in_0i <= i_in_0i + i_in_1i; // rnd_in_1r <= i_in_0r - i_in_1r; rnd_in_1i <= i_in_0i - i_in_1i; // end convround #(IWIDTH+1,OWIDTH,SHIFT) do_rnd_0r(i_clk, i_ce, rnd_in_0r, o_out_0r); convround #(IWIDTH+1,OWIDTH,SHIFT) do_rnd_0i(i_clk, i_ce, rnd_in_0i, o_out_0i); convround #(IWIDTH+1,OWIDTH,SHIFT) do_rnd_1r(i_clk, i_ce, rnd_in_1r, o_out_1r); convround #(IWIDTH+1,OWIDTH,SHIFT) do_rnd_1i(i_clk, i_ce, rnd_in_1i, o_out_1i); // Prior versions of this routine did not include the extra // clock and register/flip-flops that this routine requires. // These are placed in here to correct a bug in Verilator, that // otherwise struggles. (Hopefully this will fix the problem ...) always @(posedge i_clk) if (i_ce) begin o_left <= { o_out_0r, o_out_0i }; o_right <= { o_out_1r, o_out_1i }; end initial o_sync = 1'b0; // Final sync coming out of module always @(posedge i_clk) if (i_reset) o_sync <= 1'b0; else if (i_ce) o_sync <= r_sync; endmodule
Go to most recent revision | Compare with Previous | Blame | View Log