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