Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
sdp_subband_equalizer.vhd 9.79 KiB
-------------------------------------------------------------------------------
--
-- Copyright 2020
-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
--
-- Author: R. van der Walle
-- Purpose:
-- . Implements the functionality of the subband equalizer in the subband
--   filterbank (Fsub) of the LOFAR2 SDPFW design.
-- Description:
-- . Block diagram:
--
--   in_raw ----> pipeline    --> * co-pol weights    --+-----> Q --> out_raw
--            \-> reverse pol --> * cross-pol weights --/  \--> Q --> out_quant
--
-- . The sdp_subband_equalizer.vhd consists of mms_dp_gain_serial_arr.vhd and
--   some address counter logic to select the address of the subband weight
--   and a dp_requantize.vhd component.
-- . The subband equalizer can calibrate for co-polarization using only the
--   co-polarization weights from ram_gains_mosi. Or it can do full Jones
--   matrix calibration using also the cross polarization weights from
--   ram_gains_cross_mosi.
-- . Default the co-polarization weights are read from g_gains_file_name and
--   default the cross polarization weights are 0.
-- . Subband widths:
--   - raw_sosi   : g_raw_dat_w bits
--   - quant_sosi : c_quant_dat_w = g_raw_dat_w - g_raw_fraction_w bits
-- Remark:
-- . If the latency of sdp_subband_equalizer.vhd is changed, then
--   c_sdp_subband_equalizer_latency in sdp_pkg.vhd needs to be updated.
--
-------------------------------------------------------------------------------

library IEEE, common_lib, dp_lib;
use IEEE.std_logic_1164.all;
use common_lib.common_pkg.all;
use common_lib.common_mem_pkg.all;
use dp_lib.dp_stream_pkg.all;
use work.sdp_pkg.all;

entity sdp_subband_equalizer is
  generic (
    g_gains_file_name    : string := "UNUSED";  -- for co-polarization
    g_nof_streams        : natural := c_sdp_P_pfb;
    -- Use no default raw width, to force instance to set it
    g_raw_dat_w          : natural;  -- default: c_sdp_W_subband;
    g_raw_fraction_w     : natural  -- default: 0
  );
  port (
    dp_clk       : in  std_logic;
    dp_rst       : in  std_logic;

    in_raw_sosi_arr    : in  t_dp_sosi_arr(g_nof_streams - 1 downto 0);
    out_raw_sosi_arr   : out t_dp_sosi_arr(g_nof_streams - 1 downto 0);
    out_quant_sosi_arr : out t_dp_sosi_arr(g_nof_streams - 1 downto 0);

    mm_rst       : in  std_logic;
    mm_clk       : in  std_logic;

    ram_gains_mosi : in  t_mem_mosi := c_mem_mosi_rst;
    ram_gains_miso : out t_mem_miso;

    ram_gains_cross_mosi : in  t_mem_mosi := c_mem_mosi_rst;
    ram_gains_cross_miso : out t_mem_miso
  );
end sdp_subband_equalizer;

architecture str of sdp_subband_equalizer is
  constant c_gain_addr_w : natural := ceil_log2(c_sdp_Q_fft * c_sdp_N_sub);

  -- Product width, do -1 to skip double sign bit in product
  constant c_gain_out_dat_w : natural := c_sdp_W_sub_weight + g_raw_dat_w - 1;

  constant c_quant_dat_w : natural := g_raw_dat_w - g_raw_fraction_w;

  -- Pipeline to easy timing closure
  constant c_pipeline_remove_lsb : natural := 1;
  constant c_pipeline_remove_msb : natural := 1;

  signal in_pipe_raw_sosi_arr        : t_dp_sosi_arr(g_nof_streams - 1 downto 0);
  signal in_cross_raw_sosi_arr       : t_dp_sosi_arr(g_nof_streams - 1 downto 0);
  signal in_raw_sosi_2arr_2          : t_dp_sosi_2arr_2(g_nof_streams - 1 downto 0);

  signal weighted_raw_sosi_arr       : t_dp_sosi_arr(g_nof_streams - 1 downto 0);
  signal weighted_cross_raw_sosi_arr : t_dp_sosi_arr(g_nof_streams - 1 downto 0);

  signal sum_raw_sosi_arr            : t_dp_sosi_arr(g_nof_streams - 1 downto 0);
begin
  -----------------------------------------------------------------------------
  -- Prepare co and cross input
  -----------------------------------------------------------------------------

  -- Total pipeline of u_reverse_pol is:
  --   g_pipeline_demux_in + g_pipeline_demux_out +
  --   g_reverse_len - 1 +
  --   g_pipeline_mux_in + g_pipeline_mux_out = 1 + 0 + 2-1 + 0 + 1 = 3
  u_pipeline_co_pol : entity dp_lib.dp_pipeline_arr
  generic map (
    g_nof_streams => g_nof_streams,
    g_pipeline    => 3
  )
  port map (
    rst         => dp_rst,
    clk         => dp_clk,
    -- ST sink
    snk_in_arr  => in_raw_sosi_arr,
    -- ST source
    src_out_arr => in_pipe_raw_sosi_arr
  );

  -- The input subband data order is fsub[S_pn/Q_fft]_[N_sub][Q_fft] and
  -- the [Q_fft] = [N_pol] index contains the X and Y polarizations.
  -- Reverse the serial [N_pol] input polarizations to have the cross
  -- polarization aligned with the co polarizations in
  -- in_pipeline_raw_sosi_arr.
  gen_cross_pol : for I in 0 to g_nof_streams - 1 generate
    u_cross_pol : entity dp_lib.dp_reverse_n_data
    generic map (
      g_pipeline_demux_in  => 1,  -- serial to parallel section
      g_pipeline_demux_out => 0,
      g_pipeline_mux_in    => 0,  -- parallel to serial section
      g_pipeline_mux_out   => 1,
      g_reverse_len        => c_sdp_N_pol,  -- = 2
      g_data_w             => g_raw_dat_w * c_nof_complex,
      g_use_complex        => true,
      g_signed             => true
    )
    port map (
      rst         => dp_rst,
      clk         => dp_clk,

      snk_in      => in_raw_sosi_arr(I),
      src_out     => in_cross_raw_sosi_arr(I)
    );
  end generate;

  -----------------------------------------------------------------------------
  -- Apply co and cross weights
  -----------------------------------------------------------------------------
  -- Total pipeline of sdp_subband_weights is: 5
  u_sdp_subband_weigths : entity work.sdp_subband_weights
  generic map (
    g_gains_file_name => g_gains_file_name,  -- for co polarization
    g_nof_streams     => g_nof_streams,
    g_raw_dat_w       => g_raw_dat_w
  )
  port map (
    dp_clk => dp_clk,
    dp_rst => dp_rst,

    in_raw_sosi_arr       => in_pipe_raw_sosi_arr,
    in_cross_raw_sosi_arr => in_cross_raw_sosi_arr,

    weighted_raw_sosi_arr       => weighted_raw_sosi_arr,
    weighted_cross_raw_sosi_arr => weighted_cross_raw_sosi_arr,

    mm_rst => mm_rst,
    mm_clk => mm_clk,

    ram_gains_mosi => ram_gains_mosi,
    ram_gains_miso => ram_gains_miso,

    ram_gains_cross_mosi => ram_gains_cross_mosi,
    ram_gains_cross_miso => ram_gains_cross_miso
  );

  -----------------------------------------------------------------------------
  -- Sum co + cross
  -----------------------------------------------------------------------------
  -- Total pipeline of u_dp_complex_add is: 1

  gen_dp_complex_add : for I in 0 to g_nof_streams - 1 generate
    in_raw_sosi_2arr_2(I)(0) <= weighted_raw_sosi_arr(I);
    in_raw_sosi_2arr_2(I)(1) <= weighted_cross_raw_sosi_arr(I);

    u_dp_complex_add : entity dp_lib.dp_complex_add
    generic map(
      g_nof_inputs => c_sdp_N_pol,
      g_data_w => c_gain_out_dat_w
    )
    port map(
      rst   => dp_rst,
      clk   => dp_clk,

      snk_in_arr => in_raw_sosi_2arr_2(I),
      src_out => sum_raw_sosi_arr(I)
    );
  end generate;

  -----------------------------------------------------------------------------
  -- Requantize
  -----------------------------------------------------------------------------
  -- Total pipeline of requantize is:
  --   c_pipeline_remove_lsb + c_pipeline_remove_lsb = 1 + 1 = 2

  gen_dp_requantize : for I in 0 to g_nof_streams - 1 generate
    -- For raw output only round the c_sdp_W_sub_weight_fraction, and keep the
    -- g_raw_fraction_w, so that the output width remains the same as the input
    -- width g_raw_dat_w.
    u_dp_requantize_out_raw : entity dp_lib.dp_requantize
    generic map (
      g_complex             => true,
      g_representation      => "SIGNED",
      g_lsb_w               => c_sdp_W_sub_weight_fraction,
      g_lsb_round           => true,
      g_lsb_round_clip      => false,
      g_msb_clip            => true,  -- clip subband overflow
      g_msb_clip_symmetric  => false,
      g_pipeline_remove_lsb => c_pipeline_remove_lsb,
      g_pipeline_remove_msb => c_pipeline_remove_msb,
      g_in_dat_w            => c_gain_out_dat_w,
      g_out_dat_w           => g_raw_dat_w
    )
    port map (
      rst          => dp_rst,
      clk          => dp_clk,
      -- ST sink
      snk_in       => sum_raw_sosi_arr(I),
      -- ST source
      src_out      => out_raw_sosi_arr(I)
    );

    -- For quant output round the entire fraction, so that the output width
    -- becomes c_quant_dat_w.
    u_dp_requantize_out_quant : entity dp_lib.dp_requantize
    generic map (
      g_complex             => true,
      g_representation      => "SIGNED",
      g_lsb_w               => c_sdp_W_sub_weight_fraction + g_raw_fraction_w,
      g_lsb_round           => true,
      g_lsb_round_clip      => false,
      g_msb_clip            => true,  -- clip subband overflow
      g_msb_clip_symmetric  => false,
      g_pipeline_remove_lsb => c_pipeline_remove_lsb,
      g_pipeline_remove_msb => c_pipeline_remove_msb,
      g_in_dat_w            => c_gain_out_dat_w,
      g_out_dat_w           => c_quant_dat_w
    )
    port map (
      rst          => dp_rst,
      clk          => dp_clk,
      -- ST sink
      snk_in       => sum_raw_sosi_arr(I),
      -- ST source
      src_out      => out_quant_sosi_arr(I)
    );
  end generate;
end str;