Static functions for synthesis

A

Acciduzzu

Hi all,

I'd like to bring a more delicate issue to your attention, regarding a synthesis limitation in VHDL,
or in my tool. Maybe I'm lucky and I can even get some help from the VHDL gurus around.

I am trying to develop a number of parametrizable models for various DSP algorithms, including FFT
and CORDIC. The implementations of these algorithms require some look-up tables, for instance an
arctan table for CORDIC and a sine table for the tweedle factors of the FFT.

In order for the design to be generic, the look-up tables will have to be parametrizable, depending
on the generic parameters of the design, like datapath bitwidth or FFT size. The standard solution
to this problem is to write a script which generates a parametrized configuration file. Whenever you
want to change one of the parameters a new has to be generated. This can hardly be called a generic
solution.

The ideal scenario is to let the synthesis tool build these tables, based on the parameters you
supply. This is possible in VHDL using the so called constant functions. As far as I know, Verilog
tools do not support them yet, although they have been added in the Verilog 2001 standard.

Basically, constant functions are functions called when defining a constant, like in the example
below, for a pipeline stage of the CORDIC algorithm. However, since we need to work with real
numbers to compute the arctangent, the synthesis tool (Synopsys DC) will complain about that. This
should not happen, since the real numbers are not used in a synthesizable context. This is where my
knowledge of VHDL stops. Is there any workaround possible? If I don't use real types, everything
goes well and I can assign the value returned by the function to my constant.

--------------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
use work.cordic_pack.all;

entity cordic_pipestage is
generic(
INDEX : integer -- index of the pipeline stage
);
port(
clk : in std_logic;
rst : in std_logic;
en : in std_logic;

-- COORD_BITS and ANGLE_BITS are defined in cordic_pack
x_in : in signed(COORD_BITS-1 downto 0);
y_in : in signed(COORD_BITS-1 downto 0);
z_in : in signed(ANGLE_BITS-1 downto 0);

x_out : out signed(COORD_BITS-1 downto 0);
y_out : out signed(COORD_BITS-1 downto 0);
z_out : out signed(ANGLE_BITS-1 downto 0)
);
end entity;

architecture rtl of cordic_pipestage is

-- This function returns atan(1/2^n) values on a specified number of bits
function get_arctan(num_bits : natural; index : natural) return signed is
variable atan_int : integer;
variable result : signed(num_bits-1 downto 0);
begin
atan_int := integer(ROUND(ATAN(1.0/(2**real(index)))*(2**real(num_bits-1))));
result := TO_SIGNED(atan_int, num_bits);
return result;
end function;

-- the arctangent associated with the current index
-- here is where we call the constant function
constant atan : signed(ANGLE_BITS-1 downto 0) := get_arctan(ANGLE_BITS, INDEX);

begin
-- Architecture body goes here

end architecture;

--------------------------------------------------------------------------------------


I would also appreciate any comments on the topic of generic/reusable constant/table generation.

Regards,

Acciduzzu
 
R

Ray Andraka

Our CORDIC macro uses a constant table with arctangents expressed as std_logic vectors. A VHDL
function rounds them to the appropriate number of bits at the point of use. Alternatively, you could
express the table as integers. We chose std_logic_vector to permit more than 31 bits of precision. The
table was generated by either Excel or a C program (I don't recall now which), and is stored in a
separate package file so it can be easily picked up and reused.
Hi all,

I'd like to bring a more delicate issue to your attention, regarding a synthesis limitation in VHDL,
or in my tool. Maybe I'm lucky and I can even get some help from the VHDL gurus around.

I am trying to develop a number of parametrizable models for various DSP algorithms, including FFT
and CORDIC. The implementations of these algorithms require some look-up tables, for instance an
arctan table for CORDIC and a sine table for the tweedle factors of the FFT.

In order for the design to be generic, the look-up tables will have to be parametrizable, depending
on the generic parameters of the design, like datapath bitwidth or FFT size. The standard solution
to this problem is to write a script which generates a parametrized configuration file. Whenever you
want to change one of the parameters a new has to be generated. This can hardly be called a generic
solution.

The ideal scenario is to let the synthesis tool build these tables, based on the parameters you
supply. This is possible in VHDL using the so called constant functions. As far as I know, Verilog
tools do not support them yet, although they have been added in the Verilog 2001 standard.

Basically, constant functions are functions called when defining a constant, like in the example
below, for a pipeline stage of the CORDIC algorithm. However, since we need to work with real
numbers to compute the arctangent, the synthesis tool (Synopsys DC) will complain about that. This
should not happen, since the real numbers are not used in a synthesizable context. This is where my
knowledge of VHDL stops. Is there any workaround possible? If I don't use real types, everything
goes well and I can assign the value returned by the function to my constant.

--------------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
use work.cordic_pack.all;

entity cordic_pipestage is
generic(
INDEX : integer -- index of the pipeline stage
);
port(
clk : in std_logic;
rst : in std_logic;
en : in std_logic;

-- COORD_BITS and ANGLE_BITS are defined in cordic_pack
x_in : in signed(COORD_BITS-1 downto 0);
y_in : in signed(COORD_BITS-1 downto 0);
z_in : in signed(ANGLE_BITS-1 downto 0);

x_out : out signed(COORD_BITS-1 downto 0);
y_out : out signed(COORD_BITS-1 downto 0);
z_out : out signed(ANGLE_BITS-1 downto 0)
);
end entity;

architecture rtl of cordic_pipestage is

-- This function returns atan(1/2^n) values on a specified number of bits
function get_arctan(num_bits : natural; index : natural) return signed is
variable atan_int : integer;
variable result : signed(num_bits-1 downto 0);
begin
atan_int := integer(ROUND(ATAN(1.0/(2**real(index)))*(2**real(num_bits-1))));
result := TO_SIGNED(atan_int, num_bits);
return result;
end function;

-- the arctangent associated with the current index
-- here is where we call the constant function
constant atan : signed(ANGLE_BITS-1 downto 0) := get_arctan(ANGLE_BITS, INDEX);

begin
-- Architecture body goes here

end architecture;

--------------------------------------------------------------------------------------

I would also appreciate any comments on the topic of generic/reusable constant/table generation.

Regards,

Acciduzzu

--
--Ray Andraka, P.E.
President, the Andraka Consulting Group, Inc.
401/884-7930 Fax 401/884-7950
email (e-mail address removed)
http://www.andraka.com

"They that give up essential liberty to obtain a little
temporary safety deserve neither liberty nor safety."
-Benjamin Franklin, 1759
 
F

fabbl

Hi,
The problem you have is that synthesis tools will only build what they
are told by the VHDL at compile time. VHDL is a description language, it
describes a circuit for a synthesizer. Parametric features are soley to
allow flexibility in the description. Once a parameter is set, it is
implemented by the synthesizer as if it were fixed code. If you have say, a
counter component with a variable width parameter and set that parameter to
say 4 when instantated in higher level code, then a 4 bit counter (based on
the supporting code) is created for that process. It can't be changed once
implemented past the synthesis output.

As for your other issue with wave tables, I strongly suggest you have a
block RAM or supporting memory requirement. Hard wiring function values
(depending on the sample frequency) may be wasteful depending on the target.

Handling real numbers is done in one of several ways. There are floating
point IP's (Xilinx comes to mind) you can use as well. The case your
describing may be best served by a DSP core.


Acciduzzu said:
Hi all,

I'd like to bring a more delicate issue to your attention, regarding a synthesis limitation in VHDL,
or in my tool. Maybe I'm lucky and I can even get some help from the VHDL gurus around.

I am trying to develop a number of parametrizable models for various DSP algorithms, including FFT
and CORDIC. The implementations of these algorithms require some look-up tables, for instance an
arctan table for CORDIC and a sine table for the tweedle factors of the FFT.

In order for the design to be generic, the look-up tables will have to be parametrizable, depending
on the generic parameters of the design, like datapath bitwidth or FFT size. The standard solution
to this problem is to write a script which generates a parametrized
configuration file. Whenever you
 
A

Acciduzzu

Hi,

Thanks for your answers. Yet, a DSP solution is out of question in my implementation.
Another point of the post was how to compute synthesizable constants (integer, sdt_logic_vector)
based on intermediate floating point values. For example, when implementing an asynchronous
protocol, the timings are controlled by counters which are preloaded with constant values.
The size of the counters and their preload values depend on the operating frequency and the time
resolution required.
It would be nice to give the clock frequency in MHz as a parameter and then compute the preload
values in VHDL, but this involves the use of intermediat real values, which are not supported for
synthesis although the real values themselves are not used for implementing hardware. Is this a
frequent limitation of the synthesis tools or only affects Synopsys DC?

constant pulse_width : real := 100; -- in ns
constant clk_freq : real := 80; -- in MHz
constant clk_period : real := 1000/clk_freq; -- in ns
constant preload_value : integer := integer(round(pulse_width/clk_period));

Only preload_value will be used in an assignment, yet DC complains about using reals :(
 
R

Ray Andraka

There are a number of synthesis tools that do not support reals in constant functions. You can
either restrict yourself to a tool that does allow it, or you can work with integers only. .

In your case a simple algebraic manipulation should fix it:

constant pulse_width: integer := 100; in ns;
constant clk_freq: integer:= 80; in MHz
constant preload: integer:= (pulse_width*clock_freq+500)/1000;

If you need fractional resolution on the clock freq or pulse width, use smaller units and change the
preload divisor appropriately:

constant pulse_width: integer := 1000; in ns/10;
constant clk_freq: integer:= 80000; in KHz
constant preload: integer:= (pulse_width*clock_freq+500)/10000000;

Hi,

Thanks for your answers. Yet, a DSP solution is out of question in my implementation.
Another point of the post was how to compute synthesizable constants (integer, sdt_logic_vector)
based on intermediate floating point values. For example, when implementing an asynchronous
protocol, the timings are controlled by counters which are preloaded with constant values.
The size of the counters and their preload values depend on the operating frequency and the time
resolution required.
It would be nice to give the clock frequency in MHz as a parameter and then compute the preload
values in VHDL, but this involves the use of intermediat real values, which are not supported for
synthesis although the real values themselves are not used for implementing hardware. Is this a
frequent limitation of the synthesis tools or only affects Synopsys DC?

constant pulse_width : real := 100; -- in ns
constant clk_freq : real := 80; -- in MHz
constant clk_period : real := 1000/clk_freq; -- in ns
constant preload_value : integer := integer(round(pulse_width/clk_period));

Only preload_value will be used in an assignment, yet DC complains about using reals :(

--
--Ray Andraka, P.E.
President, the Andraka Consulting Group, Inc.
401/884-7930 Fax 401/884-7950
email (e-mail address removed)
http://www.andraka.com

"They that give up essential liberty to obtain a little
temporary safety deserve neither liberty nor safety."
-Benjamin Franklin, 1759
 
A

aser

Acciduzzu said:
It would be nice to give the clock frequency in MHz as a parameter and then compute the preload
values in VHDL, but this involves the use of intermediat real values, which are not supported for
synthesis although the real values themselves are not used for implementing hardware. Is this a
frequent limitation of the synthesis tools or only affects Synopsys DC?

constant pulse_width : real := 100; -- in ns
constant clk_freq : real := 80; -- in MHz
constant clk_period : real := 1000/clk_freq; -- in ns
constant preload_value : integer := integer(round(pulse_width/clk_period));

Only preload_value will be used in an assignment, yet DC complains about using reals :(

Ideally any synthesizer has to operate with reals when constant calculating.
Then the type transfer function is obligatory needed to
get the resulting integer or bit vector constant.
May be some synthesizers do it.
It would be better to prove them.
But I know that synthesizers do not understand the IEEE.MATH_REAL package ,
which is useful to generate Sine constants.
In this situation I used the way
when the VHDL file with constants is generated by
another VHDL file when simulating.

But time values are not considered by synthesizers at all (neither real nor integer, nor time).
To put the delay value it can be implemented by proper automata
( in Virtex it can be SRL16 unit),
or using the customer attributes
to made the place and route tool
to keep the wire delay in the range given by your generics.
These attributes are usually different to different technology or synthesizer.

Regards,
A.Ser.
 
A

Acciduzzu

Thanks again for answers. If even the VHDL tools are so picky about
synthesizable constructs, I wonder what the Verilog guys must go through if they
need to write some decent reusable modules :)
I hope the future has something better in stock for us.

Regards,

Acciduzzu
 
J

Jonathan Bromley

If even the VHDL tools are so picky about
synthesizable constructs, I wonder what the
Verilog guys must go through if they
need to write some decent reusable modules :)

[RantMode = ON]
VHDL has, for nearly twenty years, had the facilities
to do the simple and sensible stuff you are asking for.
Verilog hasn't. But most of the synth tools are
obsessively Verilog-centric, and can't or won't do
anything in VHDL unless it has an equivalent in Verilog.
That, surely, is:
- the only possible explanation why one well-known
synth tool processes VHDL default values on input
ports, but then sets them to zero whatever the
default you've specified;
- the only possible explanation why one well-known
synth tool can't handle VHDL entities with unconstrained
array ports;
- the only possible explanation why it took so long for
synth tools to learn about multi-dimensional arrays on
ports and nets;
[RantMode = OFF]
.... I could go on, but I'll spare y'all. And I promise
I'll give up language-ranting for Lent.

--

Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * Perl * Tcl/Tk * Verification * Project Services

Doulos Ltd. Church Hatch, 22 Market Place, Ringwood, Hampshire, BH24 1AW, UK
Tel: +44 (0)1425 471223 mail: (e-mail address removed)
Fax: +44 (0)1425 471573 Web: http://www.doulos.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
M

Mike Treseler

Acciduzzu said:
Thanks for your answers. Yet, a DSP solution is out of question in my
implementation. Another point of the post was how to compute synthesizable
constants (integer, std_logic_vector) based on intermediate floating point
values.

Leonardo can handle real declarations
and conversions to create integer or vector
values for synthesis. See below:

-- Mike Treseler

---------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
USE IEEE.MATH_REAL.all;

-- . . .

constant pi : real := 3.141592 ;
constant sin_pi_over_8 : integer :=
integer(round( (2.0)**16 * sin(2.0*pi/16.0)));
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,967
Messages
2,570,148
Members
46,694
Latest member
LetaCadwal

Latest Threads

Top