--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: PkgRf
--
-- Description:
--
--   This package has some type definition and functions used in the RF data
--   chain.
--

library IEEE;
  use IEEE.std_logic_1164.all;
  use IEEE.numeric_std.all;

package PkgRf is

  -- DDC sample data out width.
  constant kDdcDataOutWidth  : natural := 17;
  -- Each sample is padded in MSB with 7 extra bits of zero to byte align.
  constant kDdcDataWordWidth : natural := kDdcDataOutWidth+7;
  -- DUC sample data out width.
  constant kDucDataOutWidth  : natural := 18;
  -- Each sample is padded in MSB with 6 extra bits of zero to byte align.
  constant kDucDataWordWidth : natural := kDucDataOutWidth+6;
  -- Saturated data output width.
  constant kSatDataWidth     : natural := 16;
  -- ADC sample resolution.
  constant kAdcSampleRes     : natural := 16;

  subtype Sample18_t is    signed(17 downto 0);
  subtype Sample17_t is    signed(16 downto 0);
  subtype Sample16_t is    signed(15 downto 0);
  subtype Sample16slv_t is std_logic_vector(15 downto 0);

  type Samples16_t is array(natural range<>) of Sample16_t;
  type Samples17_t is array(natural range<>) of Sample17_t;
  type Samples18_t is array(natural range<>) of Sample18_t;

  -- These constants have the largest and smallest 18-bit, 17-bit, and 16-bit
  -- signed values.
  constant kLargest18  : Sample18_t := to_signed(2**17 - 1, 18);
  constant kSmallest18 : Sample18_t := to_signed(-2**17, 18);
  constant kLargest17  : Sample17_t := to_signed(2**16 - 1, 17);
  constant kSmallest17 : Sample17_t := to_signed(-2**16, 17);
  constant kLargest16  : Sample16_t := to_signed(2**15 - 1, 16);
  constant kSmallest16 : Sample16_t := to_signed(-2**15, 16);

  function Saturate(s : signed ) return Sample16_t;
  function to_stdlogicvector(d : Samples16_t) return std_logic_vector;
  function to_Samples16(d : std_logic_vector) return Samples16_t;
  function to_Samples17(d : std_logic_vector) return Samples17_t;
  function to_Samples18(d : std_logic_vector) return Samples18_t;
  -- Shift the ADC sample to the left by 1 bit.
  function Gain2x(d : std_logic_vector) return std_logic_vector;
  function Gain2x(s : Samples16_t) return Samples16_t;
  --synopsys translate_off
  function tb_saturate(s: std_logic_vector) return Sample16slv_t;
  --synopsys translate_on

end package PkgRf;


package body PkgRf is

  -- Function to saturate any signed number greater then 16 bits.
  -- A saturated 16-bit data is returned.
  function Saturate ( s : signed) return Sample16_t is
  begin
    if s > kLargest16 then
      return kLargest16;
    elsif s < kSmallest16 then
      return kSmallest16;
    else
      return resize(s, 16);
    end if;
  end function Saturate;

  -- This function will convert 16 bit signed array into a single
  -- std_logic_vector.
  function to_stdlogicvector(d : Samples16_t) return std_logic_vector is
    -- This alias is used to normalize the input vector to [d'length-1 downto 0]
    alias normalD : Samples16_t(d'length-1 downto 0) is d;
    variable rval : std_logic_vector(d'length * 16 - 1 downto 0);
    constant dataWidth : natural := Sample16_t'length;
  begin
    for i in normalD'range loop
      rval(i*dataWidth + dataWidth-1 downto i*dataWidth)
        := std_logic_vector(normalD(i));
    end loop;
    return rval;
  end function to_stdlogicvector;

  -- This function will convert a std_logic_vector into an array of 18 bit
  -- signed array.  The input std_logic_vector has data packed in 24 bits. But
  -- only 18 bits has valid data and remaining 6 MSB bits are padded with
  -- zeros.
  function to_Samples18(d : std_logic_vector) return Samples18_t is
    -- This alias is used to normalize the input vector to [d'length-1 downto 0]
    alias normalD : std_logic_vector(d'length-1 downto 0) is d;
    variable rval : Samples18_t(d'length / kDucDataWordWidth - 1 downto 0);
  begin
    --synopsys translate_off
    assert (((d'length) mod kDucDataWordWidth) = 0)
      report "Input to the function to_Samples18 must be a multiple of kDucDataWordWidth"
      severity error;
    --synopsys translate_on
    for i in rval'range loop
      rval(i) := Sample18_t(normalD(i*kDucDataWordWidth + Sample18_t'length-1
                                    downto i*kDucDataWordWidth));
    end loop;
    return rval;
  end function to_Samples18;

  -- This function will convert a std_logic_vector into an array of 16 bit
  -- signed array. The input std_logic_vector has data packed in 16 bits. But
  -- only 15 bits has valid data and the uper two bits only have the signed
  -- bit.
  function to_Samples16(d : std_logic_vector) return Samples16_t is
    -- This alias is used to normalize the input vector to [d'length-1 downto 0]
    alias normalD : std_logic_vector(d'length-1 downto 0) is d;
    variable rval : Samples16_t(d'length / kAdcSampleRes - 1 downto 0);
  begin
    --synopsys translate_off
    assert (((d'length) mod kAdcSampleRes) = 0)
      report "Input to the function to_Samples16 must be a multiple of kAdcSampleRes"
      severity error;
    --synopsys translate_on
    for i in rval'range loop
      rval(i) := Sample16_t(normalD(i*kAdcSampleRes + Sample16_t'length-1
                                    downto i*kAdcSampleRes));
    end loop;
    return rval;
  end function to_Samples16;

  -- This function will convert a std_logic_vector into an array of 19 bit
  -- signed array. The input std_logic_vector has data packed in 24 bits. But
  -- only 17 bits has valid data and remaining 7 MSB bits are padded with
  -- zeros.
  function to_Samples17(d : std_logic_vector) return Samples17_t is
    -- This alias is used to normalize the input vector to [d'length-1 downto 0]
    alias normalD : std_logic_vector(d'length-1 downto 0) is d;
    variable rval : Samples17_t(d'length / kDdcDataWordWidth - 1 downto 0);
  begin
    --synopsys translate_off
    assert (((d'length) mod kDdcDataWordWidth) = 0)
      report "Input to the function to_Samples17 must be a multiple of kDdcDataWordWidth"
      severity error;
    --synopsys translate_on
    for i in rval'range loop
      rval(i) := Sample17_t(normalD(i*kDdcDataWordWidth + Sample17_t'length-1
                                    downto i*kDdcDataWordWidth));
    end loop;
    return rval;
  end function to_Samples17;

  -- Function to shift the sample to the left by one bit and effectively
  -- multiply by 2.
  function Gain2x(s : Samples16_t) return Samples16_t is
    variable rval : Samples16_t(s'range);
  begin
    for i in rval'range loop
      rval(i) := s(i)(kAdcSampleRes-2 downto 0) & '0';
    end loop;
    return rval;
  end function Gain2x;

  function Gain2x (d : std_logic_vector) return std_logic_vector is
  begin
    return to_stdlogicvector(Gain2x(to_Samples16(d)));
  end function;

  --synopsys translate_off
  ---------------------------------------------------------------
  -- Function below this comment is used only for testbench.
  ---------------------------------------------------------------
  -- This function does saturation of a signed number in std_logic_vector data
  -- type. The current implementation supports only 17 or 18 bit signed
  -- number.
  function tb_saturate(s: std_logic_vector) return Sample16slv_t is
    -- This alias is used to normalize the input vector to [s'length-1 downto 0]
    alias normalS : std_logic_vector(s'length-1 downto 0) is s;
    variable rval : Sample16slv_t;
    constant len  : integer := s'length;
  begin

    -- If 2 MSBs = 00, output <= input without MSB, e.g. positive number < 1
    -- If 2 MSBs = 01, output <= 0.111111111111111, e.g. positive number >= 1
    -- If 2 MSBs = 10, output <= 1.000000000000000, e.g. negative number < -1
    -- If 2 MSBs = 11, output <= input without MSB, e.g. negative number >= -1
    if len = kDdcDataOutWidth then
      if normalS(len-1 downto len-2) = "01" then
        rval := "0111111111111111";
      elsif normalS(len-1 downto len-2) = "10" then
        rval := "1000000000000000";
      else
        rval := normalS(len-2 downto 0);
      end if;

    -- If 3 MSBs = 000, output <= input without MSB, e.g. positive number < 1
    -- If 3 MSBs = 0x1/01x, output <= 0.111111111111111, e.g. positive number >= 1
    -- If 3 MSBs = 1x0/10x, output <= 1.000000000000000, e.g. negative number < -1
    -- If 3 MSBs = 111, output <= input without MSB, e.g. negative number >= -1
    else -- len = kDucDataOutWidth
      if normalS(len-1) = '0' and normalS(len-2 downto len-3) /= "00" then
        rval := "0111111111111111";
      elsif (normalS(len-1 downto len-3) = "000") or
            (normalS(len-1 downto len-3) = "111") then
        rval := normalS(len-3 downto 0);
      else
        rval := "1000000000000000";
      end if;
    end if;
    return rval;
  end function tb_saturate;
  --synopsys translate_on

end package body;