1 |
2 |
danv |
--------------------------------------------------------------------------------
|
2 |
|
|
-- Author: Harm Jan Pepping : HJP at astron.nl: April 2012
|
3 |
|
|
--------------------------------------------------------------------------------
|
4 |
|
|
--
|
5 |
|
|
-- Copyright (C) 2012
|
6 |
|
|
-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
|
7 |
|
|
-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
|
8 |
|
|
--
|
9 |
|
|
-- This program is free software: you can redistribute it and/or modify
|
10 |
|
|
-- it under the terms of the GNU General Public License as published by
|
11 |
|
|
-- the Free Software Foundation, either version 3 of the License, or
|
12 |
|
|
-- (at your option) any later version.
|
13 |
|
|
--
|
14 |
|
|
-- This program is distributed in the hope that it will be useful,
|
15 |
|
|
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
16 |
|
|
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
17 |
|
|
-- GNU General Public License for more details.
|
18 |
|
|
--
|
19 |
|
|
-- You should have received a copy of the GNU General Public License
|
20 |
|
|
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
21 |
|
|
--
|
22 |
|
|
--------------------------------------------------------------------------------
|
23 |
|
|
|
24 |
|
|
-- Purpose: Perform the separate function to support two real inputs
|
25 |
|
|
--
|
26 |
|
|
-- Description: It composes an output stream where the bins for input A and B are
|
27 |
|
|
-- interleaved in the stream: A, B, A, B etc (for both real and imaginary part)
|
28 |
|
|
--
|
29 |
|
|
-- It is assumed that the incoming data is as follows for a 1024 point FFT:
|
30 |
|
|
--
|
31 |
|
|
-- X(0), X(1024), X(1), X(1023), X(2), X(1022), etc...
|
32 |
|
|
-- |
|
33 |
|
|
-- |
|
34 |
|
|
-- This value is X(0)!!!
|
35 |
|
|
--
|
36 |
|
|
-- The function that is performed is based on the following equation:
|
37 |
|
|
--
|
38 |
|
|
-- A.real(m) = (X.real(N-m) + X.real(m))/2
|
39 |
|
|
-- A.imag(m) = (X.imag(m) - X.imag(N-m))/2
|
40 |
|
|
-- B.real(m) = (X.imag(m) + X.imag(N-m))/2
|
41 |
|
|
-- B.imag(m) = (X.real(N-m) - X.real(m))/2
|
42 |
|
|
--
|
43 |
|
|
-- Remarks:
|
44 |
|
|
-- . The add and sub output of the separate have 1 bit growth that needs to be
|
45 |
|
|
-- rounded. Simply skipping 1 LSbit is not suitable, because it yields
|
46 |
|
|
-- asymmetry around 0 and thus a DC offset. For example for N = 3-bit data:
|
47 |
|
|
-- x = -4 -3 -2 -1 0 1 2 3
|
48 |
|
|
-- round(x/2) = -2 -2 -1 -1 0 1 1 2 = common_round for signed
|
49 |
|
|
-- floor(x/2) = -2 -2 -1 -1 0 0 1 1 = truncation
|
50 |
|
|
-- The most negative value can be ignored:
|
51 |
|
|
-- x : mean(-3 -2 -1 0 1 2 3) = 0
|
52 |
|
|
-- . round(x/2) : mean(-2 -1 -1 0 1 1 2) = 0
|
53 |
|
|
-- . floor(x/2) : mean(-2 -1 -1 0 0 1 1) = -2/8 = -0.25 = -2^(N-1)/2 / 2^N
|
54 |
|
|
-- So the DC offset due to truncation is -0.25 LSbit, independent of N.
|
55 |
|
|
|
56 |
|
|
library IEEE, common_pkg_lib, common_add_sub_lib, common_requantize_lib;
|
57 |
|
|
use IEEE.std_logic_1164.ALL;
|
58 |
|
|
use IEEE.numeric_std.ALL;
|
59 |
|
|
use common_pkg_lib.common_pkg.ALL;
|
60 |
|
|
|
61 |
|
|
entity fft_sepa is
|
62 |
|
|
port (
|
63 |
|
|
clk : in std_logic;
|
64 |
|
|
rst : in std_logic;
|
65 |
|
|
in_dat : in std_logic_vector;
|
66 |
|
|
in_val : in std_logic;
|
67 |
|
|
out_dat : out std_logic_vector;
|
68 |
|
|
out_val : out std_logic
|
69 |
|
|
);
|
70 |
|
|
end entity fft_sepa;
|
71 |
|
|
|
72 |
|
|
architecture rtl of fft_sepa is
|
73 |
|
|
|
74 |
|
|
constant c_sepa_round : boolean := true; -- must be true, because separate should round the 1 bit growth
|
75 |
|
|
|
76 |
|
|
constant c_data_w : natural := in_dat'length/c_nof_complex;
|
77 |
|
|
constant c_c_data_w : natural := c_nof_complex*c_data_w;
|
78 |
|
|
constant c_pipeline : natural := 3;
|
79 |
|
|
|
80 |
|
|
type reg_type is record
|
81 |
|
|
switch : std_logic; -- Register used to toggle between A & B definitionn
|
82 |
|
|
val_dly : std_logic_vector(c_pipeline-1 downto 0); -- Register that delays the incoming valid signal
|
83 |
|
|
xn_m_reg : std_logic_vector(c_c_data_w-1 downto 0); -- Register to hold the X(N-m) value for one cycle
|
84 |
|
|
xm_reg : std_logic_vector(c_c_data_w-1 downto 0); -- Register to hold the X(m) value for one cycle
|
85 |
|
|
add_reg_a : std_logic_vector(c_data_w-1 downto 0); -- Input register A for the adder
|
86 |
|
|
add_reg_b : std_logic_vector(c_data_w-1 downto 0); -- Input register B for the adder
|
87 |
|
|
sub_reg_a : std_logic_vector(c_data_w-1 downto 0); -- Input register A for the subtractor
|
88 |
|
|
sub_reg_b : std_logic_vector(c_data_w-1 downto 0); -- Input register B for the subtractor
|
89 |
|
|
out_dat : std_logic_vector(c_c_data_w-1 downto 0); -- Registered output value
|
90 |
|
|
out_val : std_logic; -- Registered data valid signal
|
91 |
|
|
end record;
|
92 |
|
|
|
93 |
|
|
signal r, rin : reg_type;
|
94 |
|
|
signal sub_result : std_logic_vector(c_data_w downto 0); -- Result of the subtractor
|
95 |
|
|
signal add_result : std_logic_vector(c_data_w downto 0); -- Result of the adder
|
96 |
|
|
|
97 |
|
|
signal sub_result_q : std_logic_vector(c_data_w-1 downto 0); -- Requantized result of the subtractor
|
98 |
|
|
signal add_result_q : std_logic_vector(c_data_w-1 downto 0); -- Requantized result of the adder
|
99 |
|
|
|
100 |
|
|
begin
|
101 |
|
|
|
102 |
|
|
---------------------------------------------------------------
|
103 |
|
|
-- ADDER AND SUBTRACTOR
|
104 |
|
|
---------------------------------------------------------------
|
105 |
|
|
adder : entity common_add_sub_lib.common_add_sub
|
106 |
|
|
generic map (
|
107 |
|
|
g_direction => "ADD",
|
108 |
|
|
g_representation => "SIGNED",
|
109 |
|
|
g_pipeline_input => 0,
|
110 |
|
|
g_pipeline_output => 1,
|
111 |
|
|
g_in_dat_w => c_data_w,
|
112 |
|
|
g_out_dat_w => c_data_w + 1
|
113 |
|
|
)
|
114 |
|
|
port map (
|
115 |
|
|
clk => clk,
|
116 |
|
|
in_a => r.add_reg_a,
|
117 |
|
|
in_b => r.add_reg_b,
|
118 |
|
|
result => add_result
|
119 |
|
|
);
|
120 |
|
|
|
121 |
|
|
subtractor : entity common_add_sub_lib.common_add_sub
|
122 |
|
|
generic map (
|
123 |
|
|
g_direction => "SUB",
|
124 |
|
|
g_representation => "SIGNED",
|
125 |
|
|
g_pipeline_input => 0,
|
126 |
|
|
g_pipeline_output => 1,
|
127 |
|
|
g_in_dat_w => c_data_w,
|
128 |
|
|
g_out_dat_w => c_data_w + 1
|
129 |
|
|
)
|
130 |
|
|
port map (
|
131 |
|
|
clk => clk,
|
132 |
|
|
in_a => r.sub_reg_a,
|
133 |
|
|
in_b => r.sub_reg_b,
|
134 |
|
|
result => sub_result
|
135 |
|
|
);
|
136 |
|
|
|
137 |
|
|
gen_sepa_truncate : IF c_sepa_round=FALSE GENERATE
|
138 |
|
|
-- truncate the one LSbit
|
139 |
|
|
add_result_q <= add_result(c_data_w downto 1);
|
140 |
|
|
sub_result_q <= sub_result(c_data_w downto 1);
|
141 |
|
|
end generate;
|
142 |
|
|
|
143 |
|
|
gen_sepa_round : IF c_sepa_round=TRUE GENERATE
|
144 |
|
|
-- round the one LSbit
|
145 |
|
|
round_add : ENTITY common_requantize_lib.common_round
|
146 |
|
|
GENERIC MAP (
|
147 |
|
|
g_representation => "SIGNED", -- SIGNED (round +-0.5 away from zero to +- infinity) or UNSIGNED rounding (round 0.5 up to + inifinity)
|
148 |
|
|
g_round => TRUE, -- when TRUE round the input, else truncate the input
|
149 |
|
|
g_round_clip => FALSE, -- when TRUE clip rounded input >= +max to avoid wrapping to output -min (signed) or 0 (unsigned)
|
150 |
|
|
g_pipeline_input => 0, -- >= 0
|
151 |
|
|
g_pipeline_output => 0, -- >= 0, use g_pipeline_input=0 and g_pipeline_output=0 for combinatorial output
|
152 |
|
|
g_in_dat_w => c_data_w+1,
|
153 |
|
|
g_out_dat_w => c_data_w
|
154 |
|
|
)
|
155 |
|
|
PORT MAP (
|
156 |
|
|
clk => clk,
|
157 |
|
|
in_dat => add_result,
|
158 |
|
|
out_dat => add_result_q
|
159 |
|
|
);
|
160 |
|
|
|
161 |
|
|
round_sub : ENTITY common_requantize_lib.common_round
|
162 |
|
|
GENERIC MAP (
|
163 |
|
|
g_representation => "SIGNED", -- SIGNED (round +-0.5 away from zero to +- infinity) or UNSIGNED rounding (round 0.5 up to + inifinity)
|
164 |
|
|
g_round => TRUE, -- when TRUE round the input, else truncate the input
|
165 |
|
|
g_round_clip => FALSE, -- when TRUE clip rounded input >= +max to avoid wrapping to output -min (signed) or 0 (unsigned)
|
166 |
|
|
g_pipeline_input => 0, -- >= 0
|
167 |
|
|
g_pipeline_output => 0, -- >= 0, use g_pipeline_input=0 and g_pipeline_output=0 for combinatorial output
|
168 |
|
|
g_in_dat_w => c_data_w+1,
|
169 |
|
|
g_out_dat_w => c_data_w
|
170 |
|
|
)
|
171 |
|
|
PORT MAP (
|
172 |
|
|
clk => clk,
|
173 |
|
|
in_dat => sub_result,
|
174 |
|
|
out_dat => sub_result_q
|
175 |
|
|
);
|
176 |
|
|
end generate;
|
177 |
|
|
|
178 |
|
|
---------------------------------------------------------------
|
179 |
|
|
-- CONTROL PROCESS
|
180 |
|
|
---------------------------------------------------------------
|
181 |
|
|
comb : process(r, rst, in_val, in_dat, add_result_q, sub_result_q)
|
182 |
|
|
variable v : reg_type;
|
183 |
|
|
begin
|
184 |
|
|
v := r;
|
185 |
|
|
|
186 |
|
|
-- Shift register for the valid signal
|
187 |
|
|
v.val_dly(c_pipeline-1 downto 1) := v.val_dly(c_pipeline-2 downto 0);
|
188 |
|
|
v.val_dly(0) := in_val;
|
189 |
|
|
|
190 |
|
|
-- Composition of the output registers:
|
191 |
|
|
v.out_dat := sub_result_q & add_result_q;
|
192 |
|
|
v.out_val := r.val_dly(c_pipeline-1);
|
193 |
|
|
|
194 |
|
|
-- Compose the inputs for the adder and subtractor
|
195 |
|
|
-- for both A and B
|
196 |
|
|
if in_val = '1' or r.val_dly(0) = '1' then
|
197 |
|
|
if r.switch = '0' then
|
198 |
|
|
v.xm_reg := in_dat;
|
199 |
|
|
v.add_reg_a := r.xm_reg(c_c_data_w-1 downto c_data_w); -- Xm imag
|
200 |
|
|
v.add_reg_b := r.xn_m_reg(c_c_data_w-1 downto c_data_w); -- Xn-m imag
|
201 |
|
|
v.sub_reg_a := r.xn_m_reg(c_data_w-1 downto 0); -- Xn-m real
|
202 |
|
|
v.sub_reg_b := r.xm_reg(c_data_w-1 downto 0); -- Xm real
|
203 |
|
|
else
|
204 |
|
|
v.xn_m_reg := in_dat;
|
205 |
|
|
v.add_reg_a := r.xm_reg(c_data_w-1 downto 0); -- Xm real
|
206 |
|
|
v.add_reg_b := in_dat(c_data_w-1 downto 0); -- Xn-m real
|
207 |
|
|
v.sub_reg_a := r.xm_reg(c_c_data_w-1 downto c_data_w); -- Xm imag
|
208 |
|
|
v.sub_reg_b := in_dat(c_c_data_w-1 downto c_data_w); -- Xn-m imag
|
209 |
|
|
end if;
|
210 |
|
|
end if;
|
211 |
|
|
|
212 |
|
|
if in_val = '1' then
|
213 |
|
|
v.switch := not r.switch;
|
214 |
|
|
end if;
|
215 |
|
|
|
216 |
|
|
if(rst = '1') then
|
217 |
|
|
v.switch := '0';
|
218 |
|
|
v.val_dly := (others => '0');
|
219 |
|
|
v.xn_m_reg := (others => '0');
|
220 |
|
|
v.xm_reg := (others => '0');
|
221 |
|
|
v.add_reg_a := (others => '0');
|
222 |
|
|
v.add_reg_b := (others => '0');
|
223 |
|
|
v.sub_reg_a := (others => '0');
|
224 |
|
|
v.sub_reg_b := (others => '0');
|
225 |
|
|
v.out_dat := (others => '0');
|
226 |
|
|
v.out_val := '0';
|
227 |
|
|
end if;
|
228 |
|
|
|
229 |
|
|
rin <= v;
|
230 |
|
|
|
231 |
|
|
end process comb;
|
232 |
|
|
|
233 |
|
|
regs : process(clk)
|
234 |
|
|
begin
|
235 |
|
|
if rising_edge(clk) then
|
236 |
|
|
r <= rin;
|
237 |
|
|
end if;
|
238 |
|
|
end process;
|
239 |
|
|
|
240 |
|
|
---------------------------------------------------------------
|
241 |
|
|
-- OUTPUT STAGE
|
242 |
|
|
---------------------------------------------------------------
|
243 |
|
|
out_dat <= r.out_dat;
|
244 |
|
|
out_val <= r.out_val;
|
245 |
|
|
|
246 |
|
|
end rtl;
|
247 |
|
|
|
248 |
|
|
|