# [SOLVED]Parameterized VHDL constants

Status
Not open for further replies.

#### mtwieg

##### Advanced Member level 5
When coding in C, I often use preprocessors to calculate thresholds and scaling factors based on various parameters. For example, see below:
C:
#define counter_T_sec 10.0e-6 //time between counter ticks, in seconds
#define counter_thresh_sec 1005.4e-6 //want to detect when this much time elapses
#define counter_thresh_uint(counter_thresh_sec/counter_T_sec) //the threshold in counter ticks

#define ADC_VREF 3.0  //reference voltage of ADC in volts
#define ISENSE_GAIN 1.215   //gain of a current sense amplifier, in volts per amp
#define ADC_MAX 4095.0 //max value of ADC
#define ISENSE_THRESH_amp (-5.41) //threshold in amps

uint16 counter;
uint16 counter_thresh=counter_thresh_uint;
.....
if(counter==counter_thresh)
{
}
....
{
}
I put most of my #define parameters as floats, to preserve precision, and the compiler automatically casts it to the right type when assigning it to my variables.

I'm trying to find a similar way to do this in VHDL, but so far haven't found anything close. Generics work fine when I have a finite set of implementations I want to choose from. But in my case some of these parameters could be anything (like the gain of an amplifier).

I see this sort of question brought up often, but usually the requirement is for determining port or variable widths. In my case I just want to control the values of constants (I'm ok with fixing them at a large width to ensure against overflow).

Any tips? I'm working in quartus.

#### FvM

##### Super Moderator
Staff member
I have more often constants defined in a package than passing it through generic ports down the hierarchy. And often as real parameter that is converted to integer respectively fixedpoint when it's instantiated.

#### TrickyDicky

##### Advanced Member level 5
Im not quite sure what you're getting at. Port widths can be set from generics that could be defined based on specific values anywhere. So I dont really understand what the problem is? why not post some VHDL code that you're struggling with?

#### mtwieg

##### Advanced Member level 5
Fair enough, here's a rough attempt at a vhdl implementation of the current sense threshold example I posted before:

Package file:
Code:
package my_pkg is
constant ADC_VREF_mv                        : integer := 3000; --Vref is 3.000V
constant ADC_MAX                            : integer := 4095; --12 bit ADC
constant ISENSE_GAIN_m                        : integer := 1215; --Gain is 1.215 volts per amp
constant ISENSE_THRESH_ma                    : integer := 1410; --threshold is 1.41 amps
end my_pkg;
vdl file:
Code:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.my_pkg.all;

entity example_entity is
port(
ADC_IIN        : in unsigned(11 downto 0); --data from ADC
clk10m                   : in  std_logic;    -- 10MHz clock
reset                    : in  std_logic
);
end entity;

architecture rtl of example_entity is

signal Ithresh_flag                        : std_logic;
constant ISENSE_THRESH_lsb                : unsigned(11 downto 0) := to_unsigned(ISENSE_THRESH_ma*ISENSE_GAIN_m/ADC_VREF_mv*ADC_MAX/1000,12);

-- when the current exceeds 1.41, set Ithresh_flag to 1
begin
Ithresh_proc:process(clk10m,reset)
begin
if reset = '1' then
Ithresh_flag <= '0';
elsif rising_edge(clk10m) then
if(ADC_IIN > ISENSE_THRESH_lsb) then
Ithresh_flag <= '1';
end if;
end if;
end process;
end rtl;
Does that look sensible?

#### FvM

##### Super Moderator
Staff member
Code:
constant ISENSE_THRESH_lsb : unsigned(11 downto 0) := to_unsigned(ISENSE_THRESH_ma*ISENSE_GAIN_m/ADC_VREF_mv*ADC_MAX/1000,12);
The integer expression is evaluated left to right, introducing an unnecessary rounding error. You can improve accuracy by reordering the terms, but it's easier to perform real calculations and convert the final result unsigned.

#### TrickyDicky

##### Advanced Member level 5
Still not really sure what the question is?

#### mtwieg

##### Advanced Member level 5
Right, so I could probably get better accuracy by dividing after multiplying:
Code:
constant ISENSE_THRESH_lsb : unsigned(11 downto 0) := to_unsigned(ISENSE_THRESH_ma*ISENSE_GAIN_m*ADC_MAX/ADC_VREF_mv/1000,12);
But then I run into the hazard of overflowing the 32 bit size of integer type (the example above does), unless I shave off more precision in the starting parameters. I'd like to avoid the tradeoff, like what the C preprocessor achieves.

it's easier to perform real calculations and convert the final result unsigned.
You mean do the calculations manually and then just write in the final result? That's what I'm currently doing, but some of these have many parameters, so it's an error-prone process.

#### FvM

##### Super Moderator
Staff member
No, I mean to do the real calculation in VHDL.

#### mtwieg

##### Advanced Member level 5
No, I mean to do the real calculation in VHDL.
You mean using hardware resources to calculate them at runtime, or with a method similar to my C example with? The former case isn't feasible for me since I don't have many extra multipliers around. For the latter case, I can't find a method of doing it.

#### mtwieg

##### Advanced Member level 5
Still not really sure what the question is?
Honestly I'm not sure how else to state it. I can do something very effectively in the C preprocessor, and want to do something similar in VHDL, but can't find a method that doesn't have pitfalls with loss of precision and/or variable overflow. Maybe the solution I'm looking for is simple and trivial, but searching around the internet hasn't revealed it yet.

#### TrickyDicky

##### Advanced Member level 5
With enough bits, you can be as accurate as you want. Usually its a question of how much loss of precision is acceptable. You need to analyse the calcualtion by hand to work out where a rounding error will occur with little/no effect on the outcome, and where it does. You are also allowed to use the real type (floating point) in the derivation of constant values to keep preceision if you need to and/or it makes the calculation simpler.
--- Updated ---

for example, the following is perfectly useable and synthesisable (this function may require normalisation) .

Code VHDL - [expand]1
2
3
4
5
6
7
8
package my_pkg is
constant ADC_VREF_mv                        : real := 3.000; --Vref is 3.000V
constant ADC_MAX                            : real := 4095.0; --12 bit ADC
constant ISENSE_GAIN_m                      : real := 1.215; --Gain is 1.215 volts per amp
constant ISENSE_THRESH_ma                   : real := 1.410; --threshold is 1.41 amps
end my_pkg;

constant ISENSE_THRESH_lsb                : unsigned(11 downto 0) := to_unsigned( integer(ISENSE_THRESH_ma*(ISENSE_GAIN_m/ADC_VREF_mv)*(ADC_MAX/1000.0) ) ,12);

Last edited:

### mtwieg

points: 2

#### FvM

##### Super Moderator
Staff member
You mean using hardware resources to calculate them at runtime, or with a method similar to my C example with? The former case isn't feasible for me since I don't have many extra multipliers around. For the latter case, I can't find a method of doing it.
I'm talking about compile time calculations.

Consider LPFC, FCLK, ISCALE, U2SCALE being real constants and N_TS an integer constant. Then the below unsigned expression can be used anywhere in the code without consuming hardware resources.

Code:
TO_UNSIGNED(INTEGER(0.5/LPFC/FCLK/ISCALE*U2SCALE*2.0**(15+N_TS-1)),15)

### mtwieg

points: 2

#### mtwieg

##### Advanced Member level 5
I never used the real type and wasn't aware it could be used like that. I guess that's the "simple and trivial" solution I mentioned above.

So if I use the real type in the way TrickyDicky suggests:
Code:
constant ISENSE_THRESH_lsb                : unsigned(11 downto 0) := to_unsigned( integer(ISENSE_THRESH_ma*(ISENSE_GAIN_m/ADC_VREF_mv)*(ADC_MAX/1000.0) ) ,12);
Do the order of operations no longer matter to the result? The only loss of precision is the truncation when converted to integer?

#### TrickyDicky

##### Advanced Member level 5
The order of operations wont neccessarily matter in this case, but it is worth learning the operator precidence to avoid problems like this in future. Also, dont give me all the credit. @FvM suggested the same solution as me at the same time.

#### mtwieg

##### Advanced Member level 5
Yep, thanks to you both. Seems to synthesize well so far. I wish I had asked six months ago, would have saved a lot of tedious work.

Status
Not open for further replies.