VHDL: use WHEN - ELSE statement with variables - vhdl

The problem
I'm writing a function in a package which converts some values for a testbench. I want to check the if the output exceeds a maximum value, if it does I want to set it to that maximum value. What I tired was the following:
-- vec_in: 0...1023, returns -14...23.5 dB
function conv_dac602_scale (
vec_in : std_logic_vector)
return real is
variable val_in, dB : real := 0.0;
constant lower : real := -14.0;
constant upper : real := 23.5;
begin -- function conv_dac602_scale
val_in := real(to_integer(unsigned(vec_in)));
dB := (lower+(val_in*((upper-lower)/1024.0)));
return dB when dB <= upper else upper; -- this is the important line! (129)
end function conv_dac602_scale;
When I try to compile this I got the following errors:
** Error: myfile.vhd(129): near "when": expecting ';'
** Error: myfile.vhd(260): VHDL Compiler exiting
I then tried assigning it to a variable r first:
...
r := dB when dB <= upper else upper; -- this is the important line! (129)
return r;
end function conv_dac602_scale;
Which did not change the outcome. I know that I can use a simple if/else clause instead, but my question is why I can't use the when clause.
System
Modelsim SE 10.0b, VHDL 2008

The target <= signal when x is a so called concurrent statement, which is designed for easy creation of signal assignments outside a process. You can use it within an architecture, but not inside a process. If and else are designed for sequential statements within a process. In your case, you'll have to use if/else.
Edit:
Seems this only holds true for Vhdl pre 2008. As fru1tbat pointet out, this is valid vhdl 2008 code and the problem is a not supported feature by the Modelsim compiler.

I would reconsider changing the way you call your return, as it looks like your syntax using WHEN is incorrect.
Clarify what you're trying to do with:
return dB when dB <= upper else upper;
That essentially is "return dB when dB is less than or equal to upper, else upper" where upper will not be returned due to the way you make the return statement. You may want to say: ... else return upper
I would prefer an if statement in this case:
if ( dB <= upper) then
return dB;
else
return upper;
end if;

Related

concurrent procedure call in vhdl

I am trying to understand the concurrent procedure call with different parameters' class. Assuming I have the procedure test and it is called concurrently like below:
ENTITY tb IS
END ENTITY tb;
ARCHITECTURE sim OF tb IS
SIGNAL cnt : integer RANGE 0 TO 3 := 0;
SIGNAL str : string(1 TO 5) := (OTHERS => ' ');
PROCEDURE test (CONSTANT number : IN integer RANGE 0 TO 3 := 0;
SIGNAL num_str : OUT string(1 TO 5)) IS
BEGIN
REPORT "here";
CASE number IS
WHEN 0 => num_str <= "zero ";
WHEN OTHERS => num_str <= "one ";
END CASE;
END PROCEDURE;
BEGIN
test(cnt, str); -- CONCURRENT CALL TO PROCEDURE TEST
PROCESS
BEGIN
FOR i IN 0 TO 3 LOOP
WAIT FOR 10 ns;
cnt <= i;
END LOOP;
WAIT;
END PROCESS;
END ARCHITECTURE sim;
In THE VHDL DESIGNER GUIED
Another point to note about concurrent procedure calls is that if there are no signals
associated with in-mode or inout-mode parameters, the wait statement in the equivalent
process does not have a sensitivity clause. If the procedure ever returns, the process suspends indefinitely. This may be useful if we want the procedure to be called only once at
startup time.
As procedure test has no signals associated with in-mode or inout-mode, it supposes to be executed once and then suspends indefinitely. But with this example, the procedure is executed 4 times.
Can someone explain to me what is happening or what I am missing?
Section 11.4 of the 2008 LRM (IEEE Std 1076-2008):
For any concurrent procedure call statement, there is an equivalent
process statement [...] The equivalent process statement also has no
sensitivity list, an empty declarative part, and a statement part that
consists of a procedure call statement followed by a wait statement.
The procedure call statement consists of the same procedure name and
actual parameter part that appear in the concurrent procedure call
statement.
If there exists a name that denotes a signal in the actual part of any
association element in the concurrent procedure call statement, and
that actual is associated with a formal parameter of mode in or inout,
then the equivalent process statement includes a final wait statement
with a sensitivity clause that is constructed by taking the union of
the sets constructed by applying the rule of 10.2 to each actual part
associated with a formal parameter.
Forget about the last part, in your case things are super simple and your equivalent process is:
process
begin
test(cnt, str);
wait on cnt;
end process;
The CONSTANT class declaration of your procedure's declaration only indicates that (section 4.2.2.2):
For parameters of class constant or variable, only the values of the
actual or formal are transferred into or out of the subprogram call.
It somehow forces you to manipulate this value as if it was a constant and nothing else... inside the procedure's body. It forbids you, for instance, to use signal attributes (e.g. number'EVENT). But it doesn't say anything about the actual parameter that you will associate to this formal parameter when instantiating the procedure.
And the result is, logically, what you observe: your procedure is called 4 times in the equivalent process. Each time the value of the actual parameter, that is, the signal cnt is passed through the formal parameter number.
And your book is right:
[...] if there are no signals associated with in-mode or inout-mode
parameters, the wait statement in the equivalent process does not have
a sensitivity clause.
Indeed, you have a signal (cnt) associated with an in-mode parameter (number).

Random number generator in VHDL

I'm designing a test bench and I need to create a random sequence of bits for one of the system's inputs which is normally controlled by the user.
I also want this sequence of bits not being in the same order every time I run the simulation.
I cannot use a PRNG since its initial state will be predefined meaning it while produce the same numbers every time. I also used the uniform function but I had the same issue.
RAND_GEN : process(clk) is
variable seed1, seed2 : positive := 1;
variable re : real;
begin
if rising_edge(clk) then
uniform(seed1, seed2, re);
if (re < 0.5) then
rand_bit <= '0';
else
rand_bit <= '1';
end if;
end if;
end process;
Is there any alternatives for this problem?
Testing with randomly generated inputs is a powerful tehnique and is the technique commonly used to verify ICs these days. Normally, you would run a test with a known, per-determined seed, whereas you want to be able to generate a varying seed. So, you absolutely MUST record this seed when you run the test and provide a mechanism to run a test using this seed. Otherwise, when you find a bug, you will not be able to test whether you've fixed it. You might find it more useful to a a fixed set of tests with a smaller number of manually-generated seeds.
You could use the linux date command with the %s format specifier, which outputs the number of seconds since 1/1/1970 and redirect that to a file.
date +%s >! seed.txt
Then read the file, eg:
RAND_GEN : process(clk) is
variable seed1, seed2 : positive := 1;
variable re : real;
file F: TEXT;
variable L: LINE;
variable seed_RNG : boolean := false;
begin
if not seed_RNG then
file_open(F, "seed.txt", READ_MODE);
readline (F, L);
read (L, seed1); -- or seed2
file_close(F);
report "seed1 = " & integer'image(seed1);
seed_RNG := true;
end if;
if rising_edge(clk) then
uniform(seed1, seed2, re);
if (re < 0.5) then
rand_bit <= '0';
else
rand_bit <= '1';
end if;
end if;
end process;
I don't know anything of VHDL, but in general I try to avoid randomness in tests. Flaky unit tests, for example, are bad. What's the value of a test that fails only sometimes?
Anyway, supposed you really want to do it, do you have access to a timer? You can initialize the PRNG with the current time as seed. Not cryptographically safe, but probably for this use case good enough.
Just for the record, in case anyone needs something similar, I used the above ideas by creating a do file which first writes the date in a file and then runs the do file of the actual test bench which reads this number as suggested before.
set t [clock seconds]
set outputFile [open date.txt w]
puts $outputFile $t
close $outputFile
do testbench.do

How to use iterate variable in case statement [VHDL]

I want to generate my register interface, because I have several instance (channels) of the same peripheral.
while (I < generic_num_of_instances) loop
case loc_addr is
when 0 + I*256 =>
q_ctrl_reg(I, 31 downto 0) <= s_axi_wdata;
end case;
I := I + 1;
end loop;
And it says: Error: Case choice must be locally static expression.
How can I generate this register interface? Should I use less descriptive if/elseif statements?
Matthew mentioned two important improvements, your case target must be a static expression (whose type is a locally static type) and if you are doing synthesis, you need to use a for loop.
Next, you cannot slice a multi-dimensional array, hence, q_ctrl_reg(I, 31 downto 0) is also invalid. Lets fix that by using an array of an array (shown below):
type q_ctrl_reg_type is array (0 to MAX_REG -1) of std_logic_vector(31 downto 0) ;
signal q_ctrl_reg : q_ctrl_reg_type ;
Now lets put it all together, lets us a for loop instead of the while loop and an if statement instead of your case statement:
for i in 0 to generic_num_of_instances - 1 loop
if loc_addr = 0 + I*256 then
q_ctrl_reg(I) <= s_axi_wdata;
end if;
end loop;
Yes. Or use Verilog.
Case choices is VHDL have to be locally static - that is they have to be literals, constants or generics. That isn't the case with Verilog.
You're also going to find that you need a when others branch, which will negate any improved readability gained using a case statement. (And given such a coding style is rather unusual anyway, I'd question whether it was ever more readable.)
try making that "0 + I*256" value equal to a variable that you name in the loop i.e.
while (I < generic_num_of_instances) loop
variable A : integer;
begin
A := 0 + I*256;
case loc_addr is
when A =>
q_ctrl_reg(I, 31 downto 0) <= s_axi_wdata;
end case;
I := I + 1;
end loop;
hopefully this should work

How would I create a function to convert from an integer to std_logic vector in VHDL?

I am seeking help as I am learning this language construct.
Here is what I have:
function int_slv(val,width: integer) return std_logic_vector is
variable R: std_logic_vector(0 to width-1):=(others=>'0')
variable b:integer:= width;
begin
if (b>32) then
b=32;
else
assert 2**bits >val report
"value too big for std_logic_vector"
severity warning
end if;
for i in 0 to b-1 loop
if val ((val/(2**i)) MOD 2 = 1) then
R(i)='1';
end if;
end loop;
return(R);
end int_slv;
In addition to 5 syntax errors, one wrong identifier and a modulo reduction expressions expressed as an element of an array as well as several sets of redundant parentheses, your modified code:
library ieee;
use ieee.std_logic_1164.all;
package int2bv_pkg is
function int_slv (val, width: integer) return std_logic_vector;
end package;
package body int2bv_pkg is
function int_slv (val, width: integer) return std_logic_vector is
variable R: std_logic_vector(0 to width-1):=(others=>'0'); -- added ';'
variable b:integer:= width;
begin
if b > 32 then
b := 32; -- ":=" is used for variable assignment
else
assert 2 ** width > val report -- width not bits
"value too big for std_logic_vector"
severity warning; -- missing semicolon at the end of assertion
end if;
for i in 0 to b - 1 loop
if val/2 ** i MOD 2 = 1 then -- not val (...)
R(i) := '1'; -- ":=" variable assign.
end if;
end loop;
return R; -- parentheses not needed
end int_slv;
end package body int2bv_pkg;
analyzes (compiles). The exponentiation operator "**" is the highest priority, the division operators "/" and "mod" are the same priority and executed in the order they are found (left to right). It's likely worthwhile learning VHDL operator precedence.
You were using "=" for variable assignment when you should have been using ":=" in two places, you were missing two semicolons and were using the identifier bits (which isn't declared in your function) where apparently you meant width.
The modified example analyzes, and hasn't been tested absent a Minimal, Complete and Verifiable example in the question.
Note that a package body is a design unit as is a package declaration. There are various other places in other design units you can introduce a function body.
You could also note the 2 ** 31 is outside the guaranteed range of an integer in VHDL equal to 2147483648, while the INTEGER value range guaranteed to be from -2147483647 to +2147483647 at a minimum.
This implies that were ever you are using a value that derived from an expression equivalent to 2 ** 31 you can incur a range error during execution (either at elaboration or during simulation).
This pretty much says you need a VHDL implementation with a larger INTEGER value range or you need to rethink what you're doing.
As a matter of course there are integer to unsigned and integer to signed functions found in package numeric_std in library IEEE.
The result of such can be type converted to std_logic_vector, and the source code can make great learning aids on how to wend through the limitations VHDL imposes. These to_signed or to_unsigned functions would be capable of dealing with the maximum value an INTEGER can hold and specify the length of the resulting array type while providing zero or sign filling for array lengths greater than the INTEGER's binary value. That utility extends to clipping using length as well.
VHDL -2008 package numeric_std_unsigned contains a function To_StdLogicVector that does what your int_slv function is intended to do although limited to a NATURAL range for the integer type input.
As #user1155120 has already indicated, the VHDL-2008 package numeric_std_unsigned has a builtin to_stdlogicvector. And #user1155120 already pointed out the to_signed and to_unsigned in numeric_std are available as well.
So, to expand on the previous answer, you can do:
constant C : integer := -6817563;
constant C_VEC : std_logic_vector(31 downto 0) := std_logic_vector(to_signed(c, 32));
And this mechanism will accept the full range of integer. You can also use to_unsigned, but this is limited to the range of natural.

No function declarations for operator + error in VHDL

In this piece of code I get this error for the line with +
function func (bv1 : in bit_vector; bv2 : in integer) return bit_vector is
variable temp : natural := 2**bv2;
variable result : bit_vector(1 to 32);
begin
report "asd" & natural'image(temp);
result <= bv1 + temp; // this line causes the error
return result;
end func;
The error is :
No function declarations for operator +
How can I solve this? I also get a similar error for "=" as well.
Don't use bit_vectors (or std_logic_vectors, really) for anything you want to do arithmetic on.
Use the ieee.numeric_std library and then declare your signals (or whatever) to be of type signed ot unsigned depending on what type of vector you want. (Or of course, you can just use integers and the subtypes of that)
It's because you try to add a natural to a bit_vector which does not work because they are of different types. So you'll have to use a converter, e.g. as shown here within one of the functions. The other method is to stick to all the same types, but that isn't always possible.
Some initial problems with the code are that VHDL comments markup is --, not
//, and assign to result variable must use :=, since <= is for assign
to signal.
Then, the reason for the error:
No function declarations for operator +
is that VHDL is a strong typed language, so it is not possible just to add a
natural type and a bit_vector type, as attempted in result <= bv1 + temp.
Instead you need to use the package numeric_bit_unsigned, and for example
convert temp to bit_vector using function to_bitvector before adding.
Resulting code can then be:
library ieee;
use ieee.numeric_bit_unsigned.all;
...
function func (bv1 : in bit_vector; bv2 : in integer) return bit_vector is
variable temp : natural := 2**bv2;
variable result : bit_vector(1 to 32);
begin
report "asd" & natural'image(temp);
result := bv1 + to_bitvector(temp, result'length); -- this line causes the error
return result;
end func;
You should check that the length is enough to handle the required values.
However, instead of using bit_vector type, you may consider the
std_logic_vector (depending on the design), since the std_logic_vector has
additional values that may reveal design problem in simulation.

Resources