-----------------------------------------------------------------------
-- $Header: D:\usr\cvsroot/VHDL/lib/misc/utils_pkg.vhd,v 1.6 2010/03/22 07:25:40 Jens Exp $
-----------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;

package utils_pkg is
	
	-- Constants

	-- Functions
	function MIN (X, Y: INTEGER) return INTEGER;
	function MAX (X, Y: INTEGER) return INTEGER;

	function NextPowerOfTwo(x : real) return real;
	function NextExpBaseTwo(x : real) return real;
	function NextPowerOfTwo(x : natural) return natural;
	function NextExpBaseTwo(x : natural) return natural;
	function GCD(a, b : natural) return natural;
	function LCM(a, b : natural) return natural;
	function UTILS_PERIOD_NS(f_in_MHz : real) return real;
	function UTILS_FREQ_M(f_in_MHz, f_out_MHz : real) return natural;
	function UTILS_FREQ_D(f_in_MHz, f_out_MHz : real) return natural;
	function f_correct(f : real) return natural;
	
end utils_pkg;


package body utils_pkg is

	-------------------------------------------------------------
	function MIN (X, Y: INTEGER) return INTEGER is
	variable res : integer := X;
	begin
	if Y < X then
		res := Y;
	end if;

	return res;

	end MIN;

	-------------------------------------------------------------
	function MAX (X, Y: INTEGER) return INTEGER is
	variable res : integer := X;
	begin
	if Y > X then
		res := Y;
	end if;

	return res;
	
	end MAX;

	-------------------------------------------------------------
	function NextExpBaseTwo(x : real) return real is
	begin
		return ceil(log2(x));
	end NextExpBaseTwo;	
	
	-------------------------------------------------------------
	function NextPowerOfTwo(x : real) return real is
	begin

		return 2.0**NextExpBaseTwo(x);
	end NextPowerOfTwo;	

	-------------------------------------------------------------
	function NextExpBaseTwo(x : natural) return natural is
	begin
		return natural(NextExpBaseTwo(real(x)));
	end NextExpBaseTwo;	
	
	-------------------------------------------------------------
	function NextPowerOfTwo(x : natural) return natural is
	begin

		return 2**NextExpBaseTwo(x);
	end NextPowerOfTwo;	
	
	-------------------------------------------------------------
	function GCD(a, b : natural) return natural is
	variable aa : natural;
	variable bb : natural;
	begin
		aa := a;
		bb := b;
		while bb /= 0 loop
			if aa > bb then
				aa := aa - bb;
			else
				bb := bb - aa;
			end if;
		end loop;
		
		return aa;

	end GCD;	

	-------------------------------------------------------------
	function LCM(a, b : natural) return natural is
	begin

		return (a * b)/GCD(a, b);

	end LCM;	

	-------------------------------------------------------------
	function UTILS_PERIOD_NS(f_in_MHz : real) return real is
	begin
		
		return 1.0E3/f_in_MHz;

	end UTILS_PERIOD_NS;	
	
	-------------------------------------------------------------
	function UTILS_FREQ_M(f_in_MHz, f_out_MHz : real) return natural is
	variable f_in  : natural;
	variable f_out : natural;
	variable C : natural;
	variable M : natural;
	variable D : natural;
	begin
		C := f_correct(f_out_MHz);
		if C < 2 then
			C := f_correct(f_in_MHz);
		end if;

		f_out := natural(ceil(real(C)*f_out_MHz));
		f_in  := natural(ceil(real(C)*f_in_MHz));

		M := LCM(f_in, f_out)/f_in;
		D := f_in/GCD(f_in, f_out);

		assert (M*D) /= (f_in*f_out) report "Finding M and D failed!" severity failure;

		if M = 1 then
			M := 2;
		end if;

		return M;
		
	end UTILS_FREQ_M;	

	-------------------------------------------------------------
	function UTILS_FREQ_D(f_in_MHz, f_out_MHz : real) return natural is
	variable f_in  : natural;
	variable f_out : natural;
	variable C : natural;
	variable M : natural;
	variable D : natural;
	begin
		C := f_correct(f_out_MHz);
		if C < 2 then
			C := f_correct(f_in_MHz);
		end if;

		f_out := natural(ceil(real(C)*f_out_MHz));
		f_in  := natural(ceil(real(C)*f_in_MHz));

		M := LCM(f_in, f_out)/f_in;
		D := f_in/GCD(f_in, f_out);

		assert (M*D) /= (f_in*f_out) report "Finding M and D failed!" severity failure;

		if M = 1 then
			D := 2*D;
		end if;
		
		return D;

	end UTILS_FREQ_D;	

	-------------------------------------------------------------
	function f_correct(f : real) return natural is
	constant MAX_ITER	: positive	:= 100;
	constant MAX_ERR	: real		:= 0.01;
	variable fpart		: real;
	variable err		: real;
	begin
		for i in 1 to MAX_ITER loop
			fpart := real(i)*f - ceil(real(i)*f);
			err := abs(fpart);
			if err < max_err then
				return i;
			end if;
		end loop;
	end f_correct;	

	-------------------------------------------------------------
end utils_pkg;


