Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronic Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

Register Log in

[SOLVED] Parameterized VHDL constants

Status
Not open for further replies.

mtwieg

Advanced Member level 5
Joined
Jan 20, 2011
Messages
3,466
Helped
1,234
Reputation
2,474
Reaction score
1,207
Trophy points
1,393
Activity points
25,971
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
#define ISENSE_THRESH_lsb (ISENSE_THRESH_amp*ISENSE_GAIN/ADC_VREF*ADC_MAX) //threshold in ADC lsbs

uint16 counter;
uint16 counter_thresh=counter_thresh_uint;
int16 ADC_result;
int16 ADC_thresh=ISENSE_THRESH_lsb;
.....
if(counter==counter_thresh)
{
}
....
if(ADC_result<ADC_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
Joined
Jan 22, 2008
Messages
47,455
Helped
14,041
Reputation
28,335
Reaction score
12,692
Trophy points
1,393
Location
Bochum, Germany
Activity points
275,993
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
Joined
Jun 7, 2010
Messages
7,025
Helped
2,058
Reputation
4,133
Reaction score
2,010
Trophy points
1,393
Activity points
38,604
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
Joined
Jan 20, 2011
Messages
3,466
Helped
1,234
Reputation
2,474
Reaction score
1,207
Trophy points
1,393
Activity points
25,971
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
Joined
Jan 22, 2008
Messages
47,455
Helped
14,041
Reputation
28,335
Reaction score
12,692
Trophy points
1,393
Location
Bochum, Germany
Activity points
275,993
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
Joined
Jun 7, 2010
Messages
7,025
Helped
2,058
Reputation
4,133
Reaction score
2,010
Trophy points
1,393
Activity points
38,604
Still not really sure what the question is?
 

mtwieg

Advanced Member level 5
Joined
Jan 20, 2011
Messages
3,466
Helped
1,234
Reputation
2,474
Reaction score
1,207
Trophy points
1,393
Activity points
25,971
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.
 

mtwieg

Advanced Member level 5
Joined
Jan 20, 2011
Messages
3,466
Helped
1,234
Reputation
2,474
Reaction score
1,207
Trophy points
1,393
Activity points
25,971
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
Joined
Jan 20, 2011
Messages
3,466
Helped
1,234
Reputation
2,474
Reaction score
1,207
Trophy points
1,393
Activity points
25,971
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
Joined
Jun 7, 2010
Messages
7,025
Helped
2,058
Reputation
4,133
Reaction score
2,010
Trophy points
1,393
Activity points
38,604
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
    Helpful Answer Positive Rating

FvM

Super Moderator
Staff member
Joined
Jan 22, 2008
Messages
47,455
Helped
14,041
Reputation
28,335
Reaction score
12,692
Trophy points
1,393
Location
Bochum, Germany
Activity points
275,993
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
    Helpful Answer Positive Rating

mtwieg

Advanced Member level 5
Joined
Jan 20, 2011
Messages
3,466
Helped
1,234
Reputation
2,474
Reaction score
1,207
Trophy points
1,393
Activity points
25,971
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
Joined
Jun 7, 2010
Messages
7,025
Helped
2,058
Reputation
4,133
Reaction score
2,010
Trophy points
1,393
Activity points
38,604
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
Joined
Jan 20, 2011
Messages
3,466
Helped
1,234
Reputation
2,474
Reaction score
1,207
Trophy points
1,393
Activity points
25,971
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.
Toggle Sidebar

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Top