diff --git a/libraries/dsp/filter/src/vhdl/fil_ppf_single.vhd b/libraries/dsp/filter/src/vhdl/fil_ppf_single.vhd new file mode 100644 index 0000000000000000000000000000000000000000..f27ef672b4af9cdafdcd1e4d39abfa4f58d60b31 --- /dev/null +++ b/libraries/dsp/filter/src/vhdl/fil_ppf_single.vhd @@ -0,0 +1,253 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2012 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- This program is free software: 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 +-- MERCHANTABILITY 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. If not, see <http://www.gnu.org/licenses/>. +-- +------------------------------------------------------------------------------- +-- Purpose: Performing a poly phase prefilter function on one or multiple datastreams. +-- +-- Description: The poly phase prefilter function is based on a taps memory, a +-- coefficients memory, a filter and a control unit. +-- The control unit writes the incoming data to the taps memory, +-- along with the historical tap data. It also drives the read +-- addresses for both the taps- and the coefficients memory. The +-- output of the taps memory and the coefficients memory are +-- connected to the input of the filter unit that peforms the +-- actual filter function(multiply and accumulate). +-- The prefilter support multiple streams that share the same +-- filtercoefficients. +-- +-- The follwing example shows the working for the poly phase prefilter +-- where nof_bands = 4 and nof_taps = 2. The total number of coef- +-- ficients is 8. For the given input stream all the multiplications and +-- additions are given that are required to generate the given output +-- stream. Note that every input sample is used tap=2 times. +-- +-- Incoming datastream: a0 a1 a2 a3 b0 b1 b2 b3 c0 c1 c2 c3 d0 d1 d2 d3 .... +-- +-- A0 = coef0*a0 + coef1*b0 +-- A1 = coef2*a1 + coef3*b1 +-- A2 = coef4*a2 + coef5*b2 +-- A3 = coef6*a3 + coef7*b3 +-- +-- B0 = coef0*b0 + coef1*c0 +-- B1 = coef2*b1 + coef3*c1 +-- B2 = coef4*b2 + coef5*c2 +-- B3 = coef6*b3 + coef7*c3 +-- +-- C0 = coef0*c0 + coef1*d0 +-- C1 = coef2*c1 + coef3*d1 +-- C2 = coef4*c2 + coef5*d2 +-- C3 = coef6*c3 + coef7*d3 +-- +-- D0 = coef0*d0 + coef1*e0 +-- D1 = coef2*d1 + coef3*e1 +-- D2 = coef4*d2 + coef5*e2 +-- D3 = coef6*d3 + coef7*e3 +-- +-- Outgoing datastream: A0 A1 A2 A3 B0 B1 B2 B3 C0 C1 C2 C3 D0 D1 D2 D3 +-- +-- The filter coefficients can be written via the mm interface. +-- +-- +-- Remarks: .The taps memory introduces a z-delay of nof_bands dp_clk cycles. +-- + + +library IEEE, common_lib; +use IEEE.std_logic_1164.ALL; +use IEEE.numeric_std.ALL; +use common_lib.common_pkg.ALL; +use common_lib.common_mem_pkg.ALL; +use work.fil_pkg.ALL; + +entity fil_ppf_single is + generic ( + g_fil_ppf : t_fil_ppf := c_fil_ppf; + g_fil_ppf_pipeline : t_fil_ppf_pipeline := c_fil_ppf_pipeline; + g_file_index_arr : t_nat_natural_arr := array_init(0, 128, 1); -- default use the instance index as file index 0, 1, 2, 3, 4 ... + g_coefs_file_prefix : string := "../../data/coef" -- Relative path to the mif files that contain the initial data for the coefficients memories + ); -- The sequence number and ".mif"-extension are added within the entity. + port ( + dp_clk : in std_logic; + dp_rst : in std_logic; + mm_clk : in std_logic; + mm_rst : in std_logic; + ram_coefs_mosi : in t_mem_mosi; + ram_coefs_miso : out t_mem_miso := c_mem_miso_rst; + in_dat : in std_logic_vector; + in_val : in std_logic; + out_dat : out std_logic_vector; + out_val : out std_logic + ); +end fil_ppf_single; + +architecture rtl of fil_ppf_single is + + constant c_coefs_postfix : string := ".mif"; + constant c_taps_mem_addr_w : natural := ceil_log2(g_fil_ppf.nof_bands * (2**g_fil_ppf.nof_chan)); + constant c_coef_mem_addr_w : natural := ceil_log2(g_fil_ppf.nof_bands); + constant c_taps_mem_delay : natural := g_fil_ppf_pipeline.mem_delay; + constant c_coef_mem_delay : natural := g_fil_ppf_pipeline.mem_delay; + constant c_taps_mem_data_w : natural := g_fil_ppf.in_dat_w*g_fil_ppf.nof_taps; + constant c_coef_mem_data_w : natural := g_fil_ppf.coef_dat_w; + + constant c_taps_mem : t_c_mem := (latency => c_taps_mem_delay, + adr_w => c_taps_mem_addr_w, + dat_w => c_taps_mem_data_w, + nof_dat => g_fil_ppf.nof_bands * (2**g_fil_ppf.nof_chan), + init_sl => '0'); -- use '0' instead of 'X' to avoid RTL RAM simulation warnings due to read before write + + constant c_coef_mem : t_c_mem := (latency => c_coef_mem_delay, + adr_w => c_coef_mem_addr_w, + dat_w => c_coef_mem_data_w, + nof_dat => g_fil_ppf.nof_bands, + init_sl => '0'); -- use '0' instead of 'X' to avoid RTL RAM simulation warnings due to read before write + + signal ram_coefs_mosi_arr : t_mem_mosi_arr(g_fil_ppf.nof_taps-1 downto 0); + signal ram_coefs_miso_arr : t_mem_miso_arr(g_fil_ppf.nof_taps-1 downto 0) := (others => c_mem_miso_rst); + signal taps_wren : std_logic; + signal taps_rdaddr : std_logic_vector(c_taps_mem_addr_w-1 downto 0); + signal taps_wraddr : std_logic_vector(c_taps_mem_addr_w-1 downto 0); + signal taps_mem_out_vec : std_logic_vector(c_taps_mem_data_w*g_fil_ppf.nof_streams-1 downto 0); + signal taps_mem_in_vec : std_logic_vector(c_taps_mem_data_w*g_fil_ppf.nof_streams-1 downto 0); + signal coef_rdaddr : std_logic_vector(c_coef_mem_addr_w-1 downto 0); + signal coef_vec : std_logic_vector(c_coef_mem_data_w*g_fil_ppf.nof_taps-1 downto 0); + +begin + + --------------------------------------------------------------- + -- MEMORY FOR THE HISTORICAL TAP DATA + --------------------------------------------------------------- + gen_taps_mems : for I in 0 to g_fil_ppf.nof_streams-1 generate + u_taps_mem : entity common_lib.common_ram_r_w + generic map ( + g_ram => c_taps_mem, + g_init_file => "UNUSED" -- assume block RAM gets initialized to '0' by default in simulation + ) + port map ( + rst => dp_rst, + clk => dp_clk, + wr_en => taps_wren, + wr_adr => taps_wraddr, + wr_dat => taps_mem_in_vec((I+1)*c_taps_mem_data_w-1 downto I*c_taps_mem_data_w), + rd_en => '1', + rd_adr => taps_rdaddr, + rd_dat => taps_mem_out_vec((I+1)*c_taps_mem_data_w-1 downto I*c_taps_mem_data_w), + rd_val => open + ); + end generate; + --------------------------------------------------------------- + -- COMBINE MEMORY MAPPED INTERFACES + --------------------------------------------------------------- + -- Combine the internal array of mm interfaces for the coefficents + -- memory to one array that is connected to the port of the fil_ppf + u_mem_mux_coef : entity common_lib.common_mem_mux + generic map ( + g_nof_mosi => g_fil_ppf.nof_taps, + g_mult_addr_w => c_coef_mem_addr_w + ) + port map ( + mosi => ram_coefs_mosi, + miso => ram_coefs_miso, + mosi_arr => ram_coefs_mosi_arr, + miso_arr => ram_coefs_miso_arr + ); + + --------------------------------------------------------------- + -- GENERATE THE COEFFICIENT MEMORIES + --------------------------------------------------------------- + -- For every tap a unique memory is instantiated that holds + -- the corresponding coefficients for all the bands. + gen_coef_mems : for I in 0 to g_fil_ppf.nof_taps-1 generate + u_coef_mem : entity common_lib.common_ram_crw_crw + generic map ( + g_ram => c_coef_mem, + -- Sequence number and ".hex" extensie are added to the relative path in case a ram file is provided. + g_init_file => sel_a_b(g_coefs_file_prefix = "UNUSED", g_coefs_file_prefix, g_coefs_file_prefix & "_" & NATURAL'IMAGE(g_file_index_arr(I)) & c_coefs_postfix) + ) + port map ( + -- MM side + rst_a => mm_rst, + clk_a => mm_clk, + wr_en_a => ram_coefs_mosi_arr(I).wr, + wr_dat_a => ram_coefs_mosi_arr(I).wrdata(g_fil_ppf.coef_dat_w-1 downto 0), + adr_a => ram_coefs_mosi_arr(I).address(c_coef_mem.adr_w-1 downto 0), + rd_en_a => ram_coefs_mosi_arr(I).rd, + rd_dat_a => ram_coefs_miso_arr(I).rddata(g_fil_ppf.coef_dat_w-1 downto 0), + rd_val_a => ram_coefs_miso_arr(I).rdval, + -- Datapath side + rst_b => dp_rst, + clk_b => dp_clk, + wr_en_b => '0', + wr_dat_b => (others =>'0'), + adr_b => coef_rdaddr, + rd_en_b => '1', + rd_dat_b => coef_vec((I+1)*c_coef_mem_data_w-1 downto I*c_coef_mem_data_w), + rd_val_b => open + ); + end generate; + + -- Address the coefficients, taking into account the nof_chan. The coefficients will only be + -- updated if all 2**nof_chan time-multiples signals are processed. + coef_rdaddr <= taps_rdaddr(c_taps_mem_addr_w-1 downto (c_taps_mem_addr_w - c_coef_mem_addr_w)); + + --------------------------------------------------------------- + -- FILTER CONTROL UNIT + --------------------------------------------------------------- + -- The control unit receives the input data and writes it to + -- the tap memory, along with the historical tap data. + -- It also controls the reading of the coefficients memory. + u_fil_ctrl : entity work.fil_ppf_ctrl + generic map ( + g_fil_ppf_pipeline => g_fil_ppf_pipeline, + g_fil_ppf => g_fil_ppf + ) + port map ( + clk => dp_clk, + rst => dp_rst, + in_dat => in_dat, + in_val => in_val, + taps_rdaddr => taps_rdaddr, + taps_wraddr => taps_wraddr, + taps_wren => taps_wren, + taps_in_vec => taps_mem_out_vec, + taps_out_vec => taps_mem_in_vec, + out_val => out_val + ); + + --------------------------------------------------------------- + -- FILTER UNIT + --------------------------------------------------------------- + -- The actual filter unit that performs the filter operations: + -- multiplications and additions. + gen_filter_units : for I in 0 to g_fil_ppf.nof_streams-1 generate + u_filter : entity work.fil_ppf_filter + generic map ( + g_fil_ppf_pipeline => g_fil_ppf_pipeline, + g_fil_ppf => g_fil_ppf + ) + port map ( + clk => dp_clk, + rst => dp_rst, + taps => taps_mem_out_vec((I+1)*c_taps_mem_data_w-1 downto I*c_taps_mem_data_w), + coefs => coef_vec, + result => out_dat((I+1)*g_fil_ppf.out_dat_w-1 downto I*g_fil_ppf.out_dat_w) + ); + end generate; + +end rtl;