-- EMACS settings: -*-  tab-width: 2; indent-tabs-mode: t -*-
-- vim: tabstop=2:shiftwidth=2:noexpandtab
-- kate: tab-width 2; replace-tabs off; indent-width 2;
-- =============================================================================
-- Authors:					Patrick Lehmann
-- 									Martin Zabel
--									Thomas B. Preusser
--
-- Package:					This VHDL package declares new physical types and their
--									conversion functions.
--
-- Description:
-- 
--		For detailed documentation see below.
--
--		NAMING CONVENTION:
--			t - time
--			p - period
--			d - delay
--			f - frequency
--			br - baud rate
--			vec - vector
--
--		ATTENTION:
--			This package is not supported by Xilinx Synthese Tools prior to 14.7!
--
--			It was successfully tested with:
--				- Xilinx Synthesis Tool (XST) 14.7 and Xilinx ISE Simulator (iSim) 14.7
--				- Quartus II 13.1
--				- QuestaSim 10.0d
--				- GHDL 0.31
--
--			Tool chains with known issues:
--				- Xilinx Vivado	Synthesis 2014.4
--
--			Untested tool chains
--				- Xilinx Vivado Simulator (xSim) 2014.4
--
-- License:
-- =============================================================================
-- Copyright 2007-2015 Technische Universitaet Dresden - Germany,
--										 Chair of VLSI-Design, Diagnostics and Architecture
--
-- 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.
-- =============================================================================

library IEEE;
use			IEEE.math_real.all;

library PoC;
use			PoC.config.all;
use			PoC.utils.all;
use			PoC.strings.all;


package [docs]physical is

	type FREQ is range 0 to integer'high units
		Hz;
		kHz = 1000 Hz;
		MHz = 1000 kHz;
		GHz = 1000 MHz;
	end units;

	type BAUD is range 0 to integer'high units
		Bd;
		kBd = 1000 Bd;
		MBd = 1000 kBd;
		GBd = 1000 MBd;
	end units;

	type MEMORY is range 0 to integer'high units
		Byte;
		KiB = 1024 Byte;
		MiB = 1024 KiB;
		GiB = 1024 MiB;
	end units;

	-- vector data types
	type		T_TIMEVEC						is array(natural range <>) of time;
	type		T_FREQVEC						is array(natural range <>) of FREQ;
	type		T_BAUDVEC						is array(natural range <>) of BAUD;
	type		T_MEMVEC						is array(natural range <>) of MEMORY;

	-- if true: TimingToCycles reports difference between expected and actual result
	constant C_PHYSICAL_REPORT_TIMING_DEVIATION		: boolean		:= TRUE;

	-- conversion functions
	function [docs]to_time(f : FREQ)	return time;
	function [docs]to_freq(p : time)	return FREQ;
	function [docs]to_freq(br : BAUD)	return FREQ;
	function [docs]to_baud(str : string)	return BAUD;

	-- inter-type arithmetic
	function [docs]div(a : time; b : time) return real;
	function [docs]div(a : FREQ; b : FREQ) return real;

	function [docs]"/"(x : real; t : time) return FREQ;
	function [docs]"/"(x : real; f : FREQ) return time;
	function [docs]"*"(t : time; f : FREQ) return real;
	function [docs]"*"(f : FREQ; t : time) return real;

	-- if-then-else
	function [docs]ite(cond : boolean; value1 : time;	value2 : time)			return time;
	function [docs]ite(cond : boolean; value1 : FREQ;	value2 : FREQ)			return FREQ;
	function [docs]ite(cond : boolean; value1 : BAUD;	value2 : BAUD)			return BAUD;
	function [docs]ite(cond : boolean; value1 : MEMORY;	value2 : MEMORY)	return MEMORY;

	-- min/ max for 2 arguments
	function [docs]tmin(arg1 : time; arg2 : time) return time;						-- Calculates: min(arg1, arg2) for times
	function [docs]fmin(arg1 : FREQ; arg2 : FREQ) return FREQ;						-- Calculates: min(arg1, arg2) for frequencies
	function [docs]bmin(arg1 : BAUD; arg2 : BAUD) return BAUD;						-- Calculates: min(arg1, arg2) for symbols per second
	function [docs]mmin(arg1 : MEMORY; arg2 : MEMORY) return MEMORY;			-- Calculates: min(arg1, arg2) for memory

	function [docs]tmax(arg1 : time; arg2 : time) return time;						-- Calculates: max(arg1, arg2) for times
	function [docs]fmax(arg1 : FREQ; arg2 : FREQ) return FREQ;						-- Calculates: max(arg1, arg2) for frequencies
	function [docs]bmax(arg1 : BAUD; arg2 : BAUD) return BAUD;						-- Calculates: max(arg1, arg2) for symbols per second
	function [docs]mmax(arg1 : MEMORY; arg2 : MEMORY) return MEMORY;			-- Calculates: max(arg1, arg2) for memory

	-- min/max/sum as vector aggregation
	function [docs]tmin(vec : T_TIMEVEC)	return time;										-- Calculates: min(vec) for a time vector
	function [docs]fmin(vec : T_FREQVEC)	return FREQ;										-- Calculates: min(vec) for a frequency vector
	function [docs]bmin(vec : T_BAUDVEC)	return BAUD;										-- Calculates: min(vec) for a baud vector
	function [docs]mmin(vec : T_MEMVEC)	return MEMORY;									-- Calculates: min(vec) for a memory vector

	function [docs]tmax(vec : T_TIMEVEC)	return time;										-- Calculates: max(vec) for a time vector
	function [docs]fmax(vec : T_FREQVEC)	return FREQ;										-- Calculates: max(vec) for a frequency vector
	function [docs]bmax(vec : T_BAUDVEC)	return BAUD;										-- Calculates: max(vec) for a baud vector
	function [docs]mmax(vec : T_MEMVEC)	return MEMORY;									-- Calculates: max(vec) for a memory vector

	function [docs]tsum(vec : T_TIMEVEC)	return time;										-- Calculates: sum(vec) for a time vector
	function [docs]fsum(vec : T_FREQVEC)	return FREQ;										-- Calculates: sum(vec) for a frequency vector
	function [docs]bsum(vec : T_BAUDVEC)	return BAUD;										-- Calculates: sum(vec) for a baud vector
	function [docs]msum(vec : T_MEMVEC)	return MEMORY;									-- Calculates: sum(vec) for a memory vector

	-- convert standard types (NATURAL, REAL) to time (TIME)
	function [docs]fs2Time(t_fs : integer)		return time;
	function [docs]ps2Time(t_ps : integer)		return time;
	function [docs]ns2Time(t_ns : integer)		return time;
	function [docs]us2Time(t_us : integer)		return time;
	function [docs]ms2Time(t_ms : integer)		return time;
	function [docs]sec2Time(t_sec : integer)	return time;

	function [docs]fs2Time(t_fs : REAL)				return time;
	function [docs]ps2Time(t_ps : REAL)				return time;
	function [docs]ns2Time(t_ns : REAL)				return time;
	function [docs]us2Time(t_us : REAL)				return time;
	function [docs]ms2Time(t_ms : REAL)				return time;
	function [docs]sec2Time(t_sec : REAL)			return time;

	-- convert standard types (NATURAL, REAL) to period (TIME)
	function [docs]Hz2Time(f_Hz : natural)		return time;
	function [docs]kHz2Time(f_kHz : natural)	return time;
	function [docs]MHz2Time(f_MHz : natural)	return time;
	function [docs]GHz2Time(f_GHz : natural)	return time;

	function [docs]Hz2Time(f_Hz : REAL)				return time;
	function [docs]kHz2Time(f_kHz : REAL) 		return time;
	function [docs]MHz2Time(f_MHz : REAL) 		return time;
	function [docs]GHz2Time(f_GHz : REAL) 		return time;

	-- convert standard types (NATURAL, REAL) to frequency (FREQ)
	function [docs]Hz2Freq(f_Hz : natural)		return FREQ;
	function [docs]kHz2Freq(f_kHz : natural)	return FREQ;
	function [docs]MHz2Freq(f_MHz : natural)	return FREQ;
	function [docs]GHz2Freq(f_GHz : natural)	return FREQ;

	function [docs]Hz2Freq(f_Hz : REAL)				return FREQ;
	function [docs]kHz2Freq(f_kHz : REAL)			return FREQ;
	function [docs]MHz2Freq(f_MHz : REAL)			return FREQ;
	function [docs]GHz2Freq(f_GHz : REAL)			return FREQ;

	-- convert physical types to standard type (REAL)
	function [docs]to_real(t : time;			scale : time)		return REAL;
	function [docs]to_real(f : FREQ;			scale : FREQ)		return REAL;
	function [docs]to_real(br : BAUD;			scale : BAUD)		return REAL;
	function [docs]to_real(mem : MEMORY;	scale : MEMORY)	return REAL;

	-- convert physical types to standard type (INTEGER)
	function [docs]to_int(t : time;			scale : time;		RoundingStyle : T_ROUNDING_STYLE := ROUND_TO_NEAREST)	return integer;
	function [docs]to_int(f : FREQ;			scale : FREQ;		RoundingStyle : T_ROUNDING_STYLE := ROUND_TO_NEAREST)	return integer;
	function [docs]to_int(br : BAUD;		scale : BAUD;		RoundingStyle : T_ROUNDING_STYLE := ROUND_TO_NEAREST)	return integer;
	function [docs]to_int(mem : MEMORY;	scale : MEMORY;	RoundingStyle : T_ROUNDING_STYLE := ROUND_UP)					return integer;

	-- calculate needed counter cycles to achieve a given 1. timing/delay and 2. frequency/period
	function [docs]TimingToCycles(Timing : time; Clock_Period			: time; RoundingStyle : T_ROUNDING_STYLE := ROUND_UP) return natural;
	function [docs]TimingToCycles(Timing : time; Clock_Frequency	: FREQ; RoundingStyle : T_ROUNDING_STYLE := ROUND_UP) return natural;

	function [docs]CyclesToDelay(Cycles : natural; Clock_Period			: time) return time;
	function [docs]CyclesToDelay(Cycles : natural; Clock_Frequency	: FREQ) return time;

	-- convert and format physical types to STRING
	function [docs]to_string(t : time; precision : natural)			return string;
	function [docs]to_string(f : FREQ; precision : natural)			return string;
	function [docs]to_string(br : BAUD; precision : natural)		return string;
	function [docs]to_string(mem : MEMORY; precision : natural)	return string;
end package;


package body physical is

	-- WORKAROUND: for simulators with a "Minimal Time Resolution" > 1 fs
	--	Version:	all
	--	Vendors:	all
	--	Issue:
	--		Some simulators use a lower minimal time resolution (MTR) than the VHDL
	--		standard (LRM) defines (1 fs). Usually, the MTR is set to 1 ps or 1 ns.
	--		Most simulators allow the user to specify a higher MTR -> check the
	--		simulator documentation.
	--	Solution:
	--		The currently set MTR can be calculated in VHDL. Using the correct MTR
	--		can prevent cleared intermediate values and division by zero errors.
	--	Examples:
	--		Mentor Graphics QuestaSim/ModelSim (vSim): default MTR = ? ??
	--		Xilinx ISE Simulator (iSim):               default MTR = 1 ps
	--		Xilinx Vivado Simulator (xSim):            default MTR = 1 ps
	function MinimalTimeResolutionInSimulation return time is
	begin
		if		(1 fs > 0 sec) then	return 1 fs;
		elsif	(1 ps > 0 sec) then	return 1 ps;
		elsif	(1 ns > 0 sec) then	return 1 ns;
		elsif	(1 us > 0 sec) then	return 1 us;
		elsif	(1 ms > 0 sec) then	return 1 ms;
		else											return 1 sec;
		end if;
	end function;

	-- real division for physical types
	-- ===========================================================================
	function div(a : time; b : time) return REAL is
		constant MTRIS	: time		:= MinimalTimeResolutionInSimulation;
		variable a_real : real;
		variable b_real : real;
	begin
		-- WORKAROUND: for Altera Quartus
		--	Version:	all
		--	Issue:
		--		Results of TIME arithmetic must be in 32-bit integer range, because
		--		the internally used 64-bit integer for type TIME can not be
		--		represented in VHDL.
		--	Solution:
		--		Pre- and post-scale all values to stay in the integer range.
	  if    a < 1 us  then
			a_real  := real(a / MTRIS);
		elsif a < 1 ms  then
			a_real  := real(a / (1000 * MTRIS)) * 1000.0;
		elsif a < 1 sec then
			a_real  := real(a / (1000000 * MTRIS)) * 1000000.0;
		else
			a_real  := real(a / (1000000000 * MTRIS)) * 1000000000.0;
		end if;

	  if    b < 1 us  then
			b_real  := real(b / MTRIS);
		elsif b < 1 ms  then
			b_real  := real(b / (1000 * MTRIS)) * 1000.0;
		elsif b < 1 sec then
			b_real  := real(b / (1000000 * MTRIS)) * 1000000.0;
		else
			b_real  := real(b / (1000000000 * MTRIS)) * 1000000000.0;
		end if;

        if (b_real = 0.0) then
            return 0.0;
        end if;

		return a_real / b_real; 
	end function;

	function div(a : FREQ; b : FREQ) return REAL is
	begin
		return real(a / 1 Hz) / real(b / 1 Hz);
	end function;

	function div(a : BAUD; b : BAUD) return REAL is
	begin
		return real(a / 1 Bd) / real(b / 1 Bd);
	end function;

	function div(a : MEMORY; b : MEMORY) return REAL is
	begin
		return real(a / 1 Byte) / real(b / 1 Byte);
	end function;

	-- conversion functions
	-- ===========================================================================
	function to_time(f : FREQ) return time is
		variable res : time;
	begin
		res := div(1000 MHz, f) * 1 ns;
		if POC_VERBOSE then
			report "to_time: f= " & to_string(f, 3) & "  return " & to_string(res, 3) severity note;
		end if;
		return res;
	end function;

	function to_freq(p : time) return FREQ is
		variable res : FREQ;
	begin
		if (p <= 1 sec) then res := div(1 sec, p) * 1  Hz;
		else report "to_freq: input period exceeds output frequency scale." severity failure;
		end if;
		if POC_VERBOSE then
			report "to_freq: p= " & to_string(p, 3) & "  return " & to_string(res, 3) severity note;
		end if;
		return res;
	end function;

	function to_freq(br : BAUD) return FREQ is
		variable res : FREQ;
	begin
		res := (br / 1 Bd)	* 1  Hz;
		if POC_VERBOSE then
			report "to_freq: br= " & to_string(br, 3) & "  return " & to_string(res, 3) severity note;
		end if;
		return res;
	end function;

	function to_baud(str : string) return BAUD is
		variable pos		: integer;
		variable int		: natural;
		variable base		: positive;
		variable frac		: natural;
		variable digits	: natural;
	begin
		pos			:= str'low;
		int			:= 0;
		frac		:= 0;
		digits	:= 0;
		-- read integer part
		for i in pos to str'high loop
			if chr_isDigit(str(i)) then		int := int * 10 + to_digit_dec(str(i));
			elsif (str(i) = '.') then								pos	:= -i;	exit;
			elsif (str(i) = ' ') then								pos	:= i;		exit;
			else																		pos := 0;		exit;
			end if;
		end loop;
		-- read fractional part
		if ((pos < 0) and (-pos < str'high)) then
			for i in -pos+1 to str'high loop
				if ((frac = 0) and (str(i) = '0')) then	next;
				elsif chr_isDigit(str(i)) then	frac	:= frac * 10 + to_digit_dec(str(i));
				elsif (str(i) = ' ') then								digits	:= i + pos - 1;	pos	:= i;	exit;
				else																														pos	:= 0;	exit;
				end if;
			end loop;
		end if;
		-- abort if format is unknown
		if pos = 0 then report "to_baud: Unknown format" severity FAILURE;	end if;
		-- parse unit
		pos := pos + 1;
		if ((pos + 1 = str'high) and (str(pos to pos + 1) = "Bd")) then
																		return int * 1 Bd;
		elsif (pos + 2 = str'high) then
			if (str(pos to pos + 2) = "kBd") then
				if frac = 0 then					return (int * 1 kBd);
				elsif (digits <= 3) then		return (int * 1 kBd) + (frac * 10**(3 - digits) * 1 Bd);
				else												return (int * 1 kBd) + (frac / 10**(digits - 3) * 100 Bd);
				end if;
			elsif (str(pos to pos + 2) = "MBd") then
				if frac = 0 then					return (int * 1 kBd);
				elsif (digits <= 3) then		return (int * 1 MBd) + (frac * 10**(3 - digits) * 1 kBd);
				elsif (digits <= 6) then		return (int * 1 MBd) + (frac * 10**(6 - digits) * 1 Bd);
				else												return (int * 1 MBd) + (frac / 10**(digits - 6) * 100000 Bd);
				end if;
			elsif (str(pos to pos + 2) = "GBd") then
				if frac = 0 then					return (int * 1 kBd);
				elsif (digits <= 3) then		return (int * 1 GBd) + (frac * 10**(3 - digits) * 1 MBd);
				elsif (digits <= 6) then		return (int * 1 GBd) + (frac * 10**(6 - digits) * 1 kBd);
				elsif (digits <= 9) then		return (int * 1 GBd) + (frac * 10**(9 - digits) * 1 Bd);
				else												return (int * 1 GBd) + (frac / 10**(digits - 9) * 100000000 Bd);
				end if;
			else
				report "to_baud: Unknown unit." severity FAILURE;
			end if;
		else
			report "to_baud: Unknown format" severity FAILURE;
		end if;
		return 0 Bd;
	end function;

	-- inter-type arithmetic
	-- ===========================================================================
	function "/"(x : real; t : time) return FREQ is
	begin
		return  x*div(1 ms, t) * 1 kHz;
	end function;
	function "/"(x : real; f : FREQ) return time is
	begin
		return  x*div(1 kHz, f) * 1 ms;
	end function;
	function "*"(t : time; f : FREQ) return real is
	begin
		return  div(t, 1.0/f);
	end function;
	function "*"(f : FREQ; t : time) return real is
	begin
		return  div(f, 1.0/t);
	end function;

	-- if-then-else
	-- ===========================================================================
	function ite(cond : boolean; value1 : time;	value2 : time) return time is
	begin
		if cond then
			return value1;
		else
			return value2;
		end if;
	end function;

	function ite(cond : boolean; value1 : FREQ;	value2 : FREQ) return FREQ is
	begin
		if cond then
			return value1;
		else
			return value2;
		end if;
	end function;

	function ite(cond : boolean; value1 : BAUD;	value2 : BAUD) return BAUD is
	begin
		if cond then
			return value1;
		else
			return value2;
		end if;
	end function;

	function ite(cond : boolean; value1 : MEMORY;	value2 : MEMORY) return MEMORY is
	begin
		if cond then
			return value1;
		else
			return value2;
		end if;
	end function;

	-- min/ max for 2 arguments
	-- ===========================================================================
	-- Calculates: min(arg1, arg2) for times
	function tmin(arg1 : time; arg2 : time) return time is
	begin
		if arg1 < arg2 then return arg1; end if;
		return arg2;
	end function;

	-- Calculates: min(arg1, arg2) for frequencies
	function fmin(arg1 : FREQ; arg2 : FREQ) return FREQ is
	begin
		if arg1 < arg2 then return arg1; end if;
		return arg2;
	end function;

	-- Calculates: min(arg1, arg2) for symbols per second
	function bmin(arg1 : BAUD; arg2 : BAUD) return BAUD is
	begin
		if arg1 < arg2 then return arg1; end if;
		return arg2;
	end function;

	-- Calculates: min(arg1, arg2) for memory
	function mmin(arg1 : MEMORY; arg2 : MEMORY) return MEMORY is
	begin
		if arg1 < arg2 then return arg1; end if;
		return arg2;
	end function;

	-- Calculates: max(arg1, arg2) for times
	function tmax(arg1 : time; arg2 : time) return time is
	begin
		if arg1 > arg2 then return arg1; end if;
		return arg2;
	end function;

	-- Calculates: max(arg1, arg2) for frequencies
	function fmax(arg1 : FREQ; arg2 : FREQ) return FREQ is
	begin
		if arg1 > arg2 then return arg1; end if;
		return arg2;
	end function;

	-- Calculates: max(arg1, arg2) for symbols per second
	function bmax(arg1 : BAUD; arg2 : BAUD) return BAUD is
	begin
		if arg1 > arg2 then return arg1; end if;
		return arg2;
	end function;

	-- Calculates: max(arg1, arg2) for memory
	function mmax(arg1 : MEMORY; arg2 : MEMORY) return MEMORY is
	begin
		if arg1 > arg2 then return arg1; end if;
		return arg2;
	end function;

	-- min/max/sum as vector aggregation
	-- ===========================================================================
	-- Calculates: min(vec) for a time vector
	function tmin(vec : T_TIMEVEC)	return time is
		variable  res : time := time'high;
	begin
		for i in vec'range loop
			if vec(i) < res then
				res := vec(i);
			end if;
		end loop;
		return  res;
	end;

	-- Calculates: min(vec) for a frequency vector
	function fmin(vec : T_FREQVEC)	return FREQ is
		variable  res : FREQ := FREQ'high;
	begin
		for i in vec'range loop
			if (integer(FREQ'pos(vec(i))) < integer(FREQ'pos(res))) then -- Quartus workaround
				res := vec(i);
			end if;
		end loop;
		return  res;
	end;

	-- Calculates: min(vec) for a baud vector
	function bmin(vec : T_BAUDVEC)	return BAUD is
		variable  res : BAUD := BAUD'high;
	begin
		for i in vec'range loop
			if (integer(BAUD'pos(vec(i))) < integer(BAUD'pos(res))) then -- Quartus workaround
				res := vec(i);
			end if;
		end loop;
		return  res;
	end;

	-- Calculates: min(vec) for a memory vector
	function mmin(vec : T_MEMVEC)	return MEMORY is
		variable  res : MEMORY := MEMORY'high;
	begin
		for i in vec'range loop
			if (integer(MEMORY'pos(vec(i))) < integer(MEMORY'pos(res))) then -- Quartus workaround
				res := vec(i);
			end if;
		end loop;
		return  res;
	end;

	-- Calculates: max(vec) for a time vector
	function tmax(vec : T_TIMEVEC)	return time is
		variable  res : time := time'low;
	begin
		for i in vec'range loop
			if vec(i) > res then
				res := vec(i);
			end if;
		end loop;
		return  res;
	end;

	-- Calculates: max(vec) for a frequency vector
	function fmax(vec : T_FREQVEC)	return FREQ is
		variable  res : FREQ := FREQ'low;
	begin
		for i in vec'range loop
			if (integer(FREQ'pos(vec(i))) > integer(FREQ'pos(res))) then -- Quartus workaround
				res := vec(i);
			end if;
		end loop;
		return  res;
	end;

	-- Calculates: max(vec) for a baud vector
	function bmax(vec : T_BAUDVEC)	return BAUD is
		variable  res : BAUD := BAUD'low;
	begin
		for i in vec'range loop
			if (integer(BAUD'pos(vec(i))) > integer(BAUD'pos(res))) then -- Quartus workaround
				res := vec(i);
			end if;
		end loop;
		return  res;
	end;

	-- Calculates: max(vec) for a memory vector
	function mmax(vec : T_MEMVEC)	return MEMORY is
		variable  res : MEMORY := MEMORY'low;
	begin
		for i in vec'range loop
			if (integer(MEMORY'pos(vec(i))) > integer(MEMORY'pos(res))) then -- Quartus workaround
				res := vec(i);
			end if;
		end loop;
		return  res;
	end;

	-- Calculates: sum(vec) for a time vector
	function tsum(vec : T_TIMEVEC)	return time is
		variable  res : time := 0 fs;
	begin
		for i in vec'range loop
			res	:= res + vec(i);
		end loop;
		return  res;
	end;

	-- Calculates: sum(vec) for a frequency vector
	function fsum(vec : T_FREQVEC)	return FREQ is
		variable  res : FREQ := 0 Hz;
	begin
		for i in vec'range loop
			res	:= res + vec(i);
		end loop;
		return  res;
	end;

	-- Calculates: sum(vec) for a baud vector
	function bsum(vec : T_BAUDVEC)	return BAUD is
		variable  res : BAUD := 0 Bd;
	begin
		for i in vec'range loop
			res	:= res + vec(i);
		end loop;
		return  res;
	end;

	-- Calculates: sum(vec) for a memory vector
	function msum(vec : T_MEMVEC)	return MEMORY is
		variable  res : MEMORY := 0 Byte;
	begin
		for i in vec'range loop
			res	:= res + vec(i);
		end loop;
		return  res;
	end;

	-- convert standard types (NATURAL, REAL) to time (TIME)
	-- ===========================================================================
	function fs2Time(t_fs : integer) return time is
	begin
		return t_fs * 1 fs;
	end function;

	function ps2Time(t_ps : integer) return time is
	begin
		return t_ps * 1 ps;
	end function;

	function ns2Time(t_ns : integer) return time is
	begin
		return t_ns * 1 ns;
	end function;

	function us2Time(t_us : integer) return time is
	begin
		return t_us * 1 us;
	end function;

	function ms2Time(t_ms : integer) return time is
	begin
		return t_ms * 1 ms;
	end function;

	function sec2Time(t_sec : integer) return time is
	begin
		return t_sec * 1 sec;
	end function;

	function fs2Time(t_fs : REAL) return time is
	begin
		return t_fs * 1 fs;
	end function;

	function ps2Time(t_ps : REAL) return time is
	begin
		return t_ps * 1 ps;
	end function;

	function ns2Time(t_ns : REAL) return time is
	begin
		return t_ns * 1 ns;
	end function;

	function us2Time(t_us : REAL) return time is
	begin
		return t_us * 1 us;
	end function;

	function ms2Time(t_ms : REAL) return time is
	begin
		return t_ms * 1 ms;
	end function;

	function sec2Time(t_sec : REAL) return time is
	begin
		return t_sec * 1 sec;
	end function;

	-- convert standard types (NATURAL, REAL) to period (TIME)
	-- ===========================================================================
	function Hz2Time(f_Hz : natural) return time is
	begin
		return 1 sec / f_Hz;
	end function;

	function kHz2Time(f_kHz : natural) return time is
	begin
		return 1 ms / f_kHz;
	end function;

	function MHz2Time(f_MHz : natural) return time
	 is
	begin
		return 1 us / f_MHz;
	end function;

	function GHz2Time(f_GHz : natural) return time is
	begin
		return 1 ns / f_GHz;
	end function;

	function Hz2Time(f_Hz : REAL) return time is
	begin
		return 1 sec / f_Hz;
	end function;

	function kHz2Time(f_kHz : REAL) return time is
	begin
		return 1 ms / f_kHz;
	end function;

	function MHz2Time(f_MHz : REAL) return time is
	begin
		return 1 us / f_MHz;
	end function;

	function GHz2Time(f_GHz : REAL) return time is
	begin
		return 1 ns / f_GHz;
	end function;

	-- convert standard types (NATURAL, REAL) to frequency (FREQ)
	-- ===========================================================================
	function Hz2Freq(f_Hz : natural) return FREQ is
	begin
		return f_Hz * 1 Hz;
	end function;

	function kHz2Freq(f_kHz : natural) return FREQ is
	begin
		return f_kHz * 1 kHz;
	end function;

	function MHz2Freq(f_MHz : natural) return FREQ is
	begin
		return f_MHz * 1 MHz;
	end function;

	function GHz2Freq(f_GHz : natural) return FREQ is
	begin
		return f_GHz * 1 GHz;
	end function;

	function Hz2Freq(f_Hz : REAL) return FREQ is
	begin
		return f_Hz * 1 Hz;
	end function;

	function kHz2Freq(f_kHz : REAL )return FREQ is
	begin
		return f_kHz * 1 kHz;
	end function;

	function MHz2Freq(f_MHz : REAL )return FREQ is
	begin
		return f_MHz * 1 MHz;
	end function;

	function GHz2Freq(f_GHz : REAL )return FREQ is
	begin
		return f_GHz * 1 GHz;
	end function;

	-- convert physical types to standard type (REAL)
	-- ===========================================================================
	function to_real(t : time; scale : time) return REAL is
	begin
		if		(scale = 1	fs) then	return div(t, 1	 fs);
		elsif	(scale = 1	ps) then	return div(t, 1	 ps);
		elsif	(scale = 1	ns) then	return div(t, 1	 ns);
		elsif	(scale = 1	us) then	return div(t, 1	 us);
		elsif	(scale = 1	ms) then	return div(t, 1	 ms);
		elsif	(scale = 1 sec) then	return div(t, 1 sec);
		else	report "to_real: scale must have a value of '1 <unit>'" severity failure;
		return 0.0;
		end if;
	end;

	function to_real(f : FREQ; scale : FREQ) return REAL is
	begin
		if		(scale = 1	Hz) then	return div(f, 1	 Hz);
		elsif	(scale = 1 kHz) then	return div(f, 1 kHz);
		elsif	(scale = 1 MHz) then	return div(f, 1 MHz);
		elsif	(scale = 1 GHz) then	return div(f, 1 GHz);
--	elsif	(scale = 1 THz) then	return div(f, 1 THz);
		else	report "to_real: scale must have a value of '1 <unit>'" severity failure;
		end if;
		return 0.0;
	end;

	function to_real(br : BAUD; scale : BAUD) return REAL is
	begin
		if		(scale = 1	Bd) then	return div(br, 1	Bd);
		elsif	(scale = 1 kBd) then	return div(br, 1 kBd);
		elsif	(scale = 1 MBd) then	return div(br, 1 MBd);
		elsif	(scale = 1 GBd) then	return div(br, 1 GBd);
		else	report "to_real: scale must have a value of '1 <unit>'" severity failure;
		end if;
		return 0.0;
	end;

	function to_real(mem : MEMORY; scale : MEMORY) return REAL is
	begin
		if		(scale = 1 Byte)	then	return div(mem, 1	Byte);
		elsif	(scale = 1 KiB)		then	return div(mem, 1 KiB);
		elsif	(scale = 1 MiB)		then	return div(mem, 1 MiB);
		elsif	(scale = 1 GiB)		then	return div(mem, 1 GiB);
		else	report "to_real: scale must have a value of '1 <unit>'" severity failure;
		end if;
		return 0.0;
	end;

	-- convert physical types to standard type (INTEGER)
	-- ===========================================================================
	function to_int(t : time; scale : time; RoundingStyle : T_ROUNDING_STYLE := ROUND_TO_NEAREST) return integer is
	begin
		case RoundingStyle is
			when ROUND_UP =>					return integer(ceil(to_real(t, scale)));
			when ROUND_DOWN =>				return integer(floor(to_real(t, scale)));
			when ROUND_TO_NEAREST =>	return integer(round(to_real(t, scale)));
			when others =>						null;
		end case;
		report "to_int: unsupported RoundingStyle: " & T_ROUNDING_STYLE'image(RoundingStyle) severity failure;
		return 0;
	end;

	function to_int(f : FREQ; scale : FREQ; RoundingStyle : T_ROUNDING_STYLE := ROUND_TO_NEAREST) return integer is
	begin
		case RoundingStyle is
			when ROUND_UP =>					return integer(ceil(to_real(f, scale)));
			when ROUND_DOWN =>				return integer(floor(to_real(f, scale)));
			when ROUND_TO_NEAREST =>	return integer(round(to_real(f, scale)));
			when others =>						null;
		end case;
		report "to_int: unsupported RoundingStyle: " & T_ROUNDING_STYLE'image(RoundingStyle) severity failure;
		return 0;
	end;

	function to_int(br : BAUD; scale : BAUD; RoundingStyle : T_ROUNDING_STYLE := ROUND_TO_NEAREST) return integer is
	begin
		case RoundingStyle is
			when ROUND_UP =>					return integer(ceil(to_real(br, scale)));
			when ROUND_DOWN =>				return integer(floor(to_real(br, scale)));
			when ROUND_TO_NEAREST =>	return integer(round(to_real(br, scale)));
			when others =>						null;
		end case;
		report "to_int: unsupported RoundingStyle: " & T_ROUNDING_STYLE'image(RoundingStyle) severity failure;
		return 0;
	end;

	function to_int(mem : MEMORY; scale : MEMORY; RoundingStyle : T_ROUNDING_STYLE := ROUND_UP) return integer is
	begin
		case RoundingStyle is
			when ROUND_UP =>					return integer(ceil(to_real(mem, scale)));
			when ROUND_DOWN =>				return integer(floor(to_real(mem, scale)));
			when ROUND_TO_NEAREST =>	return integer(round(to_real(mem, scale)));
			when others =>						null;
		end case;
		report "to_int: unsupported RoundingStyle: " & T_ROUNDING_STYLE'image(RoundingStyle) severity failure;
		return 0;
	end;

	-- calculate needed counter cycles to achieve a given 1. timing/delay and 2. frequency/period
	-- ===========================================================================
	--	@param Timing					A given timing or delay, which should be achieved
	--	@param Clock_Period		The period of the circuits clock
	--	@RoundingStyle				Default = ROUND_UP; other choises: ROUND_UP, ROUND_DOWN, ROUND_TO_NEAREST
	function TimingToCycles(Timing : time; Clock_Period : time; RoundingStyle : T_ROUNDING_STYLE := ROUND_UP) return natural is
		variable res_real	: REAL;
		variable res_nat	: natural;
		variable res_time	: time;
		variable res_dev	: REAL;
	begin
		res_real := div(Timing, Clock_Period);
		case RoundingStyle is
			when ROUND_TO_NEAREST =>	res_nat := natural(round(res_real));
			when ROUND_UP =>					res_nat := natural(ceil(res_real));
			when ROUND_DOWN =>				res_nat := natural(floor(res_real));
			when others =>	report "RoundingStyle '" & T_ROUNDING_STYLE'image(RoundingStyle) & "' not supported." severity failure;
		end case;
		res_time	:= CyclesToDelay(res_nat, Clock_Period);
		res_dev		:= (div(res_time, Timing) - 1.0) * 100.0;

		if POC_VERBOSE then
			report "TimingToCycles: " & 	LF &
						 "  Timing: " &					to_string(Timing, 3) & LF &
						 "  Clock_Period: " &		to_string(Clock_Period, 3) & LF &
						 "  RoundingStyle: " &	str_substr(T_ROUNDING_STYLE'image(RoundingStyle), 7) & LF &
						 "  res_real = " &			str_format(res_real, 3) & LF &
						 "  => " &							integer'image(res_nat)
			severity note;
		end if;

		if C_PHYSICAL_REPORT_TIMING_DEVIATION then
			report "TimingToCycles (timing deviation report): " & LF &
						 "  timing to achieve: " & to_string(Timing, 3) & LF &
						 "  calculated cycles: " & integer'image(res_nat) & " cy" & LF &
						 "  resulting timing:  " & to_string(res_time, 3) & LF &
						 "  deviation:         " & to_string(res_time - Timing, 3) & " (" & str_format(res_dev, 2) & "%)"
			severity note;
		end if;

		return res_nat;
	end;

	function TimingToCycles(Timing : time; Clock_Frequency	: FREQ; RoundingStyle : T_ROUNDING_STYLE := ROUND_UP) return natural is
	begin
		return TimingToCycles(Timing, to_time(Clock_Frequency), RoundingStyle);
	end function;

	function CyclesToDelay(Cycles : natural; Clock_Period : time) return time is
	begin
		return Clock_Period * Cycles;
	end function;

	function CyclesToDelay(Cycles : natural; Clock_Frequency : FREQ) return time is
	begin
		return CyclesToDelay(Cycles, to_time(Clock_Frequency));
	end function;

	-- convert and format physical types to STRING
	function to_string(t : time; precision : natural) return string is
		variable tt     : time;
		variable unit		: string(1 to 3)	:= (others => C_POC_NUL);
		variable value	: REAL;
	begin
		tt := abs t;
		if (tt < 1 ps) then
			unit(1 to 2)	:= "fs";
			value					:= to_real(tt, 1 fs);
		elsif (tt < 1 ns) then
			unit(1 to 2)	:= "ps";
			value					:= to_real(tt, 1 ps);
		elsif (tt < 1 us) then
			unit(1 to 2)	:= "ns";
			value					:= to_real(tt, 1 ns);
		elsif (tt < 1 ms) then
			unit(1 to 2)	:= "us";
			value					:= to_real(tt, 1 us);
		elsif (tt < 1 sec) then
			unit(1 to 2)	:= "ms";
			value					:= to_real(tt, 1 ms);
		else
			unit					:= "sec";
			value					:= to_real(tt, 1 sec);
		end if;

		return ite(t >= 0 fs, str_format(value, precision) & " " & str_trim(unit),
							      '-' & str_format(value, precision) & " " & str_trim(unit));
	end function;

	function to_string(f : FREQ; precision : natural) return string is
		variable unit		: string(1 to 3)	:= (others => C_POC_NUL);
		variable value	: REAL;
	begin
		if (f < 1 kHz) then
			unit(1 to 2)	:= "Hz";
			value					:= to_real(f, 1 Hz);
		elsif (f < 1 MHz) then
			unit					:= "kHz";
			value					:= to_real(f, 1 kHz);
		elsif (f < 1 GHz) then
			unit					:= "MHz";
			value					:= to_real(f, 1 MHz);
		else
			unit					:= "GHz";
			value					:= to_real(f, 1 GHz);
		end if;

		return str_format(value, precision) & " " & str_trim(unit);
	end function;

	function to_string(br : BAUD; precision : natural) return string is
		variable unit		: string(1 to 3)	:= (others => C_POC_NUL);
		variable value	: REAL;
	begin
		if (br < 1 kBd) then
			unit(1 to 2)	:= "Bd";
			value					:= to_real(br, 1 Bd);
		elsif (br < 1 MBd) then
			unit					:= "kBd";
			value					:= to_real(br, 1 kBd);
		elsif (br < 1 GBd) then
			unit					:= "MBd";
			value					:= to_real(br, 1 MBd);
		else
			unit					:= "GBd";
			value					:= to_real(br, 1 GBd);
		end if;

		return str_format(value, precision) & " " & str_trim(unit);
	end function;

	function to_string(mem : MEMORY; precision : natural) return string is
		variable unit		: string(1 to 3)	:= (others => C_POC_NUL);
		variable value	: REAL;
	begin
		if (mem < 1 KiB) then
			unit(1)				:= 'B';
			value					:= to_real(mem, 1 Byte);
		elsif (mem < 1 MiB) then
			unit					:= "KiB";
			value					:= to_real(mem, 1 KiB);
		elsif (mem < 1 GiB) then
			unit					:= "MiB";
			value					:= to_real(mem, 1 MiB);
		else
			unit					:= "GiB";
			value					:= to_real(mem, 1 GiB);
		end if;

		return str_format(value, precision) & " " & str_trim(unit);
	end function;

end package body;