[vhdl-200x-ft] FP types and packages

From: Peter Ashenden <peter@ashenden.com.au>
Date: Wed Dec 08 2004 - 21:29:32 PST

David and Jim,

Some comments on how to design FP types and operations...

First, to summarize the requirements:

What we want to achieve is representation of FP numbers in binary vector
form. The underlying representation of such numbers is just a bunch of
bits, however, specific bits have specific interpretations.

The characteristics of the representation are the exponent size (E) and the
mantissa size (M). The suggestion is to represent a FP number V using IEEE
format as a vector indexed by E downto -M, with V(E) being the sign bit,
V(E-1 downto 0) being the exponent, and V(-1 downto -M) being the mantissa.

We also want to be able to provide operations on FP values. The
characteristics of the operations (apart from their arithmetic function) are
the rounding style, whether to use denormalized numbers, whether to check
for errors, and the number of guard bits to use. Note that these are
characteristics of the operations, not of the operands.

Beyond these functional requirements, there are qualitative requirements
that have been implicit in discussions to date. I suspect that some of the
disagreement has arisen from differing assumptions about the implicit
requirements.

One potential requirement is type safety. Should FP values with different
characteristics be strictly distinct? Or, given that they share an
underlying representation as a bunch of bits, should they be less distinct?

A second potential requirement is usability. A design that requires
numerous packages to be instantiated or used is more cumbersome than one
that provides all the required declarations with few instantiations or uses.

I think there is going to be tension between these two requirements. A
design that provides strict type safety will require more manifest
statements of intent, and so be more cumbersome to use; and vice versa.

Now my 2 cents worth:

My view is that type safety should be given high priority. Systems that do
FP in hardware need not be constrained to just IEEE 32-bit or 64-bit format.
They can choose representations that meet functional requirements while
minimizing area and power. Hence, we need to allow for systems that use a
number of different FP formats. We need to support the designer in not
getting the formats mixed up. Static type checking is a language feature
that supports type safety in such a way that errors are caught early (my the
compiler) rather than later (by the test engineer or end user with lots of
effort).

Jim's suggestion that the FP number type be an unconstrained vector misses
the opportunity of using the type system to gain safety. If you declare

  type FP is array (integer range <>) of std_logic

and assume the abovementioned interpretation of indices, then

  subtype FP_64_1 is FP(8 downto -23);
  subtype FP_64_2 is FP(7 downto -24);

are both 32-bit FP representations, but with different characteristics. You
could legally (so far as VHDL is concerned), assign a value of one subtype
to an object of the other, despite the fact that is logically erroneous to
do so.

Moreover, since information that is statically intended is not statically
represented, there are performance impacts on simulation. For example,
since arithmetic operations would have parameters of the unconstrained FP
type, the procedures would have to check dynamically that the operands have
the same index ranges.

Contrast this with an approach where each FP number type with distinct
characteristics be represented by a distinct constrained type. In that
case, assignments between objects of different type could not be done
without conversion. Moreover, by associating FP operations with specific FP
types, constraints of actual parameters can be statically checked, avoiding
simulation performance overhead.

So given these arguments, I'd prefer to see a design that allows for
declaration of distinct types for different FP representations, and
association of operations with each distinct type. The generic package
approach achieves this.

Returning to the issue of characteristics of the operations: Logically,
these are orthogonal to characteristics of a given FP type. For a given FP
type, I could do an addition with no error checking and 3 guard bits,
followed by a second addition with error checking and 1 guard bit.
Depending on the behaviour of an algorithm being implemented, it may be
appropriate for different operations in the data flow to have different
characteristics. Hence, the specification of those characteristics should
be as parameters to the function.

However, given a common use model is to have the same characteristics for
most or all operations, it would make sense to have default values for the
parameters specifying characteristics. If there are commonly used values
for defaults, they could be included in the function declarations.
Alternatively, values for the default values could be provided as further
generics of the package. Those generics themselves could have defaults,
either based on common usage, or arbitrarily chosen.

A package organization that implements these suggestions would start with a
base package in library IEEE to declare types and values needed for
interfaces of other packages:

  package fphdl_types is
    type round_style ....
    type valid_fpstate ...
  end package fphdl_types;

A generic package, in library IEEE, for FP types and operations would be
declared as

  use ieee.fphdl_types.all;
  package fphdl is
    generic (
      fraction_width : NATURAL;
      exponent_width : NATURAL;
      default_round_style : round_type := round_nearest;
      default_denormalize : BOOLEAN := TRUE;
      default_check_error : BOOLEAN := TRUE;
      default_guard_bits : NATURAL := 3
    );

    type FP is array (exponent_width downto -fraction_width) of std_logic;

    constant zerofp : FP := ...
    ...

    function "+" ( L, R : FP;
                   round_style : round_type := default_round_style;
                   denormalize : BOOLEAN := default_denormalize;
                   check_error : BOOLEAN := default_check_error;
                   guard_bits : NATURAL := default_guard_bits )
                 return FP;
    ...

  end package fphdl;

We would also instantiate this in library IEEE for IEEE-format single and
double precision with default default operation characteristics as:

  package fphdl32 is new fphdl
    generic map ( fraction_width => 23, exponent_width => 8 );

  package fphdl64 is new fphdl
    generic map ( fraction_width => 52, exponent_width => 11 );

A model that only used, say, single-precision IEEE-format numbers and
operations with default characteristics would look like:

  use ieee.fphdl_types, ieee.fphdl32;
  architecture bar of foo is
    signal a, b, c : FP;
  begin
    a <= 2.0 * b + c;
    ...

Operations that used different characterisitics from default would be
expressed in prefix form:

  s <= "/"(x, y, check_error => false);

A model that used a different representation or default defaults would
instantiate the generic package and use it:

  use ieee.fphdl_types;
  package myfphdl is new ieee.fphdl
    generic map ( fraction_width => 26, exponent_width => 5,
                  default_round_style => round_zero );

  architecture fizz of foo is
    use work.myfphdl.all;
    signal a, b, c : FP;
  begin
    a <= 2.0 * b + c;
    ...

A model that used multiple different FP representations would refer to the
package instances for them, eg:

  architecture bla of foo is
    use ieee.fphdl32.all, work.myfphdl.all;
    alias fp32 is ieee.fphdl32.fp;
    alias myfp is work.myfphdl.fp;
    signal a, b : myfp;
    signal x, y : fp32;
  begin
    a <= resize (x, a'high, a'low);
    ...

Comments?

Cheers,

PA

--
Dr. Peter J. Ashenden                        peter@ashenden.com.au
Ashenden Designs Pty. Ltd.                   www.ashenden.com.au
PO Box 640                                   Ph:  +61 8 8339 7532
Stirling, SA 5152                            Fax: +61 8 8339 2616
Australia                                    Mobile: +61 414 70 9106
> -----Original Message-----
> From: owner-vhdl-200x-ft@eda.org 
> [mailto:owner-vhdl-200x-ft@eda.org] On Behalf Of Jim Lewis
> Sent: Friday, 3 December 2004 10:54
> To: vhdl-200x-ft@eda.org
> Subject: [vhdl-200x-ft] Re: floating point, thinking out loud
> 
> 
> David,
>  > What do you think about this?
> As far as I can see, the exponent and mantissa width
> are specified with generics.  Is there something I am
> missing?   I am not excited about this.
> 
> Does having width of exponent and mantissa constrained
> buy us anything?  One thing I think you mentioned in the 
> meeting is that if fp32 is a constrained type and there are 
> specific subprograms that only handle fp32, then one can do:
>    Y_fp32 <= A_fp32 + "11000000110100000000000000000000" ;
> 
> On the other hand, lets suppose that I have fp32 defined
> as a subtype of fp and all math operations are implemented
> for the type fp.
>    subtype fp32 is fp(8 downto -23) ;
> 
> Can't I coerce the string literal using a type qualifier
> as follows?
>    Y_fp32 <= A_fp32 + fp32'("11000000110100000000000000000000") ;
> 
> 
>  > Will this fill your need for a user definable floating 
> point? I need to see the trade-offs to really get a handle on 
> the situation.  I am still of the opinion that if we leave 
> exponent and mantissa unconstrained, we will end up with less 
> package instantiations and perhaps be able to instantiate 
> most/all of them in the IEEE library.
> 
> Part of my concern is that these packages are the start of 
> floating point.  As silicon area increases, what we do on a 
> chip will increase.  The use model needs to be basic and easy 
> to do, as with increased silicon area, we will be building 
> things like complex numbers and matrix math on top of them.
> 
> 
> --------------------------------------------------------------
> Here is one area I would like to explore.  I need to put more 
> time into it and will not be able to until after the next 
> meeting. If we use the subtype approach, I speculate that we 
> will also need a package which defines aliases.  For example 
> we will need to create an alias that points to the IEEE 
> 754/854 standard version of fp32:
> 
> Package fphdl_alias is
>    alias ieee_fp32 is ieee.fphdl_w_x_y_z.fp32 ;
>    . . .
> end fphdl_alias ;
> 
> Then I am thinking that we can knit it all together
> so that using fphdl looks as simple as a package reference:
> 
> context fphdl_ctx is
>    library ieee ;
> 
>    use ieee.fphdl_types.all ;
>    -- make all parameterized fphdl packages visible for usage:
>    use ieee.fphdl_xxx.all ;
>    use ieee.fphdl_yyy.all ;
>    ...
>    use ieee.fphdl_zzz.all ;
> 
>    use ieee.fphdl_alias.all ;
> end context ;
> 
> Even without the exponent and mantissa widths
> specified, this still looks like a large number
> permutations for the fphdl package.
> 
> Computer based numeric representations are ment to get
> good results with fixed width objects.  Hence we have
> standard definitions for 32 and 64 bit floating point.
> Hardware is not constrained to word widths.  In
> hardware we can use 38 bits if we wish.
> 
> Are guard bits extra bits for the ?mantissa? that are
> needed to maintain precision for some operations?  Is
> there some way to take advantage of this and reduce
> the number of package permutations we need?  After
> synthesis if the LSBs of an array always get set to
> 0, then effectively they are not part of the
> logic and this effect will be propagated to simplify
> other hardware.
> 
> ------------------------------------
> Going in an entirely different direction, are there
> any language enhancements we need to make this more
> work able?  It sure would be nice if we had a
> way to augument a type or subtype with the "other" things
> we are specifying with generics, fp_round_style, 
> fp_denormalize, fp_check_error, and fp_guard_bits. 
> Effectively this would become an attribute that can maintains 
> its value through a call to subprogram.
> 
> It is really an ironic shame about the attributes
> on signals not being passed into the subprogram.
> They are not passed because the LRM allows constant
> parameters to be passed by value, however, for
> array parameters, the LRM also allows them to be
> passed by reference.  So the bottom line seems to
> be that the attributes are not available because
> the object may be passed by value although it is
> probably passed by reference.
> 
> ------------------------------------
> 
> I should be able to do a more detailed review of what you
> are proposing sometime after the next meeting and perhaps
> then be able to propose an alternative.
> 
> Cheers,
> Jim
> 
> 
> 
> > Jim:
> > 
> > What do you think about this?  Will this fill your need for
> > a user definable floating point?
> > 
> > -------- Original Message --------
> > Subject: floating point, thinking out loud
> > Date: Tue, 30 Nov 2004 11:37:57 -0500
> > From: David Bishop <dbishop@vhdl.org>
> > To: Jim Lewis <Jim@synthworks.com>,    Peter Ashenden 
> > <peter@ashenden.com.au>, dbishop@vhdl.org
> > 
> > Thinking of this use model:
> > 
> > package fphdl_types_package is
> >   type round_style ....
> >   type valid_fpstate ...
> >   type float is array (integer range <>) of STD_LOGIC;
> >   subtype fp32 is float (8 downto -23); -- IEEE single precision
> >   subtype fp64 is float (11 downto -52); -- IEEE double precision
> >   (any other standard data widths we can think of)
> > end package fphdl_types_package;
> > 
> > package fphdl_pkg is
> >   generic (
> >     fp_fraction_width : NATURAL;
> >     fp_exponent_width : NATURAL;
> >     fp_round_style    : round_type;
> >     fp_denormalize    : BOOLEAN;
> >     fp_check_error    : BOOLEAN;
> >     fp_guard_bits     : NATURAL
> >     );
> > subtype fp is float (fp_fraction_width downto -fp_exponent_width); 
> > .....
> > 
> > end package body fphdl_pkg;
> > 
> > In the IEEE library we would still do:
> > package fphdl32_pkg is new IEEE.fphdl_pkg generic map (
> >    fp_fraction_width => 23;    -- 23 bits of fraction
> >    fp_exponent_width => 8;    -- exponent 8 bits
> >    fp_round_style    => round_nearest;  -- round nearest algorithm
> >    fp_denormalize    => true;  -- Use IEEE extended floating
> >                                        -- point 
> (Denormalized numbers)
> >    fp_check_error    => true;  -- Turn on NAN and overflow 
> processing
> >    fp_guard_bits     => 3  -- number of guard bits
> >    );
> > 
> > package use_float is new IEEE.fphdl_pkg generic map (
> >    fp_fraction_width => 18;
> >    fp_exponent_width => 8;
> >    fp_round_style => round_zero; -- truncate
> >    fp_denormalzie => false;
> >    fp_check_error => false;
> >    fp_guard_bits => 0);
> > 
> > use ieee.fphdl32_pkg.all;
> > use work.user_float.all;
> > archecture RTL of something is:
> >   subtype fp27 is float (8 downto -18);
> >   signal a, b : fp27;
> >   signal x, y : fp32;
> > begin
> >   a <= resize (x, fp27'high, -fp27'low);
> > 
> > The user would have to declare and add the generic package for the 
> > type before using it.  The "fp" type will still be there, 
> but hidden. 
> > If you create a type without the generic package, you would 
> get a "no 
> > function for" error from the compiler.
> > 
> > Upside:  You can now create your own "float" type.  You 
> just have to 
> > parameterize a package for it.
> > Downside:  You could do a 32 or 64 bit math package without 
> denormal 
> > numbers if you created it yourself.
> > 
> 
Received on Wed Dec 8 21:29:40 2004

This archive was generated by hypermail 2.1.8 : Wed Dec 08 2004 - 21:29:44 PST