OpenCores
URL https://opencores.org/ocsvn/openfpu64/openfpu64/trunk

Subversion Repositories openfpu64

[/] [openfpu64/] [trunk/] [fpu_add.vhd] - Rev 3

Go to most recent revision | Compare with Previous | Blame | View Log

-------------------------------------------------------------------------------
-- Project    : openFPU64 Add/Sub Component
-------------------------------------------------------------------------------
-- File       : fpu_add.vhd
-- Author     : Peter Huewe  <peterhuewe@gmx.de>
-- Created    : 2010-04-19
-- Last update: 2010-04-19
-- Standard   : VHDL'87
-------------------------------------------------------------------------------
-- Description: double precision floating point adder/subtractor component
--                     for openFPU64, includes rounding and normalization
-- 
-------------------------------------------------------------------------------
-- Copyright (c) 2010 
-------------------------------------------------------------------------------
-- License: gplv3, see licence.txt
-------------------------------------------------------------------------------
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use work.fpu_package.all;
 
entity fpu_add is
  port (
    clk, reset_n           : in  std_logic;  -- reset = standard active low
    mode                   : in  std_logic;  --  mode: 0 = add , 1= sub
    cs                     : in  std_logic;  -- chip select active high
    -- in operands
    sign_a, sign_b         : in  std_logic;  -- sign bits
    exponent_a, exponent_b : in  std_logic_vector (11 downto 0);  -- exponents of the operands
    mantissa_a, mantissa_b : in  std_logic_vector (57 downto 0);  -- mantissa of operands
-- out result
    sign_res               : out std_logic;
    exponent_res           : out std_logic_vector(11 downto 0);
    mantissa_res           : out std_logic_vector (57 downto 0);
 
    rounding_needed : out std_logic;  -- FUTURE wether rounding is needed or not
    valid           : out std_logic
    );
end fpu_add;
architecture rtl of fpu_add is
  -- controller part
 
 
 
  type t_state is (
    s_reset,
    s_load_wait,
    s_a_is_nan,
    s_b_is_nan,
    s_swap_a_and_b,
    s_invalid_operation_inf_minus_inf,
    s_get_result,
    s_fix_sub_borrow,
    s_normalize_right,
    s_result_is_inf,
    s_normalize_left,
    s_prepare_round_ceiling,
    s_post_normalization,
    s_finished,
    s_zero,
    s_correction_and_round,
    s_prepare_operation,
    s_check_result,
    s_wait_on_normalize_right,
    s_align_b_to_a
    );
  signal state : t_state;
 
  signal a_s, b_s                       : std_logic             := '0';
  signal a_e, b_e                       : unsigned(11 downto 0) := (others => '0');
  signal a_m, b_m                       : unsigned(57 downto 0) := (others => '0');
  signal alu_result, alu_op_a, alu_op_b : unsigned(57 downto 0) := (others => '0');
  -- Switches adder between Addition and Subtraction, helps to infer 1 big addsub by synthesis tools
  signal alu_mode                       : std_logic             := '0';
 
  -- status bits generated automatically
  signal a_is_a_denormalized_number, a_is_lesser_than_b, a_is_inf_or_nan, a_is_inf : std_logic := '0';
  signal b_is_unaligned, addition_mode, rounding_case_is_to_ceiling                : std_logic := '0';
  signal b_is_inf, b_is_inf_or_nan, or_signal                                      : std_logic := '0';
 
 
 
  alias result_is_inf   : std_logic is a_is_inf_or_nan;  -- result is stored in a
  alias signs_are_equal : std_logic is addition_mode;
  alias a_e_all_ones    : std_logic is a_is_inf_or_nan;  -- if exponent 111...111 then a is either INF or NAN
  alias b_e_all_ones    : std_logic is b_is_inf_or_nan;  -- if exponent 111...111 then b is either INF or NAN
  alias a_e_all_zeros   : std_logic is a_is_a_denormalized_number;  -- if exponent of a, a is either zero or a denormalized number
 
begin
  -- FUTURE
  rounding_needed <= '0';
 
 
  -- generate internal status signals
 
  a_is_a_denormalized_number <= '1' when a_e = ZEROS(10 downto 0)               else '0';
  a_is_inf_or_nan            <= '1' when a_e = ONES (10 downto 0)               else '0';
  b_is_inf_or_nan            <= '1' when b_e = ONES(10 downto 0)                else '0';
  b_is_inf                   <= '1' when b_m (54 downto 1) = ZEROS(54 downto 1) else '0';  -- if mantissa is zero and exponent  is 11..111 b is inf
  a_is_inf                   <= '1' when a_m (54 downto 1) = ZEROS(54 downto 1) else '0';  -- if mantissa is zero and exponent is 11..111 a is inf
  a_is_lesser_than_b         <= '1' when a_e(10 downto 0) < b_e(10 downto 0)    else '0';  --  a should be >= b
  b_is_unaligned             <= '1' when a_e /= b_e                             else '0';  -- exponents of a and b have to be the same before addition
  addition_mode              <= '1' when a_s = b_s                              else '0';
 
  -- this line has this meaning
  -- case a_m (3 downto 0) 
  --  when "1100" => add 1 to a_m(57 downto 3)
  --  when "1101" => add 1 to a_m(57 downto 3)
  --  when "1110" => add 1 to a_m(57 downto 3)
  --  when "1111" => add 1 to a_m(57 downto 3)
  rounding_case_is_to_ceiling <= '1' when a_m(3 downto 0) = "1100" or (a_m(2) = '1' and (a_m(1) = '1' or a_m(0) = '1')) else '0';
 
  -- Big ADD/SUB, has to be preloaded for each result
  alu_result <= alu_op_a+alu_op_b when alu_mode = '1' else alu_op_a-alu_op_b;
 
  state_trans : process (clk, reset_n)  -- clock, reset_n, chipselect
  begin
    if reset_n = '0' then
      a_m   <= (others => '0');
      a_e   <= (others => '0');
      a_s   <= '0';
      b_m   <= (others => '0');
      b_e   <= (others => '0');
      b_s   <= '0';
      valid <= '0';
 
      sign_res     <= '0';
      exponent_res <= (others => '0');
      mantissa_res <= (others => '0');
      alu_op_a     <= (others => '0');
      alu_op_b     <= (others => '0');
      alu_mode     <= '0';
      state        <= s_reset;          -- reset hat vorrang
    elsif rising_edge(clk) then
      if cs = '0' then
        a_m   <= (others => '0');
        a_e   <= (others => '0');
        a_s   <= '0';
        b_m   <= (others => '0');
        b_e   <= (others => '0');
        b_s   <= '0';
        valid <= '0';
 
        sign_res     <= '0';
        exponent_res <= (others => '0');
        mantissa_res <= (others => '0');
        alu_op_a     <= (others => '0');
        alu_op_b     <= (others => '0');
        alu_mode     <= '0';
        state        <= s_reset;        -- reset hat vorrang
      else
 
        -- keep values
        a_m          <= a_m;
        a_e          <= a_e;
        a_s          <= a_s;
        b_m          <= b_m;
        b_e          <= b_e;
        b_s          <= b_s;
        valid        <= '0';
        sign_res     <= a_s;
        exponent_res <= std_logic_vector(a_e);
        mantissa_res <= std_logic_vector(a_m);
        alu_op_a     <= alu_op_a;
        alu_op_b     <= alu_op_b;
        alu_mode     <= alu_mode;
        state        <= state;  -- keep state if nothing else specified.
 
 
        case state is
          -- reset state, if chipselect is 1 load operands
          when s_reset =>
            if cs = '1' then
              a_s <= sign_a;
              b_s <= sign_b xor mode;   -- "sorts operations" 
              a_m <= unsigned(mantissa_a);
              b_m <= unsigned(mantissa_b);
              a_e <= unsigned(exponent_a);
              b_e <= unsigned(exponent_b);
 
              state <= s_load_wait;
            end if;
 
          -- check operands if they are valid (!= nan), if operation is allowed
          -- or if operands need to be swapped
          when s_load_wait =>
            if a_is_inf_or_nan = '1' and a_is_inf = '0' then state                                     <= s_a_is_nan;
            elsif b_is_inf_or_nan = '1' and b_is_inf = '0' then state                                  <= s_b_is_nan;
            --if a and b are infinity and signs are not equal this is an invalid operation
            elsif a_is_inf_or_nan = '1' and b_is_inf_or_nan = '1' and signs_are_equal = '0' then state <= s_invalid_operation_inf_minus_inf;
            -- if only a is infinity then nothing is left to be done
            elsif a_is_inf_or_nan = '1' and a_is_inf = '1' then state                                  <= s_finished;
            elsif a_is_lesser_than_b = '1' then state                                                  <= s_swap_a_and_b;
            else state                                                                                 <= s_prepare_operation;
            end if;
 
          --operand a is NaN, set sign and finish
          when s_a_is_nan =>
            a_s <= b_s or mode;
 
            state <= s_finished;
 
          --operand b is NaN set result=b and finish
          when s_b_is_nan =>
            a_e <= b_e;
            a_m <= b_m;
            a_s <= b_s or mode;
 
            state <= s_finished;
 
          -- operands a and b have to be swapped
          when s_swap_a_and_b =>
            a_s <= b_s;
            b_s <= a_s;
            a_e <= b_e;
            b_e <= a_e;
            a_m <= b_m;
            b_m <= a_m;
 
            state <= s_prepare_operation;
 
          -- load adder for add/sub
          -- check if b has to be aligned
          when s_prepare_operation =>
            alu_mode <= addition_mode;  -- load alu for s_get_result
            alu_op_a <= a_m;
            alu_op_b <= b_m;
 
            if b_is_unaligned = '1' then state <= s_align_b_to_a;
            else state                         <= s_get_result;
            end if;
 
          -- INF - INF or similar is an invalid operation
          when s_invalid_operation_inf_minus_inf =>
            a_m(54) <= '1'; a_s <= '1';
 
            state <= s_finished;
 
          -- align b to a so that a_e=b_e
          when s_align_b_to_a =>
            alu_mode <= addition_mode;  -- load alu for s_get_result
            alu_op_a <= a_m;
            alu_op_b <= b_m;
 
            state <= s_get_result;  -- if a_e=b_e or b_m = 0...00x start calculation
            if b_is_unaligned = '1' then  -- otherwise align b to a
              b_m(56 downto 0) <= '0' & b_m (56 downto 2) & (b_m(1) or b_m(0));
              alu_op_b         <= '0'&'0' & b_m (56 downto 2) & (b_m(1) or b_m(0));
              b_e              <= b_e +1;
              if b_m(56 downto 1) /= 0 then
                -- still not alligned
                state <= s_align_b_to_a;
              end if;
            end if;
 
 
          -- assign calculation result
          when s_get_result =>
            b_e <= a_e;  -- in case some steps were skipped due to b_m = 0...00x
            a_m <= alu_result;
 
            state <= s_check_result;
 
-- check result:
-- sub borrow occured?
          -- normalization needed?
          -- result is zero?
          -- result is in?
          -- rounding needed?
          when s_check_result =>
            alu_mode <= '1';            -- load alu for s_fix_sub_borrow 
            alu_op_a <= not(a_m);
            alu_op_b <= (57 downto 1 => '0')&'1';
 
            if a_m(57) = '1' then state                       <= s_fix_sub_borrow;
            elsif a_m(56) = '1' then state                    <= s_normalize_right;  -- a_m(56)='1' -> normalization to the right is needed
            elsif result_is_inf = '1' then state              <= s_result_is_inf;
            elsif a_m(55) = '0' and a_is_inf = '1' then state <= s_zero;
            else state                                        <= s_correction_and_round;
            end if;
 
          -- sub borrow occured, fix it by *-1 (adder loaded in previous state)
          when s_fix_sub_borrow =>
            a_s <= not a_s;
            a_m <= alu_result;
 
            if a_m(56) = '1' then state                       <= s_normalize_right;  -- a_m(56)='1' -> normalization to the right is needed
            elsif result_is_inf = '1' then state              <= s_result_is_inf;
            elsif a_m(55) = '0' and a_is_inf = '1' then state <= s_zero;
            else state                                        <= s_correction_and_round;
            end if;
 
          -- Normalize right 
          when s_normalize_right =>
            a_m(56 downto 0) <= '0' & a_m(56 downto 2)& (a_m(0) or a_m(1));
            a_e              <= a_e +1;
 
            state <= s_wait_on_normalize_right;
 
          -- check result of Normalization to the right
          when s_wait_on_normalize_right =>
            if a_is_inf_or_nan = '1' then state <= s_result_is_inf;
            else state                          <= s_correction_and_round;
            end if;
 
          -- result is infinity
          when s_result_is_inf =>
            a_m <= (others => '0');
 
            state <= s_finished;
 
          -- 
          when s_correction_and_round =>
            alu_mode                                            <= '1';  -- load alu for s_prepare_round_ceiling
            alu_op_a                                            <= a_m;
            alu_op_b                                            <= (57 downto 4 => '0')&"1000";
            if a_m(55) = '0' and a_e_all_zeros = '0' then state <= s_normalize_left;
            elsif rounding_case_is_to_ceiling = '1' then state  <= s_prepare_round_ceiling;
            elsif a_m(56) = '1' then state                      <= s_post_normalization;  -- a_m(56)='1' -> postnormalization is needed
 
            else state <= s_finished; end if;
 
          -- shift (possible) leading 1 to correct position
          when s_normalize_left=>
            a_m(55 downto 0) <= a_m(54 downto 0) & a_m(0);
            a_e              <= a_e -1;
            alu_mode         <= '1';    -- load alu for s_prepare_round_ceiling
            alu_op_a         <= a_m(57 downto 56) & a_m(54 downto 0) & a_m(0);
            alu_op_b         <= (57 downto 4 => '0')&"1000";
 
 
            if a_m(54) = '0' and a_e_all_zeros = '0' then state                         <= s_normalize_left;
            elsif a_m(2 downto 0) = "110" or (a_m(1) = '1' and a_m(0) = '1') then state <= s_prepare_round_ceiling;
            elsif a_m(56) = '1' then state                                              <= s_post_normalization;  -- a_m(56)='1' -> postnormalization is needed
            else state                                                                  <= s_finished; end if;
 
 
          when s_prepare_round_ceiling =>
            a_m                                <= alu_result;
            if alu_result(56) = '1' then state <= s_post_normalization;  -- a_m(56)='1' -> postnormalization is needed
            else state                         <= s_finished; end if;
 
          -- shift leading 1 to correct position
          when s_post_normalization =>
            a_m(56 downto 1) <= '0' & a_m(56 downto 2);
            a_e              <= a_e +1;
            state            <= s_finished;
 
          -- result is zero
          when s_zero =>
            a_s <= '0';  -- im add/sub fall bei allen rundungsmodi ausser round to -inf ist dies richtig!
            a_e <= (others => '0');
            a_m <= (others => '0');
 
            state <= s_finished;
          -- finished
          when s_finished =>
            valid <= '1';
            state <= s_finished;        -- done here.
 
        end case;
      end if;
    end if;
  end process;
end rtl;
 
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.