1 |
2 |
danv |
-- Author: Raj Thilak Rajan : rajan at astron.nl: Nov 2009
|
2 |
|
|
--------------------------------------------------------------------------------
|
3 |
|
|
--
|
4 |
|
|
-- Copyright (C) 2012
|
5 |
|
|
-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
|
6 |
|
|
-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
|
7 |
|
|
--
|
8 |
|
|
-- This program is free software: you can redistribute it and/or modify
|
9 |
|
|
-- it under the terms of the GNU General Public License as published by
|
10 |
|
|
-- the Free Software Foundation, either version 3 of the License, or
|
11 |
|
|
-- (at your option) any later version.
|
12 |
|
|
--
|
13 |
|
|
-- This program is distributed in the hope that it will be useful,
|
14 |
|
|
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15 |
|
|
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
16 |
|
|
-- GNU General Public License for more details.
|
17 |
|
|
--
|
18 |
|
|
-- You should have received a copy of the GNU General Public License
|
19 |
|
|
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
20 |
|
|
--
|
21 |
|
|
--------------------------------------------------------------------------------
|
22 |
|
|
|
23 |
|
|
--
|
24 |
|
|
-- Purpose: Test bench for the rTwoSDF pipelined radix 2 FFT
|
25 |
|
|
--
|
26 |
|
|
-- Description: ASTRON-RP-755
|
27 |
|
|
-- The testbench can simulate (via g_use_uniNoise_file):
|
28 |
|
|
--
|
29 |
|
|
-- a) complex uniform noise input from a file generated by testFFT_input.m
|
30 |
|
|
-- b) impulse input from a manually created file
|
31 |
|
|
--
|
32 |
|
|
-- Stimuli b) are useful for visual interpretation of the FFT output, because
|
33 |
|
|
-- an impulse at the real input and zero at the imaginary inpu will result
|
34 |
|
|
-- in DC and zero if the pulse occurs at the first sample or in sinus and
|
35 |
|
|
-- cosinus wave if the impulse occurs at a later sample. However because the
|
36 |
|
|
-- imaginary input is zero this does not cover all internals of the PFT
|
37 |
|
|
-- implementation. Therefore stimuli a) are needed to fully verify the PFT.
|
38 |
|
|
--
|
39 |
|
|
-- The rTwoSDF output can be verified in two ways:
|
40 |
|
|
--
|
41 |
|
|
-- 1) The MATLAB testFFT_output.m can calculate the floating point FFT and
|
42 |
|
|
-- compare it with the rTwoSDF implementation output file result.
|
43 |
|
|
-- 2) The rTwoSDF implementation output file is also kept in SVN as golden
|
44 |
|
|
-- reference result to allow verification using a file diff command like
|
45 |
|
|
-- e.g. WinMerge. This then avoids the need to run MATLAB to verify.
|
46 |
|
|
--
|
47 |
|
|
-- The tb asserts an error when the output does not match the expected output
|
48 |
|
|
-- that is read from the golden reference file. The output is also written
|
49 |
|
|
-- to a default output file to support offline analysis.
|
50 |
|
|
--
|
51 |
|
|
-- Usage:
|
52 |
|
|
-- > vsim -vopt -voptargs=+acc work.tb_rtwosdf (double click tb_rtwosdf icon)
|
53 |
|
|
-- > run -all
|
54 |
|
|
-- > observe the *_re and *_im as radix decimal, format analogue format
|
55 |
|
|
-- signals in the Wave window
|
56 |
|
|
--
|
57 |
|
|
-- Remarks:
|
58 |
|
|
-- . The tb uses LRM 1076-1987 style for file IO. This implies that only
|
59 |
|
|
-- simulator start and quit can open and close the file. The output file
|
60 |
|
|
-- can be read in a file editor, but the SVN file icon indicates that the
|
61 |
|
|
-- file is modified even if the contents is not changed. Only after closing
|
62 |
|
|
-- the simulation (> quit -sim) does the SVN file icon indicate the true
|
63 |
|
|
-- state. Next time we better use LRM 1076-1993 style for file IO.
|
64 |
|
|
|
65 |
|
|
library ieee, common_pkg_lib;
|
66 |
|
|
use IEEE.std_logic_1164.all;
|
67 |
|
|
use IEEE.numeric_std.all;
|
68 |
|
|
use IEEE.std_logic_textio.all;
|
69 |
|
|
use STD.textio.all;
|
70 |
|
|
use common_pkg_lib.common_pkg.all;
|
71 |
|
|
use common_pkg_lib.common_lfsr_sequences_pkg.all;
|
72 |
|
|
use common_pkg_lib.tb_common_pkg.all;
|
73 |
|
|
use work.rTwoSDFPkg.all;
|
74 |
|
|
use work.twiddlesPkg.all;
|
75 |
|
|
|
76 |
|
|
|
77 |
|
|
entity tb_rTwoSDF is
|
78 |
|
|
generic(
|
79 |
|
|
-- generics for tb
|
80 |
|
|
g_use_uniNoise_file : boolean := true;
|
81 |
|
|
g_in_en : natural := 1; -- 1 = always active, others = random control
|
82 |
|
|
-- generics for rTwoSDF
|
83 |
|
|
g_use_reorder : boolean := false; -- tb supports both true and false
|
84 |
|
|
g_nof_points : natural := 1024;
|
85 |
|
|
g_in_dat_w : natural := 8;
|
86 |
|
|
g_out_dat_w : natural := 14;
|
87 |
|
|
g_guard_w : natural := 2 -- guard bits are used to avoid overflow in single FFT stage.
|
88 |
|
|
);
|
89 |
|
|
end entity tb_rTwoSDF;
|
90 |
|
|
|
91 |
|
|
|
92 |
|
|
architecture tb of tb_rTwoSDF is
|
93 |
|
|
|
94 |
|
|
constant c_clk_period : time := 20 ns;
|
95 |
|
|
|
96 |
|
|
constant c_nof_points_w : natural := ceil_log2(g_nof_points);
|
97 |
|
|
|
98 |
|
|
-- input/output data width
|
99 |
|
|
constant c_stage_dat_w : natural := sel_a_b(g_out_dat_w > c_dsp_mult_w, g_out_dat_w, c_dsp_mult_w); -- number of bits used between the stages
|
100 |
|
|
|
101 |
|
|
-- input/output files
|
102 |
|
|
constant c_file_len : natural := 8*g_nof_points;
|
103 |
|
|
constant c_repeat : natural := 2; -- >= 2 to have sufficent frames for c_outputFile evaluation by testFFT_output.m
|
104 |
|
|
|
105 |
|
|
-- input from uniform noise file created automatically by MATLAB testFFT_input.m
|
106 |
|
|
constant c_noiseInputFile : string := "data/test/in/uniNoise_p" & natural'image(g_nof_points)& "_b"& natural'image(g_in_dat_w) &"_in.txt";
|
107 |
|
|
constant c_noiseGoldenFile : string := "data/test/out/uniNoise_p" & natural'image(g_nof_points)& "_in"& natural'image(g_in_dat_w) &"_out"&natural'image(g_out_dat_w) &"_out.txt";
|
108 |
|
|
constant c_noiseOutputFile : string := "data/test/out/uniNoise_out.txt";
|
109 |
|
|
|
110 |
|
|
-- input from manually created file
|
111 |
|
|
constant c_impulseInputFile : string := "data/test/in/impulse_p" & natural'image(g_nof_points)& "_b"& natural'image(g_in_dat_w)& "_in.txt";
|
112 |
|
|
constant c_impulseGoldenFile : string := "data/test/out/impulse_p" & natural'image(g_nof_points)& "_b"& natural'image(g_in_dat_w)& "_out.txt";
|
113 |
|
|
constant c_impulseOutputFile : string := "data/test/out/impulse_out.txt";
|
114 |
|
|
|
115 |
|
|
-- determine active stimuli and result files
|
116 |
|
|
constant c_inputFile : string := sel_a_b(g_use_uniNoise_file, c_noiseInputFile, c_impulseInputFile);
|
117 |
|
|
constant c_goldenFile : string := sel_a_b(g_use_uniNoise_file, c_noiseGoldenFile, c_impulseGoldenFile);
|
118 |
|
|
constant c_outputFile : string := sel_a_b(g_use_uniNoise_file, c_noiseOutputFile, c_impulseOutputFile);
|
119 |
|
|
|
120 |
|
|
-- signal definitions
|
121 |
|
|
signal tb_end : std_logic := '0';
|
122 |
|
|
signal clk : std_logic := '0';
|
123 |
|
|
signal rst : std_logic := '0';
|
124 |
|
|
signal enable : std_logic := '1';
|
125 |
|
|
signal random : std_logic_vector(15 downto 0) := (others=>'0'); -- use different lengths to have different random sequences
|
126 |
|
|
signal in_en : std_logic := '0';
|
127 |
|
|
|
128 |
|
|
signal in_re : std_logic_vector(g_in_dat_w-1 downto 0);
|
129 |
|
|
signal in_im : std_logic_vector(g_in_dat_w-1 downto 0);
|
130 |
|
|
signal in_sync : std_logic:= '0';
|
131 |
|
|
signal in_val : std_logic:= '0';
|
132 |
|
|
|
133 |
|
|
signal out_re : std_logic_vector(g_out_dat_w-1 downto 0);
|
134 |
|
|
signal out_im : std_logic_vector(g_out_dat_w-1 downto 0);
|
135 |
|
|
signal out_sync : std_logic:= '0';
|
136 |
|
|
signal out_val : std_logic:= '0';
|
137 |
|
|
|
138 |
|
|
signal in_file_data : t_integer_matrix(0 to c_file_len-1, 1 to 2) := (others=>(others=>0)); -- [re, im]
|
139 |
|
|
signal in_file_sync : std_logic_vector(0 to c_file_len-1):= (others=>'0');
|
140 |
|
|
signal in_file_val : std_logic_vector(0 to c_file_len-1):= (others=>'0');
|
141 |
|
|
|
142 |
|
|
signal in_index : natural := 0;
|
143 |
|
|
signal in_repeat : natural := 0;
|
144 |
|
|
|
145 |
|
|
signal gold_file_data : t_integer_matrix(0 to c_file_len-1, 1 to 2) := (others=>(others=>0)); -- [re, im]
|
146 |
|
|
signal gold_file_sync : std_logic_vector(0 to c_file_len-1):= (others=>'0');
|
147 |
|
|
signal gold_file_val : std_logic_vector(0 to c_file_len-1):= (others=>'0');
|
148 |
|
|
|
149 |
|
|
signal gold_index_max : natural := c_file_len - 2*g_nof_points;
|
150 |
|
|
signal gold_index : natural;
|
151 |
|
|
signal flip_index : natural;
|
152 |
|
|
signal gold_sync : std_logic;
|
153 |
|
|
signal gold_re : integer;
|
154 |
|
|
signal gold_im : integer;
|
155 |
|
|
|
156 |
|
|
begin
|
157 |
|
|
|
158 |
|
|
clk <= (not clk) or tb_end after c_clk_period/2;
|
159 |
|
|
rst <= '1', '0' after c_clk_period*7;
|
160 |
|
|
enable <= '0', '1' after c_clk_period*23;
|
161 |
|
|
random <= func_common_random(random) when rising_edge(clk);
|
162 |
|
|
in_en <= '1' when g_in_en=1 else random(random'HIGH);
|
163 |
|
|
|
164 |
|
|
|
165 |
|
|
p_read_input_file : process
|
166 |
|
|
file v_input : TEXT open READ_MODE is c_inputFile; -- this is LRM 1076-1987 style and implies that only simulator start and quit can open and close the file
|
167 |
|
|
variable v_log_line : LINE;
|
168 |
|
|
variable v_input_line : LINE;
|
169 |
|
|
variable v_index : integer :=0;
|
170 |
|
|
variable v_comma : character;
|
171 |
|
|
variable v_sync : std_logic_vector(0 to c_file_len-1):=(others=>'0');
|
172 |
|
|
variable v_val : std_logic_vector(0 to c_file_len-1):=(others=>'0');
|
173 |
|
|
variable v_data : t_integer_matrix(0 to c_file_len-1, 1 to 2) := (others=>(others=>0));
|
174 |
|
|
begin
|
175 |
|
|
-- wait 1 clock cycle to avoid that the output messages in the transcript window get lost in the 0 ps start up messages
|
176 |
|
|
proc_common_wait_some_cycles(clk, 1);
|
177 |
|
|
-- combinatorially read the file into the array
|
178 |
|
|
write(v_log_line, string'("reading stimuli file : "));
|
179 |
|
|
write(v_log_line, c_inputFile);
|
180 |
|
|
writeline(output, v_log_line);
|
181 |
|
|
loop
|
182 |
|
|
exit when endfile(v_input);
|
183 |
|
|
readline(v_input, v_input_line);
|
184 |
|
|
|
185 |
|
|
read(v_input_line, v_sync(v_index)); -- sync
|
186 |
|
|
read(v_input_line, v_comma);
|
187 |
|
|
|
188 |
|
|
read(v_input_line, v_val(v_index)); -- valid
|
189 |
|
|
read(v_input_line, v_comma);
|
190 |
|
|
|
191 |
|
|
read(v_input_line, v_data(v_index,1)); -- real
|
192 |
|
|
read(v_input_line, v_comma);
|
193 |
|
|
|
194 |
|
|
read(v_input_line, v_data(v_index,2)); -- imag
|
195 |
|
|
v_index := v_index + 1;
|
196 |
|
|
end loop;
|
197 |
|
|
write(v_log_line, string'("finished reading stimuli file"));
|
198 |
|
|
writeline(output, v_log_line);
|
199 |
|
|
|
200 |
|
|
in_file_data <= v_data;
|
201 |
|
|
in_file_sync <= v_sync;
|
202 |
|
|
in_file_val <= v_val;
|
203 |
|
|
wait;
|
204 |
|
|
end process;
|
205 |
|
|
|
206 |
|
|
p_in_stimuli : process(clk, rst)
|
207 |
|
|
begin
|
208 |
|
|
if rst='1' then
|
209 |
|
|
in_re <= (others=>'0');
|
210 |
|
|
in_im <= (others=>'0');
|
211 |
|
|
in_sync <= '0';
|
212 |
|
|
in_val <= '0';
|
213 |
|
|
|
214 |
|
|
in_index <= 0;
|
215 |
|
|
in_repeat <= 0;
|
216 |
|
|
elsif rising_edge(clk) then
|
217 |
|
|
|
218 |
|
|
in_sync <= '0';
|
219 |
|
|
in_val <= '0';
|
220 |
|
|
|
221 |
|
|
-- start stimuli some arbitrary time after rst release to ensure that the proper behaviour of the DUT does not depend on that time
|
222 |
|
|
if enable='1' then
|
223 |
|
|
-- use always active input (the in_file contents may still contain blocks with in_val='0') or use random active input
|
224 |
|
|
if in_en='1' then
|
225 |
|
|
if in_index<c_file_len-1 then
|
226 |
|
|
in_index <= in_index+1;
|
227 |
|
|
else
|
228 |
|
|
in_index <= 0;
|
229 |
|
|
in_repeat <= in_repeat + 1;
|
230 |
|
|
end if;
|
231 |
|
|
|
232 |
|
|
if in_repeat < c_repeat then
|
233 |
|
|
in_re <= std_logic_vector(to_signed(in_file_data(in_index, 1), g_in_dat_w));
|
234 |
|
|
in_im <= std_logic_vector(to_signed(in_file_data(in_index, 2), g_in_dat_w));
|
235 |
|
|
in_sync <= std_logic(in_file_sync(in_index));
|
236 |
|
|
in_val <= std_logic(in_file_val(in_index));
|
237 |
|
|
end if;
|
238 |
|
|
|
239 |
|
|
if in_repeat > c_repeat then
|
240 |
|
|
tb_end <= '1';
|
241 |
|
|
end if;
|
242 |
|
|
end if;
|
243 |
|
|
end if;
|
244 |
|
|
|
245 |
|
|
end if;
|
246 |
|
|
end process;
|
247 |
|
|
|
248 |
|
|
-- DUT = Device Under Test
|
249 |
|
|
u_rTwoSDF : entity work.rTwoSDF
|
250 |
|
|
generic map(
|
251 |
|
|
-- generics for the FFT
|
252 |
|
|
g_use_reorder => g_use_reorder,
|
253 |
|
|
g_in_dat_w => g_in_dat_w,
|
254 |
|
|
g_out_dat_w => g_out_dat_w,
|
255 |
|
|
g_stage_dat_w => c_stage_dat_w,
|
256 |
|
|
g_guard_w => g_guard_w,
|
257 |
|
|
g_nof_points => g_nof_points
|
258 |
|
|
)
|
259 |
|
|
port map(
|
260 |
|
|
clk => clk,
|
261 |
|
|
rst => rst,
|
262 |
|
|
in_re => in_re,
|
263 |
|
|
in_im => in_im,
|
264 |
|
|
in_val => in_val,
|
265 |
|
|
out_re => out_re,
|
266 |
|
|
out_im => out_im,
|
267 |
|
|
out_val => out_val
|
268 |
|
|
);
|
269 |
|
|
|
270 |
|
|
-- Read golden file with the expected DUT output
|
271 |
|
|
p_read_golden_file : process
|
272 |
|
|
file v_golden : TEXT open READ_MODE is c_goldenFile; -- this is LRM 1076-1987 style and implies that only simulator start and quit can open and close the file
|
273 |
|
|
variable v_log_line : LINE;
|
274 |
|
|
variable v_golden_line : LINE;
|
275 |
|
|
variable v_index : integer :=0;
|
276 |
|
|
variable v_comma : character;
|
277 |
|
|
variable v_sync : std_logic_vector(0 to c_file_len-1):=(others=>'0');
|
278 |
|
|
variable v_val : std_logic_vector(0 to c_file_len-1):=(others=>'0');
|
279 |
|
|
variable v_data : t_integer_matrix(0 to c_file_len-1, 1 to 2) := (others=>(others=>0));
|
280 |
|
|
begin
|
281 |
|
|
-- wait 1 clock cycle to avoid that the output messages in the transcript window get lost in the 0 ps start up messages
|
282 |
|
|
proc_common_wait_some_cycles(clk, 1);
|
283 |
|
|
-- combinatorially read the file into the array
|
284 |
|
|
write(v_log_line, string'("reading golden file : "));
|
285 |
|
|
write(v_log_line, c_goldenFile);
|
286 |
|
|
writeline(output, v_log_line);
|
287 |
|
|
loop
|
288 |
|
|
exit when endfile(v_golden);
|
289 |
|
|
readline(v_golden, v_golden_line);
|
290 |
|
|
|
291 |
|
|
read(v_golden_line, v_sync(v_index)); -- sync
|
292 |
|
|
read(v_golden_line, v_comma);
|
293 |
|
|
|
294 |
|
|
read(v_golden_line, v_val(v_index)); -- valid
|
295 |
|
|
read(v_golden_line, v_comma);
|
296 |
|
|
|
297 |
|
|
read(v_golden_line, v_data(v_index,1)); -- real
|
298 |
|
|
read(v_golden_line, v_comma);
|
299 |
|
|
|
300 |
|
|
read(v_golden_line, v_data(v_index,2)); -- imag
|
301 |
|
|
v_index := v_index + 1;
|
302 |
|
|
end loop;
|
303 |
|
|
write(v_log_line, string'("finished reading golden file"));
|
304 |
|
|
writeline(output, v_log_line);
|
305 |
|
|
|
306 |
|
|
gold_file_data <= v_data;
|
307 |
|
|
gold_file_sync <= v_sync;
|
308 |
|
|
gold_file_val <= v_val;
|
309 |
|
|
wait;
|
310 |
|
|
end process;
|
311 |
|
|
|
312 |
|
|
-- Show read data in Wave Window for debug purposes
|
313 |
|
|
gold_index <= gold_index + 1 when rising_edge(clk) and out_val='1';
|
314 |
|
|
flip_index <= (gold_index / g_nof_points) * g_nof_points + flip(gold_index mod g_nof_points, c_nof_points_w);
|
315 |
|
|
gold_sync <= gold_file_sync(gold_index);
|
316 |
|
|
gold_re <= gold_file_data(gold_index,1) when g_use_reorder=true else gold_file_data(flip_index,1);
|
317 |
|
|
gold_im <= gold_file_data(gold_index,2) when g_use_reorder=true else gold_file_data(flip_index,2);
|
318 |
|
|
|
319 |
|
|
-- Verify the output of the DUT with the expected output from the golden reference file
|
320 |
|
|
p_verify_output : process(clk)
|
321 |
|
|
begin
|
322 |
|
|
-- Compare
|
323 |
|
|
if rising_edge(clk) then
|
324 |
|
|
if out_val='1' and gold_index <= gold_index_max then
|
325 |
|
|
-- only write when out_val='1', because then the file is independent of cycles with invalid out_dat
|
326 |
|
|
assert out_sync = gold_sync report "Output sync error" severity error;
|
327 |
|
|
assert TO_SINT(out_re) = gold_re report "Output real data error" severity error;
|
328 |
|
|
assert TO_SINT(out_im) = gold_im report "Output imag data error" severity error;
|
329 |
|
|
end if;
|
330 |
|
|
end if;
|
331 |
|
|
end process;
|
332 |
|
|
|
333 |
|
|
-- Write to default output file, this allows using command line diff or graphical diff viewer to compare it with the golden result file
|
334 |
|
|
p_write_output_file : process(clk)
|
335 |
|
|
file v_output : TEXT open WRITE_MODE is c_outputFile; -- this is LRM 1076-1987 style and implies that only simulator start and quit can open and close the file
|
336 |
|
|
variable v_line : LINE;
|
337 |
|
|
begin
|
338 |
|
|
if rising_edge(clk) then
|
339 |
|
|
if out_val='1' then
|
340 |
|
|
-- only write when out_val='1', because then the file is independent of cycles with invalid out_dat
|
341 |
|
|
write(v_line, out_sync);
|
342 |
|
|
write(v_line, string'(","));
|
343 |
|
|
write(v_line, out_val);
|
344 |
|
|
write(v_line, string'(","));
|
345 |
|
|
write(v_line, to_integer(signed(out_re)));
|
346 |
|
|
write(v_line, string'(","));
|
347 |
|
|
write(v_line, to_integer(signed(out_im)));
|
348 |
|
|
writeline(v_output, v_line);
|
349 |
|
|
end if;
|
350 |
|
|
end if;
|
351 |
|
|
end process;
|
352 |
|
|
|
353 |
|
|
end tb;
|