OpenCores
URL https://opencores.org/ocsvn/astron_filter/astron_filter/trunk

Subversion Repositories astron_filter

[/] [astron_filter/] [trunk/] [tb_fil_ppf_single.vhd] - Blame information for rev 2

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 2 danv
-- Author: Harm Jan Pepping : hajee at astron.nl   : April 2012
2
--         Eric Kooistra    : kooistra at astron.nl: july 2016
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
--
25
-- Purpose: Test bench for fil_ppf_single.vhd
26
--
27
--   The DUT fil_ppf_single.vhd has wb_factor = 1 fixed. For wb_factor > 1 use
28
--   the tb of fil_ppf_wide.vhd.
29
--
30
--   The testbench reads the filter coefficients from the reference dat file
31
--   and verifies that these are the same as the coeff in the corresponding
32
--   set of mif files (p_verify_ref_coeff_versus_mif_files) and as read via
33
--   MM from the coeff memories (p_verify_ref_coeff_versus_mm_ram).
34
--
35
--   The testbench inserts an pulse during the first nof_bands. The output is
36
--   verified by checking if the output values equal the filter coefficients
37
--   (p_verify_output). The coefficients appear in the order of the taps, but
38
--   in reversed order per tap.
39
--
40
--   The fil_ppf_filter in fil_ppf_single multiplies the in_dat by the filter
41
--   coefficients. The product has a double sign bit, whereby one sign bit is
42
--   dropped, because it only is needed to represent the positive result of
43
--   the product of the most negative in_dat and coeff, which does never occur
44
--   (because the most negative value is not used in the coefficients).
45
--   Therefore the product has width prod_w = in_dat_w + coef_dat_w - 1.
46
--   The fil_ppf_single assumes that the coefficients have DC gain = 1, so the
47
--   nof_taps and adder tree do not cause bit growth, thus sum_w = prod_w.
48
--   Therefore the maximum out_dat_w = sum_w. If out_dat_w is less, then the
49
--   sum_w - out_dat_w = lsb_w LSbits are rounded in fil_ppf_filter. This tb
50
--   compensates for the LSbits by scaling the input pulse such that the
51
--   out_dat still contains the exact coefficient values. Therefore in this tb
52
--   out_dat_w must be >= coef_dat_w (and then lsb_w <= in_dat_w-1).
53
--
54
--   The filter can operate on one or more streams in parallel. These streams
55
--   all share the same coefficient memory. The same pulse is applied to each
56
--   input stream in in_dat[nof_streams*in_dat_w-1:0] and verified for each
57
--   output stream in out_dat[nof_streams*out_dat_w-1:0] (p_verify_output).
58
--
59
--   Via g_enable_in_val_gaps it is possible toggle in_val in a random way to
60
--   verify that the DUT can handle arbitray gaps in in_dat.
61
--
62
--   It is possible to vary wb_factor, nof_chan, nof_bands, nof_taps, coef_w
63
--   and nof_streams. The input dat file is different for nof_taps, nof_bands
64
--   and coef_w. In addition the MIF files are different for wb_factor.
65
--   The nof_chan and nof_streams do not affect the input and output files,
66
--   because all multiplexed channels and pallellel streams use the same
67
--   filter coefficients.
68
--
69
--   The reference dat file is generated by the Matlab program:
70
--
71
--     $RADIOHDL_WORK/applications/apertif/matlab/run_pfir_coeff.m
72
--
73
--   The MIF files are generated by the Python script:
74
--
75
--     $RADIOHDL_WORK/libraries/dsp/filter/src/python/fil_ppf_create_mifs.py
76
--
77
--   The reference dat file and the MIF files use the same g_coefs_file_prefix.
78
--   For the reference dat file this prefix is expanded by nof_taps, nof_bands
79
--   and coef_dat_w and the MIF files in addition also have the wb_factor and
80
--   the MIF file index.
81
--
82
--   The example below shows how the mif file index relates to the reference
83
--   coefficients:
84
--
85
--   <g_coefs_file_prefix>_2taps_8bands_16b.dat
86
--   <g_coefs_file_prefix>_2taps_8bands_16b_4wb_0.mif
87
--   <g_coefs_file_prefix>_2taps_8bands_16b_4wb_1.mif
88
--   <g_coefs_file_prefix>_2taps_8bands_16b_4wb_2.mif
89
--   <g_coefs_file_prefix>_2taps_8bands_16b_4wb_3.mif
90
--   <g_coefs_file_prefix>_2taps_8bands_16b_4wb_4.mif
91
--   <g_coefs_file_prefix>_2taps_8bands_16b_4wb_5.mif
92
--   <g_coefs_file_prefix>_2taps_8bands_16b_4wb_6.mif
93
--   <g_coefs_file_prefix>_2taps_8bands_16b_4wb_7.mif
94
--
95
--                            nof_taps = 2
96
--                            nof_points = 8
97
--   pfir coef reference    : 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
98
--   pfir coef flip per tap : 7  6  5  4  3  2  1  0 15 14 13 12 11 10  9  8
99
--
100
--                            
101
--   wb_factor = 1:           7  6  5  4  3  2  1  0 15 14 13 12 11 10  9  8
102
--
103
--                            time sample:
104
--                            t0 t1 t2 t3 t4 t5 t6 t7t8 t9 t10t11t12t13t14t15
105
--
106
--                            mif index:
107
--                            0                      1
108
--
109
--                  wb_index:
110
--   wb_factor = 4:        0: 7  3                   15 11
111
--                         1: 6  2                   14 10
112
--                         2: 5  1                   13  9
113
--                         3: 4  0                   12  8
114
--
115
--                            time sample:
116
--                         0: t0 t4                  t8  t12  MSpart
117
--                         1: t1 t5                  t9  t13
118
--                         2: t2 t6                  t10 t14
119
--                         3: t3 t7                  t11 t15  LSpart
120
--
121
--                            mif index:
122
--                         0: 0                      1     first count taps
123
--                         1: 2                      3     then count wb
124
--                         2: 4                      5
125
--                         3: 6                      7
126
--
127
-- Usage:
128
--   > run -all
129
--   > observe out_dat in analogue format in Wave window
130
--   > testbench is selftesting.
131
--
132
library ieee, common_pkg_lib, dp_pkg_lib, astron_diagnostics_lib, astron_ram_lib, astron_mm_lib;
133
use IEEE.std_logic_1164.all;
134
use IEEE.numeric_std.all;
135
use IEEE.std_logic_textio.all;
136
use STD.textio.all;
137
use common_pkg_lib.common_pkg.all;
138
use astron_ram_lib.common_ram_pkg.ALL;
139
use common_pkg_lib.common_lfsr_sequences_pkg.ALL;
140
use common_pkg_lib.tb_common_pkg.all;
141
use astron_mm_lib.tb_common_mem_pkg.ALL;
142
use dp_pkg_lib.dp_stream_pkg.ALL;
143
use work.fil_pkg.all;
144
 
145
entity tb_fil_ppf_single is
146
  generic(
147
    g_fil_ppf_pipeline : t_fil_ppf_pipeline := (1, 1, 1, 1, 1, 1, 0);
148
      -- type t_fil_pipeline is record
149
      --   -- generic for the taps and coefficients memory
150
      --   mem_delay      : natural;  -- = 2
151
      --   -- generics for the multiplier in in the filter unit
152
      --   mult_input     : natural;  -- = 1
153
      --   mult_product   : natural;  -- = 1
154
      --   mult_output    : natural;  -- = 1
155
      --   -- generics for the adder tree in in the filter unit
156
      --   adder_stage    : natural;  -- = 1
157
      --   -- generics for the requantizer in the filter unit
158
      --   requant_remove_lsb : natural;  -- = 1
159
      --   requant_remove_msb : natural;  -- = 0
160
      -- end record;
161
    g_fil_ppf : t_fil_ppf := (1, 1, 64, 8, 1, 0, 8, 16, 16);
162
      -- type t_fil_ppf is record
163
      --   wb_factor      : natural; -- = 1, the wideband factor
164
      --   nof_chan       : natural; -- = default 0, defines the number of channels (=time-multiplexed input signals): nof channels = 2**nof_chan
165
      --   nof_bands      : natural; -- = 128, the number of polyphase channels (= number of points of the FFT)
166
      --   nof_taps       : natural; -- = 16, the number of FIR taps per subband
167
      --   nof_streams    : natural; -- = 1, the number of streams that are served by the same coefficients.
168
      --   backoff_w      : natural; -- = 0, number of bits for input backoff to avoid output overflow
169
      --   in_dat_w       : natural; -- = 8, number of input bits per stream
170
      --   out_dat_w      : natural; -- = 23, number of output bits (per stream). It is set to in_dat_w+coef_dat_w-1 = 23 to be sure the requantizer
171
      --                                  does not remove any of the data in order to be able to verify with the original coefficients values.
172
      --   coef_dat_w     : natural; -- = 16, data width of the FIR coefficients
173
      -- end record;
174
    g_coefs_file_prefix  : string  := "hex/run_pfir_coeff_m_incrementing";
175
    g_enable_in_val_gaps : boolean := FALSE
176
  );
177
end entity tb_fil_ppf_single;
178
 
179
architecture tb of tb_fil_ppf_single is
180
 
181
  constant c_clk_period          : time    := 10 ns;
182
 
183
  constant c_nof_channels        : natural := 2**g_fil_ppf.nof_chan;
184
  constant c_nof_coefs           : natural := g_fil_ppf.nof_taps * g_fil_ppf.nof_bands;       -- nof PFIR coef
185
  constant c_nof_data_in_filter  : natural := c_nof_coefs * c_nof_channels;                   -- nof PFIR coef expanded for all channels
186
  constant c_nof_data_per_tap    : natural := g_fil_ppf.nof_bands * c_nof_channels;
187
  constant c_nof_bands_per_mif   : natural := g_fil_ppf.nof_bands;
188
  constant c_nof_mif_files       : natural := g_fil_ppf.nof_taps;
189
  constant c_mif_coef_mem_addr_w : natural := ceil_log2(g_fil_ppf.nof_bands);
190
  constant c_mif_coef_mem_span   : natural := 2**c_mif_coef_mem_addr_w;                       -- mif coef mem span for one tap
191
  constant c_coefs_file_prefix   : string  := g_coefs_file_prefix & "_" & integer'image(g_fil_ppf.nof_taps) & "taps" &
192
                                                                    "_" & integer'image(g_fil_ppf.nof_bands) & "points" &
193
                                                                    "_" & integer'image(g_fil_ppf.coef_dat_w) & "b";
194
  constant c_mif_file_prefix     : string  := c_coefs_file_prefix & "_" & "1wb";
195
  constant c_mif_file_index_arr  : t_nat_natural_arr := array_init(0, c_nof_mif_files, 1);
196
 
197
  constant c_fil_prod_w          : natural := g_fil_ppf.in_dat_w + g_fil_ppf.coef_dat_w - 1;  -- skip double sign bit
198
  constant c_fil_sum_w           : natural := c_fil_prod_w;                                   -- DC gain = 1
199
  constant c_fil_lsb_w           : natural := c_fil_sum_w - g_fil_ppf.out_dat_w;              -- nof LSbits that get rounded for out_dat
200
  constant c_in_ampl             : natural := 2**c_fil_lsb_w;                                 -- scale in_dat to compensate for rounding
201
 
202
  constant c_gap_factor          : natural := sel_a_b(g_enable_in_val_gaps, 3, 1);
203
 
204
  -- input/output data width
205
  constant c_in_dat_w            : natural := g_fil_ppf.in_dat_w;
206
  constant c_out_dat_w           : natural := g_fil_ppf.out_dat_w;  -- must be >= coef_dat_w to be able to show the coeff in out_dat
207
 
208
  -- signal definitions
209
  signal tb_end         : std_logic := '0';
210
  signal tb_end_mm      : std_logic := '0';
211
  signal tb_end_almost  : std_logic := '0';
212
  signal clk            : std_logic := '0';
213
  signal rst            : std_logic := '0';
214
  signal random         : std_logic_vector(15 DOWNTO 0) := (OTHERS=>'0');  -- use different lengths to have different random sequences
215
 
216
  signal ram_coefs_mosi : t_mem_mosi := c_mem_mosi_rst;
217
  signal ram_coefs_miso : t_mem_miso;
218
 
219
  signal in_dat         : std_logic_vector(g_fil_ppf.nof_streams*c_in_dat_w-1 downto 0);
220
  signal in_val         : std_logic;
221
  signal in_val_cnt     : natural := 0;
222
  signal in_gap         : std_logic := '0';
223
 
224
  signal out_dat        : std_logic_vector(g_fil_ppf.nof_streams*c_out_dat_w-1 downto 0);
225
  signal out_val        : std_logic;
226
  signal out_val_cnt    : natural := 0;
227
 
228
  signal mif_coefs_arr  : t_integer_arr(g_fil_ppf.nof_bands-1 downto 0) := (OTHERS=>0);   -- = PFIR coef for 1 tap as read from 1 MIF file
229
  signal mif_dat_arr    : t_integer_arr(c_nof_data_in_filter-1 downto 0) := (OTHERS=>0);  -- = PFIR coef for all taps as read from all MIF files and expanded for all channels
230
 
231
  signal ref_coefs_arr  : t_integer_arr(c_nof_coefs-1 downto 0) := (OTHERS=>0);           -- = PFIR coef for all taps as read from the coefs file
232
  signal ref_dat_arr    : t_integer_arr(c_nof_data_in_filter-1 downto 0) := (OTHERS=>0);  -- = PFIR coef for all taps as read from the coefs file expanded for all channels
233
  signal ref_dat        : integer := 0;
234
 
235
  signal read_coefs_arr : t_integer_arr(c_nof_coefs-1 downto 0) := (OTHERS=>0);           -- = PFIR coef for all taps as read via MM from the coefs memories
236
 
237
begin
238
 
239
  clk <= (not clk) or tb_end after c_clk_period/2;
240
  rst <= '1', '0' after c_clk_period*7;
241
  random <= func_common_random(random) WHEN rising_edge(clk);
242
  in_gap <= random(random'HIGH) WHEN g_enable_in_val_gaps=TRUE ELSE '0';
243
 
244
  ---------------------------------------------------------------
245
  -- SEND PULSE TO THE DATA INPUT
246
  ---------------------------------------------------------------
247
  p_send_impulse : PROCESS
248
  BEGIN
249
    tb_end <= '0';
250
    in_dat <= (OTHERS=>'0');
251
    in_val <= '0';
252
    proc_common_wait_until_low(clk, rst);         -- Wait until reset has finished
253
    proc_common_wait_some_cycles(clk, 10);        -- Wait an additional amount of cycles
254
 
255
    -- Pulse during first tap of all channels
256
    FOR I IN 0 TO c_nof_data_per_tap-1 LOOP
257
      FOR S IN 0 To g_fil_ppf.nof_streams-1 LOOP
258
        in_dat((S+1)*c_in_dat_w-1 DOWNTO S*c_in_dat_w) <= TO_UVEC(c_in_ampl, c_in_dat_w);
259
      END LOOP;
260
      in_val <= '1';
261
      proc_common_wait_some_cycles(clk, 1);
262
      IF in_gap='1' THEN
263
        in_val <= '0';
264
        proc_common_wait_some_cycles(clk, 1);
265
      END IF;
266
    END LOOP;
267
 
268
    -- Zero during next nof_taps-1 blocks, +1 more to account for block latency of PPF and +1 more to have zeros output in last block
269
    in_dat <= (OTHERS=>'0');
270
    FOR J IN 0 TO g_fil_ppf.nof_taps-2 +1 +1  LOOP
271
      FOR I IN 0 TO c_nof_data_per_tap-1 LOOP
272
        in_val <= '1';
273
        proc_common_wait_some_cycles(clk, 1);
274
        IF in_gap='1' THEN
275
          in_val <= '0';
276
          proc_common_wait_some_cycles(clk, 1);
277
        END IF;
278
      END LOOP;
279
    END LOOP;
280
    in_val <= '0';
281
 
282
    -- Wait until done
283
    proc_common_wait_some_cycles(clk, c_gap_factor*c_nof_data_per_tap);  -- PPF latency of 1 tap
284
    proc_common_wait_until_high(clk, tb_end_mm);                         -- MM read done
285
    tb_end_almost <= '1';
286
    proc_common_wait_some_cycles(clk, 10);
287
    tb_end <= '1';
288
    WAIT;
289
  END PROCESS;
290
 
291
  ---------------------------------------------------------------
292
  -- CREATE REFERENCE ARRAY
293
  ---------------------------------------------------------------
294
  p_create_ref_from_coefs_file : PROCESS
295
    variable v_coefs_flip_arr : t_integer_arr(c_nof_coefs-1 downto 0) := (OTHERS=>0);
296
  begin
297
    -- Read all coeffs from coefs file
298
    proc_common_read_integer_file(c_coefs_file_prefix & ".dat", 0, c_nof_coefs, 1, ref_coefs_arr);
299
    wait for 1 ns;
300
    -- Reverse the coeffs per tap
301
    for J in 0 to g_fil_ppf.nof_taps-1 loop
302
      for I in 0 to g_fil_ppf.nof_bands-1 loop
303
        v_coefs_flip_arr(J*g_fil_ppf.nof_bands + g_fil_ppf.nof_bands-1-I) := ref_coefs_arr(J*g_fil_ppf.nof_bands+I);
304
      end loop;
305
    end loop;
306
    -- Expand the channels (for one stream)
307
    for I in 0 to c_nof_coefs-1 loop
308
      for K in 0 to c_nof_channels-1 loop
309
        ref_dat_arr(I*c_nof_channels + K) <= TO_SINT(TO_SVEC(v_coefs_flip_arr(I), g_fil_ppf.coef_dat_w));
310
      end loop;
311
    end loop;
312
    wait;
313
  end process;
314
 
315
  p_create_ref_from_mif_file : PROCESS
316
  begin
317
    for J in 0 to g_fil_ppf.nof_taps-1 loop
318
      -- Read coeffs per tap from MIF file
319
      proc_common_read_mif_file(c_mif_file_prefix & "_" & integer'image(J) & ".mif", mif_coefs_arr);
320
      wait for 1 ns;
321
      -- Expand the channels (for one stream)
322
      for I in 0 to g_fil_ppf.nof_bands-1 loop
323
        for K in 0 to c_nof_channels-1 loop
324
          mif_dat_arr(J*c_nof_data_per_tap + I*c_nof_channels + K) <= TO_SINT(TO_SVEC(mif_coefs_arr(I), g_fil_ppf.coef_dat_w));
325
        end loop;
326
      end loop;
327
    end loop;
328
    wait;
329
  end process;
330
 
331
  p_coefs_memory_read : process
332
    variable v_mif_base    : natural;
333
    variable v_coef_offset : natural;
334
    variable v_coef_index  : natural;
335
  begin
336
    ram_coefs_mosi <= c_mem_mosi_rst;
337
    for J in 0 to g_fil_ppf.nof_taps-1 loop
338
      v_mif_base  := J*c_mif_coef_mem_span;
339
      v_coef_offset := g_fil_ppf.nof_bands*(J+1)-1;
340
      for I in 0 to c_nof_bands_per_mif-1 loop
341
        proc_mem_mm_bus_rd(v_mif_base+I, clk, ram_coefs_miso, ram_coefs_mosi);
342
        proc_mem_mm_bus_rd_latency(1, clk);
343
        v_coef_index := v_coef_offset - I;
344
        read_coefs_arr(v_coef_index) <= TO_SINT(ram_coefs_miso.rddata(g_fil_ppf.coef_dat_w-1 DOWNTO 0));
345
      end loop;
346
    end loop;
347
    proc_common_wait_some_cycles(clk, 1);
348
    tb_end_mm <= '1';
349
    wait;
350
  end process;
351
 
352
  p_verify_ref_coeff_versus_mif_files : PROCESS
353
  begin
354
    -- Wait until the coeff dat file and coeff MIF files have been read
355
    proc_common_wait_until_low(clk, rst);
356
    assert mif_dat_arr = ref_dat_arr   report "Coefs file does not match coefs MIF files"   severity error;
357
    wait;
358
  end process;
359
 
360
  p_verify_ref_coeff_versus_mm_ram : PROCESS
361
  begin
362
    -- Wait until the coeff dat file has been read and the coeff have been read via MM
363
    proc_common_wait_until_high(clk, tb_end_almost);
364
    assert read_coefs_arr = ref_coefs_arr report "Coefs file does not match coefs read via MM" severity error;
365
    wait;
366
  end process;
367
 
368
  ---------------------------------------------------------------
369
  -- DUT = Device Under Test
370
  ---------------------------------------------------------------
371
  u_dut : entity work.fil_ppf_single
372
  generic map (
373
    g_fil_ppf           => g_fil_ppf,
374
    g_fil_ppf_pipeline  => g_fil_ppf_pipeline,
375
    g_file_index_arr    => c_mif_file_index_arr,
376
    g_coefs_file_prefix => c_mif_file_prefix
377
  )
378
  port map (
379
    dp_clk         => clk,
380
    dp_rst         => rst,
381
    mm_clk         => clk,
382
    mm_rst         => rst,
383
    ram_coefs_mosi => ram_coefs_mosi,
384
    ram_coefs_miso => ram_coefs_miso,
385
    in_dat         => in_dat,
386
    in_val         => in_val,
387
    out_dat        => out_dat,
388
    out_val        => out_val
389
  );
390
 
391
  ---------------------------------------------------------------
392
  -- VERIFY THE OUTPUT
393
  ---------------------------------------------------------------
394
  p_verify_out_dat_width : process
395
  begin
396
    -- Wait until tb_end_almost to avoid that the Error message gets lost in earlier messages
397
    proc_common_wait_until_high(clk, tb_end_almost);
398
    assert g_fil_ppf.out_dat_w >= g_fil_ppf.coef_dat_w report "Output data width too small for coefficients" severity error;
399
    wait;
400
  end process;
401
 
402
  p_verify_out_val_cnt : process
403
  begin
404
    -- Wait until tb_end_almost
405
    proc_common_wait_until_high(clk, tb_end_almost);
406
    -- The filter has a latency of 1 tap, so there remains in_dat for tap in the filter
407
    assert in_val_cnt > 0                              report "Test did not run, no valid input data" severity error;
408
    assert out_val_cnt = in_val_cnt-c_nof_data_per_tap report "Unexpected number of valid output data coefficients" severity error;
409
    wait;
410
  end process;
411
 
412
  in_val_cnt  <= in_val_cnt+1  when rising_edge(clk) and in_val='1'  else in_val_cnt;
413
  out_val_cnt <= out_val_cnt+1 when rising_edge(clk) and out_val='1' else out_val_cnt;
414
 
415
  ref_dat    <= ref_dat_arr(out_val_cnt) WHEN out_val_cnt < c_nof_data_in_filter ELSE 0;
416
 
417
  p_verify_out_dat : process(clk)
418
    variable v_coeff : integer;
419
  begin
420
    if rising_edge(clk) then
421
      if out_val='1' then
422
        if g_fil_ppf.out_dat_w >= g_fil_ppf.coef_dat_w then
423
          if g_fil_ppf.out_dat_w > g_fil_ppf.coef_dat_w then
424
            v_coeff := ref_dat;  -- positive input pulse
425
          else
426
            v_coeff := -ref_dat;  -- compensate for full scale negative input pulse
427
          end if;
428
          for S in 0 to g_fil_ppf.nof_streams-1 loop
429
            -- all streams carry the same data
430
            assert TO_SINT(out_dat((S+1)*g_fil_ppf.out_dat_w-1 downto S*g_fil_ppf.out_dat_w)) = v_coeff report "Output data error" severity error;
431
          end loop;
432
        end if;
433
      end if;
434
    end if;
435
  end process;
436
 
437
end tb;

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.