What happens when I provide a function with the parent of the subtype argument it's expecting? - vhdl

While trying to figure out the specifics of the shift_right function from the numeric_std package I noticed that the count argument is of the subtype NATURAL:
function shift_right(ARG: UNSIGNED; COUNT: NATURAL) return UNSIGNED is
begin
if (ARG'length<1) then return NAU; end if;
return UNSIGNED(XSRL(STD_LOGIC_VECTOR(ARG),COUNT));
end;
However when calling the function I can also provide an INTEGER which in contrast to NATURAL can hold a negative number.
Example of calling code that succesfully compiles:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity ExampleCode is
port
(
clk : in std_logic;
input : in signed(15 downto 0);
shift : in signed(3 downto 0);
output : out signed(15 downto 0)
);
end entity;
architecture rtl of ExampleCode is
begin
ProcessExample : process(clk)
begin
if (rising_edge(clk)) then
output <= shift_right(input, to_integer(shift));
end if;
end process;
end rtl;
The numeric_std package shows that if you use to_integer with a signed argument that it returns an integer:
function TO_INTEGER ( ARG: SIGNED) return INTEGER;
My questions are;
Does VHDL always allow parents of subtypes to be provided as arguments to functions?
When it does allow a parent type, how does it resolve the imposed constraints of the subtype?

The other answer is very detailed, but I think goes down a bit of a rabbit hole, when your specific questions can be answered more succinctly. I have answered from a perspecitve of what will practically happen in a real tool that you might use, as opposed to trying to re-interpret the language standard.
An important feature of a subtype is that there is automatic 'conversion'+ to and from the parent type. The example below clearly shows this with an enumerated type. The same automatic conversion would be invoked when passing a parent-type parameter to a function that expects the sub-type.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity e is
end;
architecture a of e is
type r is (A, B, C, D);
subtype rs is r range A to C;
signal x1 : r := B;
signal x2 : r := D;
signal xs : rs;
begin
process
begin
xs <= x1; -- Fine.
wait for 1 ns;
xs <= x2; -- Run-time error, "Value 3 is out of range 0 to 2".
wait;
end process;
end;
Does VHDL always allow parents of subtypes to be provided as arguments to functions?
From the above, yes, it does, but in many tools, you will get an error if the automatic type conversion cannot succeed, as it obviously cannot in the second assignment in the example. Note that if x2 was a constant, a tool could work out that the conversion is not going to be possible, and thrown up a compile-time error then instead.
The same applies with natural and integer; since natural is defined as subtype natural is integer range 0 to integer'high, natural in a sense is an integer, so automatic 'conversion' is simple and reasonable as long as the integer is not outside the natural's range.
When it does allow a parent type, how does it resolve the imposed constraints of the subtype?
Whether standardised or not, a particular tool might implement this in a variety of ways, so you might see different behavior for out-of-range parent-typed values with different tools.
For example, when I tried with ModelSim, it appears that its conversion from integer to natural simply copies the value, meaning that shift_right will surprisingly work with a negative value in that tool, if the shift amount integer is not a constant (at least for version 10.7e).
Obviously it is not sensible to rely on a particular behavior, but regardless of behavior, using a sub type can offer you more protection than just using base types throughout a design.
+ It's not really conversion in VHDL, but if you've used pretty much any other language, this is how you will tend to refer to it.

Related

Can you make an array of types in VHDL?

Vivado Simulation cannot support unconstrained types which have a signed component to them.
i.e.
type A is array (natural range <>) of signed;
I have been using this in a design where type A is used in port declarations as I wish to have a parallel design which I control through a generic as well as the current stage word length e.g.
port (
inputdata : A(0 to number_of_parallel_generic-1)(stage_wordlength_generic-1 downto 0)
);
As I use the type A with many variations of the generics controling them e.g. 4 wide arrays with 16 wordlengths and other variations (often controled by a for generate loop)
for i in 0 to length_of_generate_statement-1 generate
signal example_signal : A(0 to 3)(stage_wordlength_generic + i - 1 downto 0);
begin
<functional code>
end generate;
This sort of code would allow me to gain bit growth from sequential sections of my archetecture - e.g. from an addition.
Now... getting to the question at hand.
One way I could get round this rather than initiating a signal with a forever changing generate statement could actually be in the creation of an "array of types".
Lend me your eyes this is written in a not quite vhdl way but hopefully you can see what Im trying to do.
type my_arr_of_types is array(0 to length_of_array-1) of type;
for i in 0 to length_of_array-1 generate
my_arr_of_types(i) <= <type declaration with some dependance on i>;
end generate;
Hopefully you can see what I am trying to do.
This would allow you to then call an element of the my_arr_of_types which itself is a type to then assign to a signal/variable.
i.e.
signal my_sig : my_arr_of_types(n);
*Where n is any valid index of the array.
Obviously this is not allowed in VHDL or any simulation tool. But can anyone see a potential solution to my problem?
Remember I use most of these types on port statements so any solution has to fit within the limitations of the port declarations.
Using two dimensional arrays as a solution:
Package
library ieee;
use ieee.numeric_std.all;
package utilities is
type T_SLM is array(natural range <>, natural range <>) of std_logic;
end package;
Entity
Now you can use this type in a port declaration together with two generic parameters. As sizes are now known in the architecture, you can create your used defined type of signed values and you can use either generate statements or a function to convert from the T_SLM to myArray type.
library ieee;
use ieee.numeric_std.all;
library myLib;
use myLib.utilities.all;
entity foo is
generic (
number_of_parallel : natural;
stage_wordlength : natural
);
port (
Input : T_SLM(0 to number_of_parallel - 1, stage_wordlength - 1 downto 0)
);
end entity;
architecture a of foo is
type myArray is array (natural range <>) of signed(Input'range(2));
function convert(matrix : T_SLM) return myArray is
variable result : myArray(matrix'range(1));
begin
for i in matrix'range(1) loop
for k in matrix'range(2) loop
result(i)(j) := matrix(i, j);
end loop;
end loop;
return result;
end function;
signal InputData1 : myArray(Input'range(1));
signal InputData2 : myArray(Input'range(1));
begin
genInput: for i in Input'range(1) generate
genInput: for j in Input'range(2) generate
InputData1(i)(j) <= Input(i, j);
end generate;
end generate;
InputData2 <= convert(Input);
end architecture;
Many helper functions like this have been implemented in the PoC Library in package PoC.vectors.

VHDL-2008 to_01 conversion

I am getting some unexpected behavior when using the to_01 conversion function in VHDL-2008. My expectation would be that vector bits that can clearly be interpreted as high or low are mapped to '1' and '0' respectively. The remaining vector bits should be converted to '0' bits.
However, with the code depicted below, I get the whole vector converted to all '0's.
Is this behavior correct? Or is this a bug in the simulator software (ALDEC Riviera-PRO)?
Is there any IEEE function that meets my expectations or do I have to write my own function to achieve that?
library ieee;
use ieee.std_logic_1164.all;
entity test_to_01 is
end entity test_to_01;
architecture rtl of test_to_01 is
signal s_test_in : std_logic_vector(8 downto 0) := "UX01ZWLH-";
signal s_test_out : std_logic_vector(8 downto 0);
begin
s_test_out <= to_01(s_test_in);
end architecture rtl;
The observed behavior is the correct behavior. A little history about this follows.
In 2008, we propagated all of the strength reduction operations to all std_logic family packages. For better or worse, the historical implementation of to_01 comes from numeric_std and was implemented exactly as it is now. The following is an older implementation I was able to find on the web:
function TO_01(S : SIGNED ; xmap : STD_LOGIC:= '0') return SIGNED is
variable RESULT: SIGNED(S'length-1 downto 0);
variable bad_element : boolean := FALSE;
alias xs : SIGNED(s'length-1 downto 0) is S;
begin
for i in RESULT'range loop
case xs(i) is
when '0' | 'L' => RESULT(i):='0';
when '1' | 'H' => RESULT(i):='1';
when others => bad_element := TRUE;
end case;
end loop;
if bad_element then
assert NO_WARNING
report "numeric_std.TO_01: Array Element not in {0,1,H,L}"
severity warning;
for i in RESULT'range loop
RESULT(i) := xmap; -- standard fixup
end loop;
end if;
return RESULT;
end TO_01;
One of the prime directives of the VHDL WG is to not break old code. In this case it looks like this objective put forward an implementation that perhaps is less desirable.
If you want something different, you can always put it forward for the next revision of the standard. It would have to have a different name. Note we are currently closing on VHDL-2018 now, so it would be the revision after that.
Note that IEEE P1076 WG is an individual based working group. This means experienced users, such as yourself, are participating. Typically the amount of work done in a standards revision is overwhelming. As a result, we always need more active participants. Particularly working on the packages. See eda-twiki.org and http://www.eda-twiki.org/cgi-bin/view.cgi/P1076/WebHome
I found a workaround:
s_test_out <= to_stdlogicvector(to_bitvector(s_test_in));

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.

VHDL Parametric case

I've some problem with my synthesis tool. I'm writing a module and I'm tryng to make it parametric and scalable. In my design I've a FSM and some counters. The counters have a parametric width ( they are function of the width of the datapath ). The problem is that I'm using that counter to drive a case statements. The synthesizer gives me back this error :
2049990 ERROR - (VHDL-1544) array type case expression must be of a locally static subtype
I've also tried to use subtype, but it doesnt work. The declaration is :
constant LENGTH_COUNTER_WORD : integer := integer(ceil(log2(real(WIDTH_DATA/WIDTH_WORD))));
subtype type_counter_word is std_logic_vector( LENGTH_COUNTER_WORD - 1 downto 0);
signal counter_word : std_logic_vector( LENGTH_COUNTER_WORD - 1 downto 0);
The case :
case type_counter_word'(counter_word) is
when (others => '1') =>
do_stuff();
when others =>
do_other_stuff();
end case;
I cannot switch to VHDL-2008. I've read I can use variable, but I'd like to find a different solution, if it exists. I cannot imagine there isn't any way to give parameters to synthesizer before the synthesis.
This is fixed in VHDL-2008. You can only work around it in earlier standards by using cascaded if statements (with the attendant priority logic). Variables don't make a difference when determining if choices are locally static.
I'm not sure how complicated your do_stuff() and do_other_stuff() operations are, but if you are just doing simple signal assignments, you could look into the and_reduce() function in the ieee.std_logic_misc library.
As an example:
output <= '1' when and_reduce(type_counter_word'(counter_word)) = '1' else '0';
Otherwise, as Kevin's answer suggests, a process block using if statements might be your best option.
About the time of Kevin's good enough answer, I had written this to demonstrate:
library ieee;
use ieee.std_logic_1164.all;
use ieee.math_real.all;
entity counterword is
generic (
WIDTH_DATA: positive := 16;
WIDTH_WORD: positive := 8
);
end entity;
architecture foo of counterword is
constant LENGTH_COUNTER_WORD : integer :=
integer(ceil(log2(real(WIDTH_DATA/WIDTH_WORD))));
subtype type_counter_word is
std_logic_vector( LENGTH_COUNTER_WORD - 1 downto 0);
signal counter_word : std_logic_vector( LENGTH_COUNTER_WORD - 1 downto 0);
procedure do_stuff is
begin
end;
procedure do_other_stuff is
begin
end;
begin
UNLABELLED:
process (counter_word)
begin
-- case type_counter_word'(counter_word) is
-- when (others => '1') =>
-- do_stuff;
-- when others =>
-- do_other_stuff;
-- end case;
if counter_word = type_counter_word'(others => '1') then
do_stuff;
else
do_other_stuff;
end if;
end process;
end architecture;
Note because type_counter_word is a subtype you can provide the subtype constraints in a qualified expression for the aggregate:
if counter_word = type_counter_word'(others => '1') then
From IEEE Std 1076-2008:
9.3.5 Qualified expressions
A qualified expression is a basic operation (see 5.1) that is used to explicitly state the type, and possibly the subtype, of an operand that is an expression or an aggregate.
This example analyzes, elaborates and simulates while doing nothing in particular. It'll call the sequential procedure statement do_other_stuff, which does nothing.
(For do_stuff and do_other stuff, empty interface lists aren't allowed).

Conversion function "To_bit" must have exactly one formal parameter

I am getting above error while running modelsim on a VHDL Testcase and I am unable to understand why is it an error.
The Testcase:
LIBRARY IEEE;
Use ieee.std_logic_1164.all;
entity a is
port (in11 : in std_logic
);
end a;
Architecture a of a is:
component b_1
port ( in1 : in bit);
end component;
begin
inst : b_1 port map ( in1=> **to_Bit**(in11));
end a;
That's a modelsim error, actually it should report that you are not allowed to use this function as actual in a port map, this works:
LIBRARY IEEE; Use ieee.std_logic_1164.all;
entity a is port (in11 : in std_logic ); end a;
architecture a of a is
signal inBit : Bit;
component b_1 port ( in1 : in bit); end component;
begin
inBit <= to_bit(in11);
inst : b_1 port map ( in1=> inBit); end a;
There are restrictions that apply to actuals in port maps, c.f. vhdlref:
The actual, if a port or signal, must be denoted by a static name
(see 6.1). The actual, if an expression, must be a globally static
expression (see 7.4).
The thing is, both cases should be globally static...
VHDL-93 allows type conversions and conversion functions in association lists.
A conversion function is a special case of a function with only one argument.
Let's look at the declaration of to_bit:
function to_bit(s : std_ulogic; xmap : bit := '0') return bit;
Although to_bit(s) looks like a valid conversion function, it's not, because the declaration contains two arguments.
The second argument xmap is used as the result when is_x(s) is true.
This is not a ModelSim bug, but maybe the error message is a bit cryptic. ModelSim figures that to_bit is meant to be a conversion function, but refuses to use it, because it has a second argument, and is thus not a valid conversion function.
A simple wrapper function can solve the problem:
function to_bit(s : std_ulogic) return bit is
begin
return to_bit(s, '0');
end;
Note that the function can also have the name to_bit, because VHDL supports function overloading. It would be nice to have this in the package std_logic_1164.

Resources