------------------------------------------------------------------
----                                                          ----
----  Content: Numeric System Package                         ----
----           for Stillwater KPUs                            ----
----                                                          ----
----  Author:  E. Theodore L. Omtzigt                         ----
----           theo@stillwater-sc.com                         ----
----                                                          ----
------------------------------------------------------------------
----                                                          ----
---- Copyright (C) 2017-2018                                  ----
----               E. Theodore L. Omtzigt                     ----
----               theo@stillwater-sc.com                     ----
----                                                          ----
------------------------------------------------------------------
---
---- A posit is a tapered floating point representation. 
---- To compute with posits, the regime and exponent fields 
---- need to be consolidated. This process generates a triple 
----                (sign, exponent, fraction)
---- These triples are the input values to the arithmetic units
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

--The numeric_system package defines the number system that the
--overall system will be using. We are unifying integers, floats, and posits
--by converting them to a (sign, scale, fraction) triple. 
--
--Each number system, that is, integer, float, posit, valid, will have
--a slightly different relationship between <NBITS,ES> and bit sizes
--for scale and fraction. This pkg configures the root of that configuration.

--	static constexpr size_t escale = size_t(1) << es;         // 2^es
--	static constexpr size_t range = escale * (4 * nbits - 8); // dynamic range of the posit configuration
--	static constexpr size_t half_range = range >> 1;          // position of the fixed point
--	static constexpr size_t radix_point = half_range;
--	// the upper is 1 bit bigger than the lower because maxpos^2 has that scale
--	static constexpr size_t upper_range = half_range + 1;     // size of the upper accumulator
--	static constexpr size_t qbits = range + capacity;     // size of the quire minus the sign bit: we are managing the sign explicitly

package numeric_system_pkg is

	generic(
		NBITS : positive := 8;          -- number of bits to represent encoding
		EBITS : natural  := 0;          -- number of bits to represent exponent
		SBITS : positive := 5;          -- number of bits to represent scale
		FBITS : natural  := 5;          -- number of bits to represent the fraction
		CBITS : natural  := 10          -- number of bits to represent quire capacity
	);

	-- arithmetic property constants
	constant GUARDBITS : natural := 3;

	impure function adder_width return positive;
	impure function multiplier_width return positive;
	impure function divider_width return positive;
	impure function posit_dynamic_range return positive;
	impure function quire_width return positive;

	-- derived entities
	constant BITS_ADDER_OUT      : positive := FBITS + 2;
	constant BITS_MULTIPLIER_OUT : positive := 2 * FBITS + 1;
	constant BITS_DIVIDER_OUT    : positive := 2 * FBITS + 3;
	constant BITS_MAX_ARITH_OUT  : positive := 2 * FBITS + 3;
	constant BITS_DYNAMIC_RANGE  : positive := (2**EBITS) * (4 * NBITS - 8);
	constant BITS_QUIRE_WIDTH    : positive := BITS_DYNAMIC_RANGE + CBITS;

	-- posits that don't have fraction bits, still have a hidden bit
	-- don't know what FBITS - 1 downto 0 evaluates to: possibly empty/null and thus trouble

	-- posit encoding is a 2's complement encoding with respect to relational operators
	subtype posit_word is signed(NBITS - 1 downto 0);
	-- scales are integers and operators on scale are add/sub
	subtype scale_word is signed(SBITS - 1 downto 0);
	-- fractions are unsigned binary values with radix at FBITS-1
	subtype fraction_word is unsigned(FBITS - 1 downto 0);

	-- ssf triple === (sign, scale, fraction) normal form
	-- ssf triples are stored in the register file
	type ssf_type is record
		isNaR    : std_logic;           -- encoding special case of Not a Real
		isZero   : std_logic;           -- encoding special case of 0
		sign     : std_logic;           -- sign
		scale    : scale_word;          -- scale of the posit: useed ^ k * 2^e
		fraction : fraction_word;
	end record;

	subtype add_significant_word is std_logic_vector(BITS_ADDER_OUT - 1 downto 0);
	-- sss triple === (sign, scale, significant) output of an adder/subtractor
	-- these records flow to the quire and the posit rounding stage
	type sss_add_output_type is record
		valid       : std_logic;
		isNaR       : std_logic;        -- encoding special case of Not a Real
		isZero      : std_logic;        -- encoding special case of 0
		sign        : std_logic;        -- sign
		scale       : scale_word;       -- scale of the posit: useed ^ k * 2^e
		significant : add_significant_word;
	end record;

	subtype mul_significant_word is std_logic_vector(BITS_MULTIPLIER_OUT - 1 downto 0);
	-- sss triple === (sign, scale, signficant) output of a multiplier
	-- these records flow to the quire and the posit rounding stage
	type sss_mul_output_type is record
		valid       : std_logic;
		isNaR       : std_logic;        -- encoding special case of Not a Real
		isZero      : std_logic;        -- encoding special case of 0
		sign        : std_logic;        -- sign
		scale       : scale_word;       -- scale of the posit: useed ^ k * 2^e
		significant : mul_significant_word;
	end record;

	subtype div_significant_word is std_logic_vector(BITS_DIVIDER_OUT - 1 downto 0);
	-- sss triple === (sign, scale, signficant) output of a divider
	-- these records flow to the quire and the posit rounding stage
	type sss_div_output_type is record
		isNaR       : std_logic;        -- encoding special case of Not a Real
		isZero      : std_logic;        -- encoding special case of 0
		sign        : std_logic;        -- sign
		scale       : scale_word;       -- scale of the posit: useed ^ k * 2^e
		significant : div_significant_word;
	end record;

	-- max sss triple that can come out of the arithmetic data path.
	-- all smaller sss triples are right extended with '0's to unify
	type sss_max_result_type is record
		isNaR       : std_logic;        -- encoding special case of Not a Real
		isZero      : std_logic;        -- encoding special case of 0
		sign        : std_logic;        -- sign
		scale       : scale_word;       -- scale of the posit: useed ^ k * 2^e
		significant : std_logic_vector(BITS_MAX_ARITH_OUT - 1 downto 0);
	end record;

	-- input record to the ALU
	type alu_input_type is record
		opcode    : std_logic_vector(2 downto 0); -- instruction to execute: add/sub, mul/div, recip, sqrt
		operand_1 : ssf_type;
		operand_2 : ssf_type;
		operand_3 : ssf_type;
	end record;

	type quire_cmd_type is (LOAD, STORE, CLEAR, NOP); -- ACCUMULATE is default: keeps to 2 bits

	-- are we managing the quire as a sign-magnitude or a 2's complement accumulator?
	-- subtype quire_word is unsigned(BITS_QUIRE_WIDTH - 1 downto 0);
	subtype quire_word is signed(BITS_QUIRE_WIDTH - 1 downto 0);
	constant QUIRE_ZERO : quire_word := (others => '0');

	type quire_in_type is record
		cmd  : quire_cmd_type;
		d_in : quire_word;              -- for quire load
	end record;

	subtype quire_in_reg is ssf_type;   -- adding a raw posit value out of the posit registers
	subtype quire_in_add is sss_add_output_type; -- adding the result of a posit addition without rounding
	subtype quire_in_mul is sss_mul_output_type; -- adding the result of a posit multiplication without rounding

	type quire_state_type is (QS_ZERO, QS_NEG, QS_POS, QS_OVERFLOW);

	type quire_out_type is record
		state : quire_state_type;
		q_out : quire_word;             -- for quire store
	end record;

	-- stringer for ssf tripslet
	function ssf_to_string(
		tag     : String;
		triplet : ssf_type
	) return String;

	-- compare two ssf triplets
	function cmpSSF(
		val : ssf_type;
		ref : ssf_type
	) return boolean;

	-- report the configuration and the derived constants
	procedure reportConfiguration;

end package;

package body numeric_system_pkg is

	impure function adder_width return positive is
		constant fbits_value : positive := FBITS;
	begin
		return fbits_value + 2;
	end function;

	impure function multiplier_width return positive is
		constant fbits_value : positive := FBITS;
	begin
		return 2 * fbits_value + 1;
	end function;

	impure function divider_width return positive is
		constant fbits_value : positive := FBITS;
	begin
		return 2 * fbits_value + 3;
	end function;

	impure function posit_dynamic_range return positive is
		constant ebits_value : natural  := EBITS;
		constant nbits_value : positive := NBITS;
		variable escale      : positive := 1;
	begin
		escale := 2 ** ebits_value;
		return escale * (4 * nbits_value - 8);
	end function;

	impure function quire_width return positive is
		constant ebits_value : natural  := EBITS;
		constant nbits_value : positive := NBITS;
		constant cbits_value : positive := CBITS;
		variable escale      : positive := 1;
		variable qw          : positive := 1;
	begin
		escale := 2 ** ebits_value;
		qw     := escale * (4 * nbits_value - 8) + cbits_value;
		return qw;
	end function;

	procedure reportConfiguration is
	begin
		report "Configuration Parameters";
		report "  NBITS               = " & to_string(NBITS);
		report "  EBITS               = " & to_string(EBITS);
		report "  SBITS               = " & to_string(SBITS);
		report "  FBITS               = " & to_string(FBITS);
		report "  CBITS               = " & to_string(CBITS);

		report "  GUARDBITS           = " & to_string(GUARDBITS);

		report "Derived Constants";
		report "  BITS_ADDER_OUT      : " & to_string(BITS_ADDER_OUT);
		report "  BITS_MULTIPLIER_OUT : " & to_string(BITS_MULTIPLIER_OUT);
		report "  BITS_DIVIDER_OUT    : " & to_string(BITS_DIVIDER_OUT);
		report "  BITS_MAX_ARITH_OUT  : " & to_string(BITS_MAX_ARITH_OUT);
		report "  BITS_DYNAMIC_RANGE  : " & to_string(BITS_DYNAMIC_RANGE);
		report "  BITS_QUIRE_WIDTH    : " & to_string(BITS_QUIRE_WIDTH);
	end procedure;

	function ssf_to_string(
		tag     : String;
		triplet : ssf_type
	) return String is
	begin
		if triplet.isZero = '1' then
			return tag & " = (zero)";
		elsif triplet.isNaR = '1' then
			return tag & " = (NaR)";
		else
			return tag & " = (" & to_string(triplet.sign) & "," & to_string(triplet.scale) & "," & to_string(triplet.fraction) & ")";
		end if;
	end function;

	function cmpSSF(
		val : ssf_type;
		ref : ssf_type
	) return boolean is
	begin
		-- first check special cases
		if val.isZero = '1' AND val.isZero = ref.isZero then
			return true;
		end if;
		if val.isNaR = '1' AND val.isNaR = ref.isNaR then
			return true;
		end if;
		-- no special case, so compare ssf values
		if val.sign = ref.sign AND val.scale = ref.scale AND val.fraction = ref.fraction then
			return true;
		else
			return false;
		end if;

	end function;

end package body numeric_system_pkg;

------------------------------------------------------------------
---                    posit configuration                     ---
------------------------------------------------------------------
-- configuration for a posit<5,1>@1.0   : [s] [rr] [e] [f] [ggg]
-- es = 1 -> useed = 2^2^1 = 4
-- max k = nbits-2 = 3 -> 4^3 = 2^6 -> maxpos = 64
-- fraction bits [h] [f] [ggg]  -> 1 hidden bit, 1 fraction bit, 3 guard bits  == 5 bits total
-- adder out: 5 + 1 = 6

-- configuration for a posit<8,0>@1.0 : [s] [rr] [] [fffff] [ggg]
-- es = 0 -> useed = 2^2^0 = 2
-- max k = nbits-2 = 6 -> 2^6      -> maxpos = 64 -> scale is 6+1 bits
-- fraction bits [h] [ffff] [ggg]  -> 1 hidden bit, 4 fraction bit, 3 guard bits  == 8 bits total
-- adder out: 8 + 1 = 9

-- configuration for a posit<8,1>@1.0 : [s] [rr] [e] [ffff] [ggg]
-- es = 1 -> useed = 2^2^1 = 4
-- max k = nbits-2 = 6 -> 4^6 = 2^12 -> maxpos = 4096 -> scale is 12+1 bits
-- fraction bits [h] [ffff] [ggg]  -> 1 hidden bit, 4 fraction bit, 3 guard bits  == 8 bits total
-- adder out: 8 + 1 = 9

------------------------------------------------------------------
---                    float configuration                     ---
------------------------------------------------------------------
-- configuation for a 8-bit float      : [s] [ee] [f ffff] [ggg]
-- nbits = 8, es = 2
-- configuation for a 16-bit float     : [s] [eeee e] [ff ffff ffff] [ggg]
-- nbits = 16, es = 5
-- configuation for a 32-bit float     : [s] [eeee eeee] [fff ffff ffff ffff ffff ffff] [ggg]
-- nbits = 32, es = 8

-- Standard posit configurations
-- posit<  8,0> useed scale     1     minpos scale         -6     maxpos scale          6
-- posit< 16,1> useed scale     2     minpos scale        -28     maxpos scale         28
-- posit< 32,2> useed scale     4     minpos scale       -120     maxpos scale        120
-- posit< 64,3> useed scale     8     minpos scale       -496     maxpos scale        496

package p4e0 is new work.numeric_system_pkg
	generic map(
		NBITS => 4,                     -- 4 encoding bits
		EBITS => 0,                     -- no exponent bits
		SBITS => 3,                     -- posit<4,0> scale ranges from -2 to 2
		FBITS => 1,                     -- one fraction bit
		CBITS => 10                     -- 10 capacity bits for the quire		
	);

package p5e0 is new work.numeric_system_pkg
	generic map(
		NBITS => 5,                     -- 5 encoding bits
		EBITS => 0,                     -- no exponent bits
		SBITS => 4,                     -- posit<5,0> scale ranges from -3 to 3
		FBITS => 2,                     -- 2 fraction bits
		CBITS => 10                     -- 10 capacity bits for the quire
	);

package p8e0 is new work.numeric_system_pkg
	generic map(
		NBITS => 8,                     -- 8 encoding bits
		EBITS => 0,                     -- no exponent bits
		SBITS => 5,                     -- posit<8,0> scale ranges from -6 to 6
		FBITS => 5,                     -- 5 fraction bits
		CBITS => 10                     -- 10 capacity bits for the quire
	);

package p8e1 is new work.numeric_system_pkg
	generic map(
		NBITS => 8,                     -- 8 encoding bits
		EBITS => 1,                     -- 1 exponent bit
		SBITS => 6,                     -- posit<8,1> scale ranges from -12 to 12
		FBITS => 4,                     -- 4 fraction bits
		CBITS => 10                     -- 10 capacity bits for the quire
	);

package p8e2 is new work.numeric_system_pkg
	generic map(
		NBITS => 8,                     -- 8 encoding bits
		EBITS => 2,                     -- 2 exponent bits
		SBITS => 7,                     -- posit<8,2> scale ranges from -24 to 24
		FBITS => 3,                     -- 3 fraction bits
		CBITS => 10                     -- 10 capacity bits for the quire
	);

package p16e1 is new work.numeric_system_pkg
	generic map(
		NBITS => 16,                    -- 16 encoding bits
		EBITS => 1,                     -- one exponent bit
		SBITS => 7,                     -- posit<16,1> scale ranges from -28 to 28
		FBITS => 12,                    -- 12 fraction bits
		CBITS => 10                     -- 10 capacity bits for the quire
	);

-- posit<  8,0> useed scale     1     minpos scale         -6     maxpos scale          6
-- posit<  8,1> useed scale     2     minpos scale        -12     maxpos scale         12
-- posit<  8,2> useed scale     4     minpos scale        -24     maxpos scale         24
-- posit< 16,0> useed scale     1     minpos scale        -14     maxpos scale         14
-- posit< 16,1> useed scale     2     minpos scale        -28     maxpos scale         28
-- posit< 16,2> useed scale     4     minpos scale        -56     maxpos scale         56

------------------------------------------------------------------
----                                                          ----
----  Content: Data Path Configuration Package                ----
----           for Stillwater KPUs                            ----
----                                                          ----
----  Author:  E. Theodore L. Omtzigt                         ----
----           theo@stillwater-sc.com                         ----
----                                                          ----
------------------------------------------------------------------
----                                                          ----
---- Copyright (C) 2017-2018                                  ----
----               E. Theodore L. Omtzigt                     ----
----               theo@stillwater-sc.com                     ----
----                                                          ----
------------------------------------------------------------------

-- generic configuration package name that we can use in the components
-- and can change here to create different configurations
package config_pkg is new work.numeric_system_pkg
	generic map(
		NBITS => 8,                     -- 8 encoding bits
		EBITS => 1,                     -- no exponent bits
		SBITS => 5,                     -- posit<8,0> scale ranges from -6 to 6
		FBITS => 5,                     -- 5 fraction bits
		CBITS => 8                      -- 8 capacity bits for quire
	);

