Adds initial version of the testbench

This commit is contained in:
Johannes Kutning
2025-11-10 21:50:44 +01:00
parent 65d16659d0
commit 04768b5d14
11 changed files with 297 additions and 157 deletions
+9
View File
File diff suppressed because one or more lines are too long
+144
View File
@@ -0,0 +1,144 @@
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
package float is
constant SIGN : std_logic_vector( 31 downto 31 ) := ( others => '0' );
constant EXP : std_logic_vector( 30 downto 23 ) := ( others => '0' );
constant MANTISSA : std_logic_vector( 22 downto 0 ) := ( others => '0' );
function find_leftmost( arg : unsigned; value : std_ulogic ) return integer;
function count_leading_digits( arg : unsigned; value : std_ulogic ) return integer;
function to_float( arg : std_logic_vector ) return std_logic_vector;
function to_fixed( arg : std_logic_vector ) return std_logic_vector;
end package float;
package body float is
function find_leftmost( arg : unsigned; value : std_ulogic ) return integer is
begin
for i in arg'left downto arg'right loop
if ( arg( i ) = value ) then
return i;
end if;
end loop;
return -1;
end function find_leftmost;
function count_leading_digits( arg : unsigned; value : std_ulogic ) return integer is
variable left_most_value : integer range -1 to arg'high;
variable leading_values : integer range 0 to arg'high;
begin
left_most_value := find_leftmost( arg, not value );
leading_values := 0;
if ( left_most_value /= -1 ) then
leading_values := arg'high - left_most_value;
end if;
return leading_values;
end function count_leading_digits;
function to_float( arg : std_logic_vector ) return std_logic_vector is
variable y : std_logic_vector( 31 downto 0 );
variable s : std_logic;
variable e : unsigned( 7 downto 0 );
variable m : unsigned( 22 downto 0 );
variable value : unsigned( 30 downto 0 );
variable leading_sign_digits : integer range 0 to value'high;
variable reminding : integer range 0 to value'high;
begin
s := arg( 31 );
if ( s = '0' ) then
value := unsigned( arg( 30 downto 0 ) );
else
value := not unsigned( arg( 30 downto 0 ) );
value := value +1;
end if;
leading_sign_digits := count_leading_digits( value, '0' );
reminding := value'high - leading_sign_digits;
e := to_unsigned( 126 - leading_sign_digits, e'length );
if ( reminding > m'length ) then
m := value( reminding - 1 downto reminding - m'length );
elsif ( reminding > 1 ) then
m := ( others => '0' );
m( m'high downto m'high - reminding + 1 ) := value( reminding - 1 downto 0 );
else
m := ( others => '0' );
end if;
if (arg = x"00000000") then
y := (others => '0');
else
y := s & std_logic_vector( e ) & std_logic_vector( m );
end if;
return y;
end function to_float;
function to_fixed( arg : std_logic_vector ) return std_logic_vector is
variable y : unsigned( 31 downto 0 );
variable s : std_logic;
variable e : unsigned( 7 downto 0 );
variable m_index_max : integer range -127 to 128;
begin
s := arg( 31 );
e := unsigned(arg(30 downto 23));
m_index_max := to_integer(signed(30-(126-e)));
if (arg = x"00000000") then
y := (others => '0');
else
y := (others => '0');
case m_index_max is
when 30 => y(30 downto 7):= '1' & unsigned( arg(22 downto 0) );
when 29 => y(29 downto 6):= '1' & unsigned( arg(22 downto 0) );
when 28 => y(28 downto 5):= '1' & unsigned( arg(22 downto 0) );
when 27 => y(27 downto 4):= '1' & unsigned( arg(22 downto 0) );
when 26 => y(26 downto 3):= '1' & unsigned( arg(22 downto 0) );
when 25 => y(25 downto 2):= '1' & unsigned( arg(22 downto 0) );
when 24 => y(24 downto 1):= '1' & unsigned( arg(22 downto 0) );
when 23 => y(23 downto 0):= '1' & unsigned( arg(22 downto 0) );
when 22 => y(22 downto 0):= '1' & unsigned( arg(22 downto 1) );
when 21 => y(21 downto 0):= '1' & unsigned( arg(22 downto 2) );
when 20 => y(20 downto 0):= '1' & unsigned( arg(22 downto 3) );
when 19 => y(19 downto 0):= '1' & unsigned( arg(22 downto 4) );
when 18 => y(18 downto 0):= '1' & unsigned( arg(22 downto 5) );
when 17 => y(17 downto 0):= '1' & unsigned( arg(22 downto 6) );
when 16 => y(16 downto 0):= '1' & unsigned( arg(22 downto 7) );
when 15 => y(15 downto 0):= '1' & unsigned( arg(22 downto 8) );
when 14 => y(14 downto 0):= '1' & unsigned( arg(22 downto 9) );
when 13 => y(13 downto 0):= '1' & unsigned( arg(22 downto 10) );
when 12 => y(12 downto 0):= '1' & unsigned( arg(22 downto 11) );
when 11 => y(11 downto 0):= '1' & unsigned( arg(22 downto 12) );
when 10 => y(10 downto 0):= '1' & unsigned( arg(22 downto 13) );
when 9 => y(9 downto 0):= '1' & unsigned( arg(22 downto 14) );
when 8 => y(8 downto 0):= '1' & unsigned( arg(22 downto 15) );
when 7 => y(7 downto 0):= '1' & unsigned( arg(22 downto 16) );
when 6 => y(6 downto 0):= '1' & unsigned( arg(22 downto 17) );
when 5 => y(5 downto 0):= '1' & unsigned( arg(22 downto 18) );
when 4 => y(4 downto 0):= '1' & unsigned( arg(22 downto 19) );
when 3 => y(3 downto 0):= '1' & unsigned( arg(22 downto 20) );
when 2 => y(2 downto 0):= '1' & unsigned( arg(22 downto 21) );
when 1 => y(1 downto 0):= '1' & unsigned( arg(22 downto 22) );
when 0 => y(0):= '1';
when others => null;
end case;
if ( s = '1' ) then
y := not(y);
y:= y + x"00000001";
end if;
end if;
return std_logic_vector( y );
end function to_fixed;
end package body float;
+133
View File
@@ -0,0 +1,133 @@
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity float_add is
port(A : in std_logic_vector(31 downto 0);
B : in std_logic_vector(31 downto 0);
clk : in std_logic;
reset : in std_logic;
start : in std_logic;
done : out std_logic;
sum : out std_logic_vector(31 downto 0)
);
end float_add;
architecture mixed of float_add is
type ST is (WAIT_STATE, ALIGN_STATE, ADDITION_STATE, NORMALIZE_STATE, OUTPUT_STATE);
signal state : ST := WAIT_STATE;
---Internal Signals latched from the inputs
signal A_mantissa, B_mantissa : std_logic_vector (24 downto 0);
signal A_exp, B_exp : std_logic_vector (8 downto 0);
signal A_sgn, B_sgn : std_logic;
--Internal signals for Output
signal sum_exp: std_logic_vector (8 downto 0);
signal sum_mantissa : std_logic_vector (24 downto 0);
signal sum_sgn : std_logic;
begin
Control_Unit : process (clk, reset) is
variable diff : signed(8 downto 0);
begin
if(reset = '1') then
state <= WAIT_STATE; --start in wait state
done <= '0';
elsif rising_edge(clk) then
case state is
when WAIT_STATE =>
if (start = '1') then --wait till start request startes high
A_sgn <= A(31);
A_exp <= '0' & A(30 downto 23); --One bit is added for signed subtraction
A_mantissa <= "01" & A(22 downto 0); --Two bits are added extra, one for leading 1 and other one for storing carry
B_sgn <= B(31);
B_exp <= '0' & B(30 downto 23);
B_mantissa <= "01" & B(22 downto 0);
state <= ALIGN_STATE;
else
state <= WAIT_STATE;
end if;
when ALIGN_STATE => --Compare exponent and align it
--If any num is greater by 2**24, we skip the addition.
if unsigned(A_exp) > unsigned(B_exp) then
--B needs downshifting
diff := signed(A_exp) - signed(B_exp); --Small Alu
if diff > 23 then
sum_mantissa <= A_mantissa; --B insignificant relative to A
sum_exp <= A_exp;
sum_sgn <= A_sgn;
state <= OUTPUT_STATE; --start latch A as output
else
--downshift B to equilabrate B_exp to A_exp
sum_exp <= A_exp;
B_mantissa(24-to_integer(diff) downto 0) <= B_mantissa(24 downto to_integer(diff));
B_mantissa(24 downto 25-to_integer(diff)) <= (others => '0');
state <= ADDITION_STATE;
end if;
elsif unsigned(A_exp) < unsigned(B_exp) then --A_exp < B_exp. A needs downshifting
diff := signed(B_exp) - signed(A_exp); -- Small Alu
if diff > 23 then
sum_mantissa <= B_mantissa; --A insignificant relative to B
sum_sgn <= B_sgn;
sum_exp <= B_exp;
state <= OUTPUT_STATE; --start latch B as output
else
--downshift A to equilabrate A_exp to B_exp
sum_exp <= B_exp;
A_mantissa(24-to_integer(diff) downto 0) <= A_mantissa(24 downto to_integer(diff));
A_mantissa(24 downto 25-to_integer(diff)) <= (others => '0');
state <= ADDITION_STATE;
end if;
else -- Both exponent is equal. No need to mantissa shift
sum_exp <= A_exp;
state <= ADDITION_STATE;
end if;
when ADDITION_STATE => --Mantissa addition
state <= NORMALIZE_STATE;
if (A_sgn xor B_sgn) = '0' then --signs are the same. Just add them
sum_mantissa <= std_logic_vector((unsigned(A_mantissa) + unsigned(B_mantissa))); --Big Alu
sum_sgn <= A_sgn; --both nums have same sign
--Else subtract smaller from larger and use sign of larger
elsif unsigned(A_mantissa) >= unsigned(B_mantissa) then
sum_mantissa <= std_logic_vector((unsigned(A_mantissa) - unsigned(B_mantissa))); --Big Alu
sum_sgn <= A_sgn;
else
sum_mantissa <= std_logic_vector((unsigned(B_mantissa) - unsigned(A_mantissa))); --Big Alu
sum_sgn <= B_sgn;
end if;
when NORMALIZE_STATE => --Normalization.
if unsigned(sum_mantissa) = TO_UNSIGNED(0, 25) then
--The sum is 0
sum_mantissa <= (others => '0');
sum_exp <= (others => '0');
state <= OUTPUT_STATE;
elsif(sum_mantissa(24) = '1') then --If sum overflowed we downshift and are done.
sum_mantissa <= '0' & sum_mantissa(24 downto 1); --shift the 1 down
sum_exp <= std_logic_vector((unsigned(sum_exp)+ 1));
state <= OUTPUT_STATE;
elsif(sum_mantissa(23) = '0') then --in this case we need to upshift
--This iterates the normalization shifts, thus can take many clocks.
sum_mantissa <= sum_mantissa(23 downto 0) & '0';
sum_exp <= std_logic_vector((unsigned(sum_exp)-1));
state<= NORMALIZE_STATE; --keep shifting till leading 1 appears
else
state <= OUTPUT_STATE; --leading 1 already there. Latch output
end if;
when OUTPUT_STATE =>
sum(22 downto 0) <= sum_mantissa(22 downto 0);
sum(30 downto 23) <= sum_exp(7 downto 0);
sum(31) <= sum_sgn;
done <= '1'; -- signal done
if (start = '0') then -- stay in the state till request ends i.e start is low
done <= '0';
state <= WAIT_STATE;
end if;
when others =>
state <= WAIT_STATE; --Just in case.
end case;
end if;
end process;
end mixed;
+9
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+115
View File
@@ -0,0 +1,115 @@
library ieee;
use ieee.std_logic_1164.all;
use ieee.float_pkg.all;
library work;
use work.test_utility.all;
use work.sine_data.all;
use work.cosine_data.all;
use work.sine_cosine_data.all;
library std;
use std.env.all;
use std.textio.all;
entity tb_fsm_add is
generic( CHECK_RESULTS : boolean; GUI_MODE : boolean := true );
end;
architecture test of tb_fsm_add is
signal clk : std_logic := '0';
signal reset : std_logic := '1';
signal run_calc : std_logic;
signal calc_cnt : std_logic_vector( 3 downto 0 );
signal signal_a_read : std_logic;
signal signal_a_readdata : std_logic_vector( 31 downto 0 );
signal signal_b_read : std_logic;
signal signal_b_readdata : std_logic_vector( 31 downto 0 );
signal signal_write : std_logic;
signal signal_writedata : std_logic_vector( 31 downto 0 );
begin
u_fsm_add : entity work.fsm_add
port map (
clk => clk,
reset => reset,
run_calc => run_calc,
calc_cnt => calc_cnt,
signal_a_read => signal_a_read,
signal_a_readdata => signal_a_readdata,
signal_b_read => signal_b_read,
signal_b_readdata => signal_b_readdata,
signal_write => signal_write,
signal_writedata => signal_writedata
);
a_clk: clk <= not clk after 10 ns;
a_reset: process( clk )
begin
if falling_edge( clk ) then
reset <= '0';
end if;
end process;
input_data_a_simulus: process is
variable index : integer := 0;
begin
while true loop
signal_a_readdata <= to_std_logic_vector( to_float( work.sine_data.expected( index ) ) );
wait until rising_edge( signal_a_read );
if ( index < 1023 ) then
index := index + 1;
end if;
end loop;
end process input_data_a_simulus;
input_data_b_simulus: process is
variable index : integer := 0;
begin
while true loop
signal_b_readdata <= to_std_logic_vector( to_float( work.cosine_data.expected( index ) ) );
wait until rising_edge( signal_b_read );
if ( index < 1023 ) then
index := index + 1;
end if;
end loop;
end process input_data_b_simulus;
stimulus: process is
variable expected : real;
variable float_value : float32;
variable real_value : real;
variable abs_err : real := 0.5e-1;
begin
std.textio.write( std.textio.OUTPUT, "--------------------------------------------------------------------------------" & LF );
std.textio.write( std.textio.OUTPUT, "Starting tb_fsm_add" & LF );
wait until falling_edge( reset );
wait until falling_edge( clk );
run_calc <= '1';
wait until falling_edge( clk );
for i in 0 to 16 loop
wait until rising_edge( signal_write );
expected := work.sine_cosine_data.expected( i + 1 );
float_value := to_float( signal_writedata );
real_value := to_real( float_value );
assert_near( real_value, expected, abs_err );
end loop;
if GUI_MODE = true then
finish;
else
stop;
end if;
end process stimulus;
end architecture test;
+3
View File
@@ -0,0 +1,3 @@
$ version 1.1
/tb_fsm_add/*
/tb_fsm_add/u_fsm_add/*
-85
View File
@@ -1,85 +0,0 @@
library ieee;
use ieee.std_logic_1164.all;
library std;
use std.env.all;
library work;
use work.test_utility.all;
entity tb_top_entity_float_add is
generic( CHECK_RESULTS : boolean; GUI_MODE : boolean := true );
end;
architecture test of tb_top_entity_float_add is
signal clk : std_logic := '0';
signal reset : std_logic := '1';
signal run_calc : std_logic := '0';
signal operand_a : std_logic_vector(31 downto 0) := ( others => '0' );
signal operand_b : std_logic_vector(31 downto 0) := ( others => '0' );
signal result : std_logic_vector(31 downto 0);
signal calc_complete : std_logic;
begin
u_top_entity_float_add : entity work.top_entity_float_add
port map (
clk => clk,
reset => reset,
run_calc => run_calc,
operand_a => operand_a,
operand_b => operand_b,
result => result,
calc_complete => calc_complete
);
a_clk: clk <= not clk after 10 ns;
a_reset: process( clk )
begin
if falling_edge( clk ) then
reset <= '0';
end if;
end process;
delay : process
variable res : std_logic_vector( 31 downto 0 );
variable expected : std_logic_vector( 31 downto 0 );
begin
wait until falling_edge( reset );
-----------------------------------------------------------------------
wait until rising_edge( clk );
run_calc <= '1';
operand_a <= x"01000000";
operand_b <= x"02000000";
wait until rising_edge( calc_complete );
run_calc <= '0';
if ( CHECK_RESULTS ) then
res := result;
expected := x"02200000";
assert_eq( res, expected );
end if;
wait until rising_edge( clk );
-----------------------------------------------------------------------
wait until rising_edge( clk );
run_calc <= '1';
operand_a <= x"4048f5c3"; -- 3.14
operand_b <= x"402d70a4"; -- 2.71
wait until rising_edge( calc_complete );
if ( CHECK_RESULTS ) then
res := result;
expected := x"40bb3333"; -- 5.85
assert_eq( res, expected );
end if;
wait until rising_edge( clk );
if GUI_MODE = true then
finish;
else
stop;
end if;
end process delay;
end architecture test;