diff options
Diffstat (limited to 'libraries/openieee/numeric_std-body.proto')
-rw-r--r-- | libraries/openieee/numeric_std-body.proto | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/libraries/openieee/numeric_std-body.proto b/libraries/openieee/numeric_std-body.proto new file mode 100644 index 000000000..757db6299 --- /dev/null +++ b/libraries/openieee/numeric_std-body.proto @@ -0,0 +1,275 @@ +-- This -*- vhdl -*- file is part of GHDL. +-- IEEE 1076.3 compliant numeric std package body. +-- The implementation is based only on the specifications. +-- Copyright (C) 2015 Tristan Gingold +-- +-- GHDL is free software; you can redistribute it and/or modify it under +-- the terms of the GNU General Public License as published by the Free +-- Software Foundation; either version 2, or (at your option) any later +-- version. +-- +-- GHDL is distributed in the hope that it will be useful, but WITHOUT ANY +-- WARRANTY; without even the implied warranty of MERCHANTABILITY or +-- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +-- for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with GCC; see the file COPYING2. If not see +-- <http://www.gnu.org/licenses/>. + +package body NUMERIC_STD is + constant NO_WARNING : Boolean := False; + + constant null_unsigned : unsigned (0 downto 1) := (others => '0'); + constant null_signed : signed (0 downto 1) := (others => '0'); + + subtype nat1 is natural range 0 to 1; + + type nat1_to_sl_type is array (nat1) of std_ulogic; + constant nat1_to_01 : nat1_to_sl_type := (0 => '0', 1 => '1'); + + subtype sl_01 is std_ulogic range '0' to '1'; + subtype sl_x01 is std_ulogic range 'X' to '1'; + + type carry_array is array (sl_01, sl_01, sl_01) of sl_01; + constant compute_carry : carry_array := + ('0' => ('0' => ('0' => '0', '1' => '0'), + '1' => ('0' => '0', '1' => '1')), + '1' => ('0' => ('0' => '0', '1' => '1'), + '1' => ('0' => '1', '1' => '1'))); + constant compute_sum : carry_array := + ('0' => ('0' => ('0' => '0', '1' => '1'), + '1' => ('0' => '1', '1' => '0')), + '1' => ('0' => ('0' => '1', '1' => '0'), + '1' => ('0' => '0', '1' => '1'))); + + type sl_to_x01_array is array (std_ulogic) of sl_x01; + constant sl_to_x01 : sl_to_x01_array := + ('0' | 'L' => '0', '1' | 'H' => '1', others => 'X'); + + type compare_type is (compare_unknown, + compare_lt, + compare_eq, + compare_gt); + + -- Match. + -- '-' matches with everything. + -- '0'/'L' matches, '1'/'H' matches. + type match_table_type is array (std_ulogic, std_ulogic) of boolean; + constant match_table: match_table_type := + ('0' | 'L' => ('0' | 'L' | '-' => true, others => false), + '1' | 'H' => ('1' | 'H' | '-' => true, others => false), + '-' => (others => true), + others => ('-' => true, others => false)); + + function MAX (L, R : natural) return natural is + begin + if L > R then + return L; + else + return R; + end if; + end MAX; + + function TO_INTEGER (ARG : UNSIGNED) return NATURAL + is + variable argn : UNSIGNED (ARG'Length -1 downto 0); + variable res : natural := 0; + begin + if argn'length = 0 then + assert NO_WARNING + report "NUMERIC_STD.TO_INTEGER: null array detected, returning 0" + severity warning; + return 0; + end if; + argn := TO_01 (ARG, 'X'); + if argn (0) = 'X' then + assert NO_WARNING + report + "NUMERIC_STD.TO_INTEGER: non logical value detected, returning 0" + severity warning; + return 0; + end if; + + for i in argn'range loop + res := res + res; + if argn (i) = '1' then + res := res + 1; + end if; + end loop; + + return res; + end TO_INTEGER; + + function TO_INTEGER (ARG : SIGNED) return INTEGER + is + variable argn : SIGNED (ARG'Length -1 downto 0); + variable res : integer := 0; + variable b : STD_ULOGIC; + begin + if argn'length = 0 then + assert NO_WARNING + report "NUMERIC_STD.TO_INTEGER: null array detected, returning 0" + severity warning; + return 0; + end if; + argn := TO_01 (ARG, 'X'); + if argn (0) = 'X' then + assert NO_WARNING + report + "NUMERIC_STD.TO_INTEGER: non logical value detected, returning 0" + severity warning; + return 0; + end if; + if argn (argn'left) = '1' then + -- Negative value + b := '0'; + else + b := '1'; + end if; + + for i in argn'range loop + res := res + res; + if argn (i) = b then + res := res + 1; + end if; + end loop; + + if b = '0' then + -- Avoid overflow. + res := -res - 1; + end if; + + return res; + end TO_INTEGER; + + function TO_01 (S : SIGNED; XMAP : STD_LOGIC := '0') return SIGNED + is + subtype res_type is SIGNED (S'Length - 1 downto 0); + variable res : res_type; + alias snorm: res_type is S; + begin + if S'length = 0 then + assert NO_WARNING + report "NUMERIC_STD.TO_01: null array detected" + severity warning; + return null_signed; + else + for i in res_type'range loop + case snorm (i) is + when '0' | 'L' => res (i) := '0'; + when '1' | 'H' => res (i) := '1'; + when others => + assert NO_WARNING + report "NUMERIC_STD.TO_01: non logical value detected" + severity warning; + res := (others => XMAP); + exit; + end case; + end loop; + end if; + return res; + end TO_01; + + function TO_01 (S : UNSIGNED; XMAP : STD_LOGIC := '0') return UNSIGNED + is + subtype res_type is UNSIGNED (S'Length - 1 downto 0); + variable res : res_type; + alias snorm: res_type is S; + begin + if S'length = 0 then + assert NO_WARNING + report "NUMERIC_STD.TO_01: null array detected" + severity warning; + return null_unsigned; + else + for i in res_type'range loop + case snorm (i) is + when '0' | 'L' => res (i) := '0'; + when '1' | 'H' => res (i) := '1'; + when others => + assert NO_WARNING + report "NUMERIC_STD.TO_01: non logical value detected" + severity warning; + res := (others => XMAP); + exit; + end case; + end loop; + end if; + return res; + end TO_01; + + function TO_UNSIGNED (ARG, SIZE : NATURAL) return UNSIGNED + is + variable res : UNSIGNED (SIZE - 1 downto 0); + variable a : natural := arg; + variable d : nat1; + begin + if size = 0 then + return null_unsigned; + end if; + for i in res'reverse_range loop + d := a rem 2; + res (i) := nat1_to_01 (d); + a := a / 2; + end loop; + if a /= 0 then + assert NO_WARNING + report "NUMERIC_STD.TO_UNSIGNED: vector is truncated" + severity warning; + end if; + return res; + end TO_UNSIGNED; + + function TO_SIGNED (ARG : INTEGER; SIZE : NATURAL) return SIGNED + is + variable res : SIGNED (SIZE - 1 downto 0); + variable v : integer := arg; + variable b0, b1 : std_ulogic; + variable d : nat1; + begin + if size = 0 then + return null_signed; + end if; + if arg < 0 then + -- Use one complement to avoid overflow: + -- -v = (not v) + 1 + -- not v = -v - 1 + -- not v = -(v + 1) + v := -(arg + 1); + b0 := '1'; + b1 := '0'; + else + v := arg; + b0 := '0'; + b1 := '1'; + end if; + + for i in res'reverse_range loop + d := v rem 2; + v := v / 2; + if d = 0 then + res (i) := b0; + else + res (i) := b1; + end if; + end loop; + if v /= 0 or res (res'left) /= b0 then + assert NO_WARNING + report "NUMERIC_STD.TO_SIGNED: vector is truncated" + severity warning; + end if; + return res; + end TO_SIGNED; + + function std_match (l, r : std_ulogic) return boolean is + begin + return match_table (l, r); + end std_match; + + @MATCH + + @ARITH + + @LOG +end NUMERIC_STD; |