------------------------------------------------------------------------------
--  This file is a part of the GRLIB VHDL IP LIBRARY
--  Copyright (C) 2003 - 2008, Gaisler Research
--  Copyright (C) 2008 - 2014, Aeroflex Gaisler
--  Copyright (C) 2015 - 2017, Cobham Gaisler
--
--  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 2 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, write to the Free Software
--  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
-----------------------------------------------------------------------------
-- Package: 	stdlib
-- File:	stdlib.vhd
-- Author:	Jiri Gaisler - Gaisler Research
-- Description:	Package for common VHDL functions
------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
-- pragma translate_off
use std.textio.all;
-- pragma translate_on
library grlib;
use grlib.version.all;

package stdlib is

constant LIBVHDL_VERSION : integer := grlib_version;
constant LIBVHDL_BUILD : integer := grlib_build;
constant LIBVHDL_CFGVER : integer := 0;

constant zero32 : std_logic_vector(31 downto 0) := (others => '0');
constant zero64 : std_logic_vector(63 downto 0) := (others => '0');
constant zero128 : std_logic_vector(127 downto 0) := (others => '0');
constant one32  : std_logic_vector(31 downto 0) := (others => '1');
constant one64  : std_logic_vector(63 downto 0) := (others => '1');
constant one128  : std_logic_vector(127 downto 0) := (others => '1');

type log2arr is array(0 to 512) of integer;
constant log2   : log2arr := (
0,0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
  6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
  8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
  8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
  8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
  8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
  others => 9);
constant log2x  : log2arr := (
0,1,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
  6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
  8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
  8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
  8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
  8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
  others => 9);
function log2ext(i: integer) return integer;

function decode(v : std_logic_vector) return std_logic_vector;
function genmux(s,v : std_logic_vector) return std_ulogic;
function xorv(d : std_logic_vector) return std_ulogic;
function orv(d : std_logic_vector) return std_ulogic;
function andv(d : std_logic_vector) return std_ulogic;
function notx(d : std_logic_vector) return boolean;
function notx(d : std_ulogic) return boolean;
function "-" (d : std_logic_vector; i : integer) return std_logic_vector;
function "-" (i : integer; d : std_logic_vector) return std_logic_vector;
function "+" (d : std_logic_vector; i : integer) return std_logic_vector;
function "+" (i : integer; d : std_logic_vector) return std_logic_vector;
function "-" (d : std_logic_vector; i : std_ulogic) return std_logic_vector;
function "+" (d : std_logic_vector; i : std_ulogic) return std_logic_vector;
function "-" (a, b : std_logic_vector) return std_logic_vector;
function "+" (a, b : std_logic_vector) return std_logic_vector;
function "*" (a, b : std_logic_vector) return std_logic_vector;
function unsigned_mul (a, b : std_logic_vector) return std_logic_vector;
function signed_mul (a, b : std_logic_vector) return std_logic_vector;
function mixed_mul (a, b : std_logic_vector; sign : std_logic) return std_logic_vector;
--function ">" (a, b : std_logic_vector) return boolean;
function "<" (i : integer; b : std_logic_vector) return boolean;
function conv_integer(v : std_logic_vector) return integer;
function conv_integer(v : std_logic) return integer;
function conv_std_logic_vector(i : integer; w : integer) return std_logic_vector;
function conv_std_logic_vector_signed(i : integer; w : integer) return std_logic_vector;
function conv_std_logic(b : boolean) return std_ulogic;
attribute sync_set_reset : string;
attribute async_set_reset : string;

-- Reporting and diagnostics

-- pragma translate_off

function tost(v:std_logic_vector) return string;
function tost(v:std_logic) return string;
function tost(i : integer) return string;
function tost_any(s: std_ulogic) return string;
function tost_bits(s: std_logic_vector) return string;
function tost(b: boolean) return string;
function tost(r: real) return string;
procedure print(s : string);

component report_version
  generic (msg1, msg2, msg3, msg4 : string := ""; mdel : integer := 4);
end component;

component report_design
  generic (msg1, fabtech, memtech : string := ""; mdel : integer := 4);
end component;

-- pragma translate_on

function unary_to_slv(i: std_logic_vector) return std_logic_vector;
function gray_encoder(idata : in std_logic_vector) return std_logic_vector;
function gray_decoder(idata : in std_logic_vector) return std_logic_vector;

end;

package body stdlib is

function notx(d : std_logic_vector) return boolean is
variable res : boolean;
begin
  res := true;
-- pragma translate_off
  res := not is_x(d);
-- pragma translate_on
  return (res);
end;

function notx(d : std_ulogic) return boolean is
variable res : boolean;
begin
  res := true;
-- pragma translate_off
  res := not is_x(d);
-- pragma translate_on
  return (res);
end;

-- generic decoder

function decode(v : std_logic_vector) return std_logic_vector is
variable res : std_logic_vector((2**v'length)-1 downto 0);
variable i : integer range res'range;
begin
  res := (others => '0'); i := 0;
  if notx(v) then i := to_integer(unsigned(v)); end if;
  res(i) := '1';
  return(res);
end;

-- generic multiplexer

function genmux(s,v : std_logic_vector) return std_ulogic is
variable res : std_logic_vector(v'length-1 downto 0);
variable i : integer range res'range;
begin
  res := v; i := 0;
  if notx(s) then i := to_integer(unsigned(s)); end if;
  return(res(i));
end;

-- vector XOR

function xorv(d : std_logic_vector) return std_ulogic is
variable tmp : std_ulogic;
begin
  tmp := '0';
  for i in d'range loop tmp := tmp xor d(i); end loop;
  return(tmp);
end;

-- vector OR

function orv(d : std_logic_vector) return std_ulogic is
variable tmp : std_ulogic;
begin
  tmp := '0';
  for i in d'range loop tmp := tmp or d(i); end loop;
  return(tmp);
end;

-- vector AND

function andv(d : std_logic_vector) return std_ulogic is
variable tmp : std_ulogic;
begin
  tmp := '1';
  for i in d'range loop tmp := tmp and d(i); end loop;
  return(tmp);
end;

-- unsigned multiplication

function "*" (a, b : std_logic_vector) return std_logic_vector is
variable z : std_logic_vector(a'length+b'length-1 downto 0);
begin
-- pragma translate_off
  if notx(a&b) then
-- pragma translate_on
    return(std_logic_vector(unsigned(a) * unsigned(b)));
-- pragma translate_off
  else
     z := (others =>'X'); return(z);
  end if;
-- pragma translate_on
end;

-- signed multiplication

function signed_mul (a, b : std_logic_vector) return std_logic_vector is
variable z : std_logic_vector(a'length+b'length-1 downto 0);
begin
-- pragma translate_off
  if notx(a&b) then
-- pragma translate_on
    return(std_logic_vector(signed(a) * signed(b)));
-- pragma translate_off
  else
     z := (others =>'X'); return(z);
  end if;
-- pragma translate_on
end;

-- unsigned multiplication

function unsigned_mul (a, b : std_logic_vector) return std_logic_vector is
variable z : std_logic_vector(a'length+b'length-1 downto 0);
begin
-- pragma translate_off
  if notx(a&b) then
-- pragma translate_on
    return(std_logic_vector(unsigned(a) * unsigned(b)));
-- pragma translate_off
  else
     z := (others =>'X'); return(z);
  end if;
-- pragma translate_on
end;

-- signed/unsigned multiplication

function mixed_mul (a, b : std_logic_vector; sign : std_logic) return std_logic_vector is
variable z : std_logic_vector(a'length+b'length-1 downto 0);
begin
-- pragma translate_off
  if notx(a&b) then
-- pragma translate_on
    if sign = '0' then
      return(std_logic_vector(unsigned(a) * unsigned(b)));
    else
      return(std_logic_vector(signed(a) * signed(b)));
    end if;
-- pragma translate_off
  else
     z := (others =>'X'); return(z);
  end if;
-- pragma translate_on
end;

-- unsigned addition

function "+" (a, b : std_logic_vector) return std_logic_vector is
variable x : std_logic_vector(a'length-1 downto 0);
variable y : std_logic_vector(b'length-1 downto 0);
begin
-- pragma translate_off
  if notx(a&b) then
-- pragma translate_on
    return(std_logic_vector(unsigned(a) + unsigned(b)));
-- pragma translate_off
  else
     x := (others =>'X'); y := (others =>'X');
     if (x'length > y'length) then return(x); else return(y); end if;
  end if;
-- pragma translate_on
end;

function "+" (i : integer; d : std_logic_vector) return std_logic_vector is
variable x : std_logic_vector(d'length-1 downto 0);
begin
-- pragma translate_off
  if notx(d) then
-- pragma translate_on
    return(std_logic_vector(unsigned(d) + i));
-- pragma translate_off
  else x := (others =>'X'); return(x);
  end if;
-- pragma translate_on
end;

function "+" (d : std_logic_vector; i : integer) return std_logic_vector is
variable x : std_logic_vector(d'length-1 downto 0);
begin
-- pragma translate_off
  if notx(d) then
-- pragma translate_on
    return(std_logic_vector(unsigned(d) + i));
-- pragma translate_off
  else x := (others =>'X'); return(x);
  end if;
-- pragma translate_on
end;

function "+" (d : std_logic_vector; i : std_ulogic) return std_logic_vector is
variable x : std_logic_vector(d'length-1 downto 0);
variable y : std_logic_vector(0 downto 0);
begin
  y(0) := i;
-- pragma translate_off
  if notx(d) then
-- pragma translate_on
    return(std_logic_vector(unsigned(d) + unsigned(y)));
-- pragma translate_off
  else x := (others =>'X'); return(x); 
  end if;
-- pragma translate_on
end;

-- unsigned subtraction

function "-" (a, b : std_logic_vector) return std_logic_vector is
variable x : std_logic_vector(a'length-1 downto 0);
variable y : std_logic_vector(b'length-1 downto 0);
begin
-- pragma translate_off
  if notx(a&b) then
-- pragma translate_on
    return(std_logic_vector(unsigned(a) - unsigned(b)));
-- pragma translate_off
  else
     x := (others =>'X'); y := (others =>'X');
     if (x'length > y'length) then return(x); else return(y); end if; 
  end if;
-- pragma translate_on
end;

function "-" (d : std_logic_vector; i : integer) return std_logic_vector is
variable x : std_logic_vector(d'length-1 downto 0);
begin
-- pragma translate_off
  if notx(d) then
-- pragma translate_on
    return(std_logic_vector(unsigned(d) - i));
-- pragma translate_off
  else x := (others =>'X'); return(x); 
  end if;
-- pragma translate_on
end;

function "-" (i : integer; d : std_logic_vector) return std_logic_vector is
variable x : std_logic_vector(d'length-1 downto 0);
begin
-- pragma translate_off
  if notx(d) then
-- pragma translate_on
    return(std_logic_vector(i - unsigned(d)));
-- pragma translate_off
  else x := (others =>'X'); return(x); 
  end if;
-- pragma translate_on
end;

function "-" (d : std_logic_vector; i : std_ulogic) return std_logic_vector is
variable x : std_logic_vector(d'length-1 downto 0);
variable y : std_logic_vector(0 downto 0);
begin
  y(0) := i;
-- pragma translate_off
  if notx(d) then
-- pragma translate_on
    return(std_logic_vector(unsigned(d) - unsigned(y)));
-- pragma translate_off
  else x := (others =>'X'); return(x); 
  end if;
-- pragma translate_on
end;

function ">=" (a, b : std_logic_vector) return boolean is
begin
  return(unsigned(a) >= unsigned(b));
end;

function "<" (i : integer; b : std_logic_vector) return boolean is
begin
  return( i < to_integer(unsigned(b)));
end;

function ">" (a, b : std_logic_vector) return boolean is
begin
  return(unsigned(a) > unsigned(b));
end;

function conv_integer(v : std_logic_vector) return integer is
begin
  if notx(v) then return(to_integer(unsigned(v)));
  else return(0); end if;
end;

function conv_integer(v : std_logic) return integer is
begin
  if notx(v) then
    if v = '1' then return(1);
    else return(0); end if;
  else return(0); end if;
end;

function conv_std_logic_vector(i : integer; w : integer) return std_logic_vector is
variable tmp : std_logic_vector(w-1 downto 0);
begin
  tmp := std_logic_vector(to_unsigned(i, w));
  return(tmp);
end;

function conv_std_logic_vector_signed(i : integer; w : integer) return std_logic_vector is
variable tmp : std_logic_vector(w-1 downto 0);
begin
  tmp := std_logic_vector(to_signed(i, w));
  return(tmp);
end;

function conv_std_logic(b : boolean) return std_ulogic is
begin
  if b then return('1'); else return('0'); end if;
end;

function log2ext(i: integer) return integer is
--  variable v: std_logic_vector(31 downto 0);
begin
-- workaround for DC bug
--  if i=0 then return 0; end if;
--  v := std_logic_vector(to_unsigned((i-1),v'length));
--  for x in v'high downto v'low loop
--    if v(x)='1' then return x+1; end if;
--  end loop;
--  return 0;
  for x in 1 to 32 loop
    if (2**x > i) then return (x-1); end if;
  end loop;
  return 32;
end;

-- pragma translate_off
subtype nibble is std_logic_vector(3 downto 0);

function todec(i:integer) return character is
begin
  case i is
  when 0 => return('0');
  when 1 => return('1');
  when 2 => return('2');
  when 3 => return('3');
  when 4 => return('4');
  when 5 => return('5');
  when 6 => return('6');
  when 7 => return('7');
  when 8 => return('8');
  when 9 => return('9');
  when others => return('0');
  end case;
end;

function tohex(n:nibble) return character is
begin
  case n is
  when "0000" => return('0');
  when "0001" => return('1');
  when "0010" => return('2');
  when "0011" => return('3');
  when "0100" => return('4');
  when "0101" => return('5');
  when "0110" => return('6');
  when "0111" => return('7');
  when "1000" => return('8');
  when "1001" => return('9');
  when "1010" => return('a');
  when "1011" => return('b');
  when "1100" => return('c');
  when "1101" => return('d');
  when "1110" => return('e');
  when "1111" => return('f');
  when others => return('X');
  end case;
end;

function tost(v:std_logic_vector) return string is
constant vlen : natural := v'length; --'
constant slen : natural := (vlen+3)/4;
variable vv : std_logic_vector(0 to slen*4-1) := (others => '0');
variable s : string(1 to slen);
variable nz : boolean := false;
variable index : integer := -1;
begin
  vv(slen*4-vlen to slen*4-1) := v;
  for i in 0 to slen-1 loop
    if (vv(i*4 to i*4+3) = "0000") and nz and (i /= (slen-1)) then
      index := i;
    else
      nz := false;
      s(i+1) := tohex(vv(i*4 to i*4+3));
    end if;
  end loop;
  if ((index +2) = slen) then return(s(slen to slen));
  else return(string'("0x") & s(index+2 to slen)); end if; --'
end;

function tost(v:std_logic) return string is
begin
  if to_x01(v) = '1' then return("1"); else return("0"); end if;
end;

function tost_any(s: std_ulogic) return string is
begin
  case s is
    when '1' => return "1";
    when '0' => return "0";
    when '-' => return "-";
    when 'U' => return "U";
    when 'X' => return "X";
    when 'Z' => return "Z";
    when 'H' => return "H";
    when 'L' => return "L";
    when 'W' => return "W";
  end case;
end;

function tost_bits(s: std_logic_vector) return string is
  constant len: natural := s'length;
  variable str: string(1 to len);
  variable i: integer;
begin
  i := 1;
  for x in s'range loop      
    str(i to i) := tost_any(s(x));
    i := i+1;
  end loop;
  return str;
end;

function tost(b: boolean) return string is
begin
  if b then return "true"; else return "false"; end if;
end tost;
  
function tost(i : integer) return string is
variable L : line;
variable s, x : string(1 to 128);
variable n, tmp : integer := 0;
begin
  tmp := i;
  if i < 0 then tmp := -i; end if;
  loop
    s(128-n) := todec(tmp mod 10);
    tmp := tmp / 10;
    n := n+1;
    if tmp = 0 then exit; end if;
  end loop;
  x(1 to n) := s(129-n to 128);
  if i < 0 then return "-" & x(1 to n); end if;
  return(x(1 to n));
end;

function tost(r: real) return string is
  variable x: real;
  variable i,j: integer;
  variable s: string(1 to 30);
  variable c: character;
begin
  if r = 0.0 then
    return "0.0000";
  elsif r < 0.0 then
    return "-" & tost(-r);
  elsif r < 0.001 then
    x:=r; i:=0;
    while x<1.0 loop x:=x*10.0; i:=i+1; end loop;
    return tost(x) & "e-" & tost(i);
  elsif r >= 1000000.0 then
    x:=10000000.0; i:=6;
    while r>=x loop x:=x*10.0; i:=i+1; end loop;
    return tost(10.0*r/x) & "e+" & tost(i);
  else
    i:=0; x:=r+0.00005;
    while x >= 10.0 loop x:=x/10.0; i:=i+1; end loop;
    j := 1;
    while i > -5 loop
      if x >= 9.0 then c:='9'; x:=x-9.0;
      elsif x >= 8.0 then c:='8'; x:=x-8.0;
      elsif x >= 7.0 then c:='7'; x:=x-7.0;
      elsif x >= 6.0 then c:='6'; x:=x-6.0;
      elsif x >= 5.0 then c:='5'; x:=x-5.0;
      elsif x >= 4.0 then c:='4'; x:=x-4.0;
      elsif x >= 3.0 then c:='3'; x:=x-3.0;
      elsif x >= 2.0 then c:='2'; x:=x-2.0;
      elsif x >= 1.0 then c:='1'; x:=x-1.0;
      else c:='0';
      end if;
      s(j) := c;
      j:=j+1;
      if i=0 then s(j):='.'; j:=j+1; end if;
      i:=i-1;
      x := x * 10.0;
    end loop;
    return s(1 to j-1);
  end if;    
end tost;

procedure print(s : string) is
    variable L : line;
begin
  L := new string'(s); writeline(output, L);
end;
-- pragma translate_on

  function unary_to_slv(i: std_logic_vector) return std_logic_vector is
    variable o : std_logic_vector(log2(i'length)-1 downto 0);
  begin

--    -- 16 bits unary to binary conversion
--    o(0) := i(1) or i(3) or i(5)  or i(7)  or i(9)  or i(11) or i(13) or i(15);
--    o(1) := i(2) or i(3) or i(6)  or i(7)  or i(10) or i(11) or i(14) or i(15);
--    o(2) := i(4) or i(5) or i(6)  or i(7)  or i(12) or i(13) or i(14) or i(15);
--    o(3) := i(8) or i(9) or i(10) or i(11) or i(12) or i(13) or i(14) or i(15);
--
--    -- parametrized conversion
--
--    o(0) := i(1);
--
--    o(0)          := unary_to_slv(i(3 downto 2)) or unary_to_slv(i(1 downto 0));
--    o(1)          := orv(i(3 downto 2));
--
--    o(1 downto 0) := unary_to_slv(i(7 downto 4)) or unary_to_slv(i(3 downto 0));
--    o(2)          := orv(i(7 downto 4));
--
--    o(2 downto 0) := unary_to_slv(i(15 downto 8)) or unary_to_slv(i(7 downto 0));
--    o(3)          := orv(i(15 downto 8));
--

    assert i'length = 0 or i'length = 2**log2(i'length)
      report "unary_to_slv: input vector size must be power of 2"
      severity failure;

    if i'length > 2 then
      -- recursion on left and right halves
      o(log2(i'length)-2 downto 0)  := unary_to_slv(i(i'left downto (i'left-i'length/2+1))) or unary_to_slv(i((i'left-i'length/2) downto i'right));
      o(log2(i'length)-1)           := orv(i(i'left downto (i'left-i'length/2+1)));
    else
      o(0 downto 0) := ( 0 => i(i'left));
    end if;

    return o;
  end unary_to_slv;

  function gray_encoder(idata : in std_logic_vector) return std_logic_vector is
    variable vdata : std_logic_vector(idata'left downto idata'right);
  begin
    for i in idata'right to (idata'left)-1 loop
      vdata(i) := idata(i) xor idata(i+1);
    end loop;
    vdata(vdata'left) := idata(idata'left);
    return vdata;
  end gray_encoder;

  function gray_decoder(idata : in std_logic_vector) return std_logic_vector is
    variable vdata : std_logic_vector(idata'left downto idata'right);
  begin
    vdata(vdata'left) := idata(idata'left);
    for i in (idata'left)-1 downto idata'right loop
      vdata(i) := idata(i) xor vdata(i+1);
    end loop;
    return vdata;    
  end gray_decoder;

end;


