library ieee;
use ieee.std_logic_1164.all;
--use ieee.math_real.all;
use ieee.numeric_std.all;

package generics is
	constant synthesis : boolean := true
									-- synthesis translate_off
									and false
									-- synthesis translate_on
									;

	function sim_cond(cond : boolean) return boolean;

	function istr(v : integer) return string;
	
	function as_std_logic(v : boolean) return std_logic;

	function rot_l(v : std_logic_vector; n : integer := 1) return std_logic_vector;
	function rot_r(v : std_logic_vector; n : integer := 1) return std_logic_vector;
	function sh_l(v : std_logic_vector; n : integer := 1) return std_logic_vector;
	function sh_r(v : std_logic_vector; n : integer := 1) return std_logic_vector;

	function log2_int(v : integer) return integer;
	function log2(v : integer) return integer;
	function is_log2(v : integer) return boolean;
	
	function binary_flatten(v : std_logic_vector; n : integer) return std_logic_vector;
	function min(a : integer; b : integer) return integer;
	function max(a : integer; b : integer) return integer;
	
	function find_first_bit(v : std_logic_vector) return integer;
	function mask_first_bit(v : std_logic_vector) return std_logic_vector;
	function next_multiple(v : integer; m : integer) return integer;
	function reverse_vector(inp : std_logic_vector) return std_logic_vector;
	function reorder_vector(inp : std_logic_vector) return std_logic_vector;
	function htonl(inp : std_logic_vector) return std_logic_vector;
	function ntohl(inp : std_logic_vector) return std_logic_vector;
	function vector_all_valid(i : std_logic_vector) return boolean;
	function to_integer(i : std_logic) return integer;
	
	function integer_reverse_bits(i : integer; bits : integer) return integer;
	
	function dbg_collapse16(d : in std_logic_vector) return std_logic_vector;			

	function bram_we_width(aw : integer) return integer;

	function chr(sl: std_logic) return character;
	function chr(i : integer) return character;
	function chr(b : boolean) return character;
	function vector_to_string(v : std_logic_vector) return string;
	function vector_to_hex_string(v : std_logic_vector) return string;

	
	function to_std_logic_vector(v : integer; size : integer) return std_logic_vector;

	function popcnt(v : std_logic_vector) return integer;
	function popcnt(v : integer; bits : integer) return integer;
	function popcnt_x(v : std_logic_vector) return integer;
	
	function maxlen_lfsr_advance(reg : std_logic_vector) return std_logic_vector;
	function random_vector(len : integer; seed : integer) return std_logic_vector;
	function random(max : integer; seed : integer) return integer;
		
	function reverse_any_vector(a : in std_logic_vector) return std_logic_vector;

	function sel(cond : boolean; if_true : integer; if_false : integer) return integer;
	function sel(cond : boolean; if_true : std_logic_vector; if_false : std_logic_vector) return std_logic_vector;
	function sel(cond : boolean; if_true : std_logic; if_false : std_logic) return std_logic;
	function sel(cond : boolean; if_true : string; if_false : string) return string;
	procedure clkp(signal clk : in std_logic; n : in integer);

	function vector_mux(sel : std_logic_vector; i1 : std_logic_vector; i2 : std_logic_vector) return std_logic_vector;
	function div_ceil(a : integer; b : integer) return integer;

	function int_strlen(vv : integer) return natural;
end generics;

package body generics is
	function chr(b : boolean) return character is
	begin
		if b then
			return 't';
		end if;
		return 'f';
	end function;

	function chr(i : integer) return character is
		variable s : string(1 to 10) := "0123456789";
	begin
		if i < 10 then
			return s(i + 1);
		else
			return 'X';
		end if;
	end function;

	function istr(v : integer) return string is
	begin
		return integer'image(v);
	end function;
	

	function as_std_logic(v : boolean) return std_logic is
	begin
		if v then return '1'; end if;
		return '0';
	end function;

	function int_strlen(vv : integer) return natural is
		variable ret : natural := 0;
		variable v : integer := vv;
	begin
		if v < 0 then
			ret := ret + 1;
			v := -v;
		end if;
		while v >= 10 loop
			v := v / 10;
			ret := ret + 1;
		end loop;
		return ret + 1;
	end function;
	
	function rot_l(v : std_logic_vector; n : integer := 1) return std_logic_vector is
	begin
		return std_logic_vector(rotate_left(unsigned(v), n));
	end function;
	function rot_r(v : std_logic_vector; n : integer := 1) return std_logic_vector is
	begin
		return std_logic_vector(rotate_right(unsigned(v), n));
	end function;
	function sh_l(v : std_logic_vector; n : integer := 1) return std_logic_vector is
	begin
		return std_logic_vector(shift_left(unsigned(v), n));
	end function;
	function sh_r(v : std_logic_vector; n : integer := 1) return std_logic_vector is
	begin
		return std_logic_vector(shift_right(unsigned(v), n));
	end function;
	function log2(v : integer) return integer is
	begin
		return log2_int(v);
	end function;
	function is_log2(v : integer) return boolean is
	begin
		return 2 ** log2(v) = v;
	end function;
	function to_integer(i : std_logic) return integer is
	begin
		if i = '1' then
			return 1;
		elsif i = '0' then
			return 0;
		else
			return -1;
		end if;
	end function;
	function dbg_collapse16(d : in std_logic_vector) return std_logic_vector is
		variable ret : std_logic_vector(0 to 15);
		variable oi : integer;
	begin
		oi := 0;
		ret := (others => '0');
		for i in d'range loop
			ret(oi) := ret(oi) xor d(i);
			if oi < 15 then
				oi := oi + 1;
			else
				oi := 0;
			end if;
		end loop;
		return ret;
	end function;
	function random(max : integer; seed : integer) return integer is
	begin
		if max = 0 then return 0; end if;
		return to_integer(unsigned(random_vector(log2_int(max) + 1, seed))) mod max;
	end function;
	function sel(cond : boolean; if_true : std_logic; if_false : std_logic) return std_logic is
	begin
		if cond then
			return if_true;
		end if;
		return if_false;
	end function;
	function sel(cond : boolean; if_true : string; if_false : string) return string is
	begin
		if cond then
			return if_true;
		else
			return if_false;
		end if;
	end function;
	function div_ceil(a : integer; b : integer) return integer is
	begin
		return a / b + sel(a mod b /= 0, 1, 0);
	end function;



	function sim_cond(cond : boolean) return boolean is
	begin
		if synthesis then
			return true;
		else
			return cond;
		end if;
	end function;
	
	function integer_reverse_bits(i : integer; bits : integer) return integer is
		variable m : std_logic_vector(0 to bits - 1);
	begin
		m := std_logic_vector(to_unsigned(i, bits));
		m := reverse_any_vector(m);
		return to_integer(unsigned(m));
	end function;

	function vector_mux(sel : std_logic_vector; i1 : std_logic_vector; i2 : std_logic_vector) return std_logic_vector is
		variable ret : std_logic_vector(0 to sel'length - 1);
	begin
		for i in 0 to sel'length - 1 loop
			if sel(sel'left + i) = '1' then
				ret(i) := i1(i1'left + i);
			else
				ret(i) := i2(i2'left + i);
			end if;
		end loop;
		return ret;
	end function;


	procedure clkp(signal clk : in std_logic; n : in integer) is
	begin
		for i in 1 to n loop
			wait until rising_edge(clk);
		end loop;
		wait for 1 ps;
	end procedure;

	function random_vector(len : integer; seed : integer) return std_logic_vector is
		variable lfsr : std_logic_vector(0 to 7) := std_logic_vector(to_unsigned(seed, 8));
		variable ret : std_logic_vector(0 to len - 1);
	begin
		for i in 0 to len / 8 loop
			lfsr := maxlen_lfsr_advance(lfsr);
			lfsr := maxlen_lfsr_advance(lfsr);
			lfsr := maxlen_lfsr_advance(lfsr);
			lfsr := maxlen_lfsr_advance(lfsr);
			ret(i * 8 to min(ret'length, (i + 1) * 8) - 1) := lfsr(0 to min(8, ret'length - i * 8) - 1);
		end loop;
		return ret;
	end function;
	function sel(cond : boolean; if_true : integer; if_false : integer) return integer is
	begin
		if cond then return if_true; else return if_false; end if;
	end function;
	function sel(cond : boolean; if_true : std_logic_vector; if_false : std_logic_vector) return std_logic_vector is
	begin
		if cond then return if_true; else return if_false; end if;
	end function;

	function popcnt(v : integer; bits : integer) return integer is
	begin
		return popcnt(std_logic_vector(to_unsigned(v, bits)));
	end function;

	function reverse_any_vector (a: in std_logic_vector) return std_logic_vector is
		variable result: std_logic_vector(a'range);
		alias aa: std_logic_vector(a'reverse_range) is a;
	begin
		for i in aa'range loop
			result(i) := aa(i);
		end loop;
		return result;
	end;
	function maxlen_lfsr_advance(reg : std_logic_vector) return std_logic_vector is
		variable ret : std_logic_vector(reg'range);
	begin
		if ret'left > ret'right then
			ret(reg'left downto 1) := reg(reg'left - 1 downto 0);
		else
			ret(1 to reg'right) := reg(0 to reg'right - 1);
		end if;
		if reg'length = 3 then
			ret(0) := reg(2) xnor reg(1);
		elsif reg'length = 4 then
			ret(0) := reg(3) xnor reg(2);
		elsif reg'length = 8 then
			ret(0) := reg(7) xnor reg(5) xnor reg(4) xnor reg(3);
		elsif reg'length = 57 then
			ret(0) := reg(56) xnor reg(49);
		else
			assert false report "no matching shift register configured for length " & integer'image(reg'length) severity failure;
		end if;
		return ret;
	end function;



	function vector_all_valid(i : std_logic_vector) return boolean is
	begin
		for j in i'range loop
			if i(j) /= '1' and i(j) /= '0' then
				return false;
			end if;
		end loop;
		return true;
	end function;

	function popcnt_x(v : std_logic_vector) return integer is
		variable ret : integer;
	begin
		ret := 0;
		for i in v'range loop
			if v(i) = 'X' then
				ret := ret + 1;
			end if;
		end loop;
		return ret;
	end function;
	
	function popcnt(v : std_logic_vector) return integer is
		variable res : integer;
	begin
		res := 0;
		for i in v'range loop
			if v(i) = '1' then
				res := res + 1;
			end if;
		end loop;
		return res;
	end function;
	function to_std_logic_vector(v : integer; size : integer) return std_logic_vector is
		variable ret : std_logic_vector(size - 1 downto 0);
		variable tmp : unsigned(size - 1 downto 0);
	begin
		tmp := to_unsigned(v, size);
		return std_logic_vector(tmp);
	end function;


	function chr(sl: std_logic) return character is
		variable c: character;
    begin
		case sl is
			when 'U' => c:= 'U';
			when 'X' => c:= 'X';
			when '0' => c:= '0';
			when '1' => c:= '1';
			when 'Z' => c:= 'Z';
			when 'W' => c:= 'W';
			when 'L' => c:= 'L';
			when 'H' => c:= 'H';
			when '-' => c:= '-';
		end case;
		return c;
	end chr;

	function vector_to_hex_string(v : std_logic_vector) return string is
		variable ret : string(1 to (v'length + 3) / 4);
		type hchar_t is array(0 to 15) of character;
		constant hchar : hchar_t := ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f');
		variable off : integer := v'length mod 4;
	begin
		if off /= 0 then
			ret(1) := hchar(to_integer(unsigned(v(0 to off - 1))));
		end if;
		for i in 0 to v'length / 4 - 1 loop
			ret(i + sel(off = 0, 1, 2)) := hchar(to_integer(unsigned(v(off + i * 4 to off + i * 4 + 3))));
		end loop;
		return ret;
	end function;

	function vector_to_string(v : std_logic_vector) return string is
		variable ret : string(1 to v'length);
		variable at : integer;
	begin
		at := 1;
		for i in v'range loop
			ret(at) := chr(v(i));
			at := at + 1;
		end loop;
		return ret;
--		for i in 0 to v'length - 1 loop
--			ret(i + 1) := chr(v(i));
--		end loop;
--		return ret;
	end function;

	function bram_we_width(aw : integer) return integer is
	begin
		if aw > 16 then
			return 4;
		elsif aw > 8 then
			return 2;
		else
			return 1;
		end if;
	end function;

	function htonl(inp : std_logic_vector) return std_logic_vector is
		variable ret : std_logic_vector(inp'range);
	begin
		ret(31 downto 24) := inp(7 downto 0);
		ret(23 downto 16) := inp(15 downto 8);
		ret(15 downto 8)  := inp(23 downto 16);
		ret(7 downto 0)   := inp(31 downto 24);
		return ret;
	end function;

	function ntohl(inp : std_logic_vector) return std_logic_vector is
	begin
		return htonl(inp);
	end function;

	function reorder_vector(inp : std_logic_vector) return std_logic_vector is
		variable ret : std_logic_vector(inp'reverse_range);
	begin
		return inp;
		if inp'left < inp'right then
			for i in inp'range loop
				ret(inp'right - i) := inp(i);
			end loop;
		elsif inp'left > inp'right then
			for i in inp'range loop
				ret(inp'left - i) := inp(i);
			end loop;
		else
			ret(inp'left) := inp(inp'left);
		end if;
		return ret;
	end function;

	function reverse_vector(inp : std_logic_vector) return std_logic_vector is
		variable ret : std_logic_vector(inp'reverse_range);
	begin
		for i in inp'range loop
			ret(i) := inp(i);
		end loop;
		return ret;
	end function;

	function next_multiple(v : integer; m : integer) return integer is
	begin
		if v mod m = 0 then
			return v;
		else
			return v + m - v mod m;
		end if;
	end function;
	function mask_first_bit(v : std_logic_vector) return std_logic_vector is
		variable ret : std_logic_vector(v'range) := (others => '0');
	begin
		for i in 0 to v'length - 1 loop
			if v(i) = '1' then
				ret(i) := '1';
				return ret;
			end if;
		end loop;
		return ret;
	end function;
		
	function find_first_bit(v : std_logic_vector) return integer is
	begin
		for i in 0 to v'length - 1 loop
			if v(i) = '1' then return i; end if;
		end loop;
		return 0;
	end function;

	function min(a : integer; b : integer) return integer is
	begin
		if a < b then
			return a;
		end if;
		return b;
	end function;
	function max(a : integer; b : integer) return integer is
	begin
		if a > b then
			return a;
		end if;
		return b;
	end function;

	function binary_flatten(v : std_logic_vector; n : integer) return std_logic_vector is
		variable res : std_logic_vector(n - 1 downto 0) := (others => '0');
	begin
		for i in 0 to n - 1 loop
			if unsigned(v) = to_unsigned(i, v'length) then
				res(i) := '1';
				return res;
			end if;
		end loop;
		return res;
	end function;

	function log2_int(v : integer) return integer is
		variable vv, ret : integer;
	begin
		vv := v;
		ret := 0;
		while vv > 1 loop
			ret := ret + 1;
			vv  := vv / 2;
		end loop;
		if 2 ** ret = v then
			return ret;
		else
			return ret + 1;
		end if;
		--return integer(ceil(log2(real(v))));
	end function;
end generics;