VHDL coding tips and tricks: fixed point package
Showing posts with label fixed point package. Show all posts
Showing posts with label fixed point package. Show all posts

Wednesday, October 18, 2017

How to use ieee_proposed libraries in Modelsim?

You might have come across the ieee_proposed fixed and floating point packages, if you were looking for ways to deal with fractional numbers in VHDL. These libraries are amazing. But they are not so widely supported by simulation and synthesis tools like other ieee libraries.

Many tools use their own version of these libraries.

Lets see what I meant. See the below code, using the floating point operations + and *.

LIBRARY ieee;
library ieee_proposed;
use ieee_proposed.float_pkg.all;
 
ENTITY temp IS
port( a,b : in float(3 downto -4);
    c,d : out float(3 downto -4)
    );  
END temp;
 
ARCHITECTURE behavior OF temp IS 
 
BEGIN

<= a+b;
<= a*b;

END;

Upon compilation in Modelsim 10.4a, we would get the following errors,

** Error: D:/MODELSIM/temp.vhd(2): Library ieee_proposed not found.
** Error: D:/MODELSIM/temp.vhd(3): (vcom-1136) Unknown identifier "ieee_proposed".
** Error: D:/MODELSIM/temp.vhd(5): VHDL Compiler exiting

This error can be easily removed by just changing the name of the library. As I said, some tools, use their own version of ieee_proposed. Modelsim names it floatfixlib.

See the new code, which compiles fine, without any errors.

LIBRARY ieee;
Library floatfixlib; 
use floatfixlib.float_pkg.all;
 
ENTITY temp IS
port( a,b : in float(3 downto -4);
    c,d : out float(3 downto -4)
    );  
END temp;
 
ARCHITECTURE behavior OF temp IS 
 
BEGIN

<= a+b;
<= a*b;

END;

The code simply adds and multiplies two floating point numbers. Without this package, somebody would have had to implement the whole floating point operation from scratch.

It goes without saying that for using fixed point library, I just have to include the following line in the code,

use floatfixlib.fixed_pkg.all;

I have used Modelsim version 10.4a for this particular post.

Monday, March 29, 2010

Fixed Point Operations in VHDL : Tutorial Series Part 3

     This article is a continuation of the tutorial series on fixed_pkg library.In this article I will talk about,arithmetical operations on fixed point signals.I assume that you have read Part 1 and Part 2 of the series.

     If you have gone through Part 2 of the series then you must have seen that assigning a signal results in rounding off the value if the range of the output signal is not sufficient.Yes,this is a great advantage of fixed_pkg.It is designed so that there is no possibility of an overflow.This is different from "numeric_std" library which simply neglect the underflow and overflow bits.In order to make sure that overflow doesn't happen we have to carefully design the size of output signals based on input signal size and the operation performed.The following table shows the Results range for different kind of operations:


    In the table A'left means the left most array index and A'right means the right most array index.The functions max and min are defined as follows:

max(a,b) = a if a > b else b.
min(a,b) = a if a < b else b.

The operations supported by the package for unsigned types are:
+, –, *, /, rem, mod, =, /=, <, >, >=, <=, sll, srl, rol, ror, sla, sra.

The operations supported by the package for signed types are:
+, –, *, /, rem, mod, =, /=, <, >, >=, <=, sll, srl, rol, ror, sla, sra, abs, - (unary).

 Without going much into the explanations I will directly explain their use with the help of some examples. 

Examples for addition:
signal n1,n2 : ufixed(4 downto -3);
signal n3 : ufixed(5 downto -3);
begin
n1 <=  to_ufixed (5.75,n1);     -- n1 = "00101110" = 5.75
n2 <=  to_ufixed (6.5,n2);      -- n2 = "00110100" = 6.5
n3 <= n1+n2;                   -- n3 = "001100010" = 12.25
--See that the range of 'n3' is 5 downto -3 but that of 'n1' and 'n2' is 4 downto -3.

If the range of n3 is 4 downto -3 then the result will be:
n3 = "00110001" = 6.125.
So it is very important to declare the output signal ranges as per the above table.

--For signed numbers:
signal s1,s2 : sfixed(4 downto -3);
signal s3 : sfixed(5 downto -3);
s1 <=  to_sfixed (5.75,s1);     -- s1 = "00101110" = 5.75
s2 <=  to_sfixed (-6.5,s2);     -- s2 = "11001100" = -6.5
s3 <= s1+s2;                   -- s3 = "111111010" = -0.75

Examples for Subtraction:
n1 <=  to_ufixed (5.75,n1);      -- n1 = "00101110" = 5.75
n2 <=  to_ufixed (6.5,n2);       -- n2 = "00110100" = 6.5
n3 <= n2-n1;                    -- n3 = "000000110" = 0.75    

s1 <=  to_sfixed (5.75,s1);      -- s1 = "00101110" = 5.75
s2 <=  to_sfixed (-6.5,s2);      -- s2 = "11001100" = -6.5
s3 <= s2-s1;                    -- s3 = "110011110" = -12.25

Examples for multiplication:
signal n1,n2 : ufixed(4 downto -3);
signal n3 : ufixed(9 downto -6);

signal s1,s2,s3 : sfixed(4 downto -3);
signal s4,s5 : sfixed(9 downto -6);

n1 <=  to_ufixed (5.75,n1);         -- n1 = "00101110" = 5.75
n2 <=  to_ufixed (6.5,n2);          -- n2 = "00110100" = 6.5
n3 <= n2*n1;                        -- n3 = "0000100101011000" = 37.375

s1 <=  to_sfixed (5.75,s1);         -- s1 = "00101110" = 5.75
s2 <=  to_sfixed (-6.5,s2);         -- s2 = "11001100" = -6.5
s4 <= s2*s1;                        -- s4 = "1111011010101000" = -37.375

s3 <=  to_sfixed (-5.75,s1);        -- s3 = "11010010" = -5.75
s5 <= s3*s2;                        -- s5 = "0000100101011000" = 37.375

Examples for Remainder:
signal n1 : ufixed(4 downto -3);
signal n2 : ufixed(2 downto -4);
signal n3 : ufixed(2 downto -3);     -- see how range of n3 is declared here.

signal s1 : sfixed(4 downto -3);
signal s2 : sfixed(2 downto -4);
signal s3 : sfixed(2 downto -3);     -- see how range of s3 is declared here.

n1 <=  to_ufixed (7.25,n1);        -- n1 = "00111010" = 7.25
n2 <=  to_ufixed (1.5,n2);         -- n2 = "0011000" = 1.5
n3 <= n1 rem n2;                   -- n3 = "001010" = 1.25

s1 <=  to_sfixed (-7.25,s1);       -- s1 = "11000110" = -7.25
s2 <=  to_sfixed (-1.5,s2);        -- s2 = "1101000" = -1.5
s3 <= s1 rem s2;                   -- s3 = "110110" = -1.25


Examples for division:
signal n1 : ufixed(4 downto -3);
signal n2 : ufixed(2 downto -4);
signal n3 : ufixed(8 downto -6);     -- see how range of n3 is declared here.

signal s1 : sfixed(4 downto -3);
signal s2 : sfixed(2 downto -4);
signal s3 : sfixed(9 downto -5);     -- see how range of s3 is declared here.

n1 <=  to_ufixed (6.75,n1);          -- n1 = "00110110" = 6.75
n2 <=  to_ufixed (1.5,n2);           -- n2 = "0011000" = 1.5
n3 <= n1 / n2;                       -- n3 = "000000100100000" = 1.5

s1 <=  to_sfixed (-6.75,s1);         -- s1 = "11001010" = -6.75
s2 <=  to_sfixed (1.5,s2);           -- s2 = "0011000" = 1.5
s3 <= s1 / s2;                       -- s3 = "111111101110000" = -1.5

     These are only a few of the operators available in the package.In the next part of the tutorial, I will explain the use of some more operators with example.
Read Part 1 and Part 2 of the series here:
Fixed Point Operations in VHDL : Tutorial Series Part 1
Fixed Point Operations in VHDL : Tutorial Series Part 2

Sunday, March 28, 2010

Fixed Point Operations in VHDL : Tutorial Series Part 2

     This article is a continuation of the tutorial series on fixed_pkg library.In this article I will talk about, type conversions and assignments of signals of the fixed point data type.I assume that you have read Part 1 of the series.
Let us see how a signal assignment is made.Suppose I have a signal of unsigned fixed point type.I can assign a value (say 9.75 ) in the following way:

signal n1 : ufixed(4 downto -3);
n1 <= to_ufixed (9.75,4,-3);   -- n1 ="01001110"

Here TO_UFIXED is the conversion function used to convert the value "9.75" to binary number in the prescribed range.For a signed number you can use the function "TO_SFIXED".

--an example for TO_SFIXED.
signal n2 : sfixed(4 downto -3);
n1 <= to_sfixed (-9.75,4,-3);  -- n2 = "10110010"

Assignments can be made in a different way also.This is done by using a signal name as a parameter, instead of actual range values.

--Both n1 and n3 contains the same value.
signal n1,n2,n3 : ufixed(4 downto -3);
n1 <= to_ufixed (9.75,n1);   -- n1 ="01001110"
n3 <= to_ufixed (9.75,n2);   -- n3 ="01001110"
Another method of type conversion is by using signal_name'high and signal_name'low attributes.See the below example.
--Type conversion using attributes.
signal n1,n2,n3 : ufixed(4 downto -3);
n1 <= to_ufixed (9.75,n1'high,n1'low);   -- n1 ="01001110"
n3 <= to_ufixed (9.75,n2'high,n2'low);   -- n3 ="01001110"

All the above methods applies equally to TO_SFIXED conversion function also.
Another useful function available in fixed_pkg is resize().This function is used to fix the size of the output.But while changing the size of the output the signal value may get rounded off or get saturated.


--An example for resize() function.
signal n1 : ufixed(4 downto -3);
signal n2 : ufixed(5 downto -5);
n1 <=  to_ufixed (9.75,n2);   -- n1 = "01001110"
n2 <= resize(n1,n2);          -- n2 ="00100111000"

--Resizing functions can be called in the following ways also:
--Method 1
n2 <= resize(n1,n2'high,n2'low);
--Method 2
n2 <= resize(n1,5,-5);

Take the following statements.
n2 <=  to_ufixed (31.75,n2);      -- n2 ="01111111000"  
n1 <= resize(n2,n1);              -- n1 = "11111110"
In the above code snippet the values are assigned without any rounding off because the value "31.75" matched with the range of 'n1'.
Now take the next two statements:
n2 <=  to_ufixed (32.75,n2);      -- n2 ="10000011000"  
n1 <= resize(n2,n1);              -- n1 = "11111111" 
Here the range of 'n1' is not sufficient for the value "32.75",so while resizing it we got all 1's in the output.While writing your code, you should take care of such things.

Note :- Beware of the following type of assignments:

signal n1 : ufixed(4 downto -3);
signal n2 : ufixed(3 downto -4);
n1 <=  to_ufixed (5.75,n1);      -- n1 = "00101110" = 5.75
n4 <= n1;                                -- n4 = "00101110" = 2.875 
    
     Here the compiler will not show any error or warning on 4th line because the size and type of both the signals are same.But as you can see this is not what we expected.Take care while writing your code using fixed_pkg.You may get unexpected results even if you are careless about a single line of code.

Fixed Point Operations in VHDL : Tutorial Series Part 1

     You must have heard about library named fixed_pkg.In terms of complexity this library can be placed some where between integer math and floating point maths.I have decided to write a series of tutorials about the usage of fixed_pkg library.The library helps to handle fractional numbers with ease.
The library can be downloaded from here.

In the first part of this tutorial, I will give an introduction about the library and the new data types available for use.

How to use this library in your module?
Add the following two lines to your code(the place where you usually add the libraries):

library ieee_proposed;
use ieee_proposed.fixed_pkg.all;

What are the new data types available in the package?
     FIXED_PKG defines two new data types.They are UFIXED ( for unsigned fixed point) and SFIXED (for signed fixed point).

How to declare signals?
Say you want a fixed point unsigned signal with 'a' bits for decimal part and 'b' bits for fractional part,then you can declare them as follows:

signal example : ufixed( a-1 downto -b);
--an example
signal example : ufixed (3 downto -4);

Here the signal 'example' has 4 bits for decimal part and 4 bits for fractional part.
example = 9.75 = "1001.1100" or simply example ="10011100".

For signed numbers we use "sfixed" while declaring the signals.Signed numbers are stored as 2's complement format.


--an example for signed fixed point type.
signal example : sfixed(4 downto -4);

     Here the signal 'example' has 5 bits of decimal part and 4 bits for fractional part.
example = -9.75 = "101100100".This is got by taking 2's complement of binary value of 9.75.The MSB bit '1' indicates the number as negative.

If you declare the signal as sfixed and still store a positive value(say 9.75) then it has the same kind of storage format as ufixed.


--an example
signal example : sfixed(4 downto -4);
--If 'example' contains 9.75 then it is storage as "01001.1100".

     Remember that you declare the signals with sufficient width so that values are get stored correctly.If the width is not enough then the signal may get rounded off.