VHDL component and outputs based on generic - vhdl

I have a system consisting out of several components that has to be attached to a bus. However to keep the system bus independent, I gave the system a generic bus port that I run through a bus specific module that translates between my system and a specific bus. Thus it is easy to connect the entire system to different buses by switching the translating module.
However I don't want to go through the hassle of connecting the system with the translation module every time. Thus I wonder if it is possible to instantiate a module from a generic parameter, and use its types for the outputs of the architecture.
The whole becomes clearer with a little illustration, my translator modules have the following "signature".
entity translate<BusName>
port(toSystem: out toSystem_t,
fromSystem: in fromSystem_t,
toBus: out to<BusName>_t,
fromBus: in from<BusName>_t
end entity;
I now want to build an entity containing the system and a translator, based on a generic, somewhat like so:
entity entireSystem is
generic(busType := translate<BusName>)
port(toBus: out to<BusName>_t,
fromBus: in from<BusName>_t)
end entity
architecture genArc of entireSystem is
signal toTrans: fromSystem;
signal fromTrans: toSystem;
begin
system: system(toTrans,fromTrans)
translator: translate<BusName>(
fromTrans,
toTrans,
toBus,
fromBus
)
end architecture;
My problems are:
Can I use a generic parameter to directly instantiate a component, or do I have to go the if generic=xxx generate path? Can I derive the type of ports from a generic parameter.
It would be best if I can use one generic parameter to determine the ports and the entity, so that one can not pick wrong types for an entity by accidents. It would be fine to derive the types from the generic parameter with a function.

I don't think so, no.
If you want to instantiate different things based on a generic, you have to use if..generate.
The only influence that generics can have on the types of ports is that of changing the width. You can't switch between (say) an integer and a boolean based on a generic.

Can I use a generic parameter to directly instantiate a component,
or do I have to go the if generic=xxx generate path?
You can use the generic map syntax in module instantiation to pass the bus size to your sub-components. i.e.:
u_system: system
generic map ( INPUT_WIDTH => INPUT_WIDTH, OUTPUT_WIDTH => OUTPUT_WIDTH )
port map ( ... )
In the top level, you will need to have two generics instead of one.
Alternatively, assuming that your top level component must have the same bus size as your sub-components, you can try to do this in a package file and define the bus size by using function call. i.e.:
package pack is
-- # Define support bus name here
constant BusA : integer := 0;
...
constant BusZ : integer := 25;
-- # Bus name here
constant busname : integer := BusA;
-- # Input and Output Width
constant IWIDTH : integer := getIWIDTH( busname )
constant OWIDTH : integer := getOWIDTH( busname )
-- # Function declaration
function getIWIDTH( ii: integer ) return integer;
function getOWIDTH( ii: integer ) return integer;
end pack;
package body pack is
function getIWIDTH( ii: integer ) return integer is
variable oo : integer;
begin
-- # get iwidth here
return oo;
end function;
function getOWIDTH( ii: integer ) return integer is
variable oo : integer;
begin
-- # get owidth here
return oo;
end function;
end package body;

Related

How to return record with unconstrained 2d array from a function

I have a record type(REC) which contains an element of type VECTOR which is an unconstrained 2d array of STD_LOGIC:
package TEST is
type VECTOR is array (NATURAL range <>, NATURAL range <>) of STD_LOGIC;
type REC is record
data : VECTOR;
end record REC;
function con(
a : REC;
b : REC)
return REC;
end package TEST;
When declaring the body of the con function I want to specify the size of the REC.data array like so:
package body TEST is
function con(
a : REC;
b : REC)
return REC is variable r : REC(
data(a.data'length(1) + b.data'length(1) - 1 downto 0, a.data'length(2) - 1 downto 0));
begin
-- . . . do things
end function con;
end package body TEST;
but in the line where I attempt to set the size of data it Vivado throws the following error:
Sliced name is allowed only on single-dimensional arrays
Does this mean I cannot have an unconstrained 2d arraign in a record or is there a different method for defining the size of it in the con function ?
---- EDIT ----
Since I now understand that it is not possible to use 2d arrays in this context what method should I use to create a function which:
Takes two 2d arrays (or arrays of arrays) of size [x][y] and [z][y] as inputs
Outputs an array of size [x+z][y]
With both input arrays being unconstrained and the output array being constrained to size [x+z][y]
And all arrays (input and return) be a record type
user1155120 I don't think I fully understand what you are trying to say. Are you saying my code is not
a minimal reproducible example, because except for me forgetting to include the STD_LOGIC library
the code reproduces the problem when I paste it into Vivado, and it is about as minimal as I can get it.
Or are you saying that the code you linked as this works works for you, because at least in my
Vivado it still throws the same error ? – Mercury 4 hours ago
The original comment was meant for Tricky. There's a missing return statement in the function con which would prevent use in an example.
It isn't clear if record constraints (a -2008 feature) are supported in Vivado Simulator. In Vivado Synthesis Guide UG901 2020.1 we see all sorts of wonderful uses of record types from everything to inferring ram to record elements of record types. Xilinx has been busy.
It'd seems odd if unconstrained elements in record type declarations were supported but not record constraints (they're pretty much a package deal, pun aside). Note the comment called it a -2008 feature.
A record constraint is limited to supply the constraint of an element or subelement that is of an unconstrained type (typically arrays). VHDL has always been capable of supplying multi-dimensional array constraints. The language of the standard is particular based on the flexibility of the syntax.
This code is VHDL -2008 compliant and provides your record constraint:
library ieee; -- ADDED for MCVe
use ieee.std_logic_1164.all; -- ADDED
package TEST is
type VECTOR is array (NATURAL range <>, NATURAL range <>) of STD_LOGIC;
type REC is record
data: VECTOR;
end record REC;
function con (a: REC; b: REC) return REC;
end package TEST;
package body TEST is
function con (a: REC; b: REC) return REC is
variable r:
REC ( -- record constraint:
data (
natural range a.data'length(1) + b.data'length(1) - 1 downto 0,
natural range a.data'length(2) - 1 downto 0
)
);
begin
-- . . . do things
return r; -- ADDED required return statement
end function con;
end package body TEST;
You'll note the changes to the record constraint from yours is the addition of natural range before each range of the element data constraint.
From IEEE Std 1076-2008 5.3.3 Record types:
record_constraint ::=
    ( record_element_constraint { , record_element_constraint } )
record_element_constraint ::= record_element_simple_name element_constraint
From 6.3 Subtype declarations:
element_constraint ::=
      array_constraint
    | record_constraint
Here the element data is an array so an array_constraint is appropriate.
From 5.3.2 Array types, 5.3.2.1:
array_constraint ::=
    index_constraint [ array_element_constraint ]
    | ( open ) [ array_element_constraint ]
Because element data array elements are scalar (enumeration type STD_LOGIC) we follow index_constraint.
index_constraint ::= ( discrete_range { , discrete_range } )
discrete_range ::= discrete_subtype_indication | range
The code shown above uses a discrete subtype indication for the index ranges of the element data dimensions and successfully analyzes (compiles).
From 5.2.2 Scalar types, 5.2.2.1:
range ::=
      range_attribute_name
    | simple_expression direction simple_expression
direction ::= to | downto
The constraint in the question uses a range with simple expressions and direction.
So why did it produce the error message about multi-dimensional slices?
In 9. Expressions, 9.1 the BNF, simple_expression -> term -> factor -> primary -> name.
In 8. Names, 8.1 name -> slice_name, only multi-dimensional slices are not allowed semantically in 8.5 Slice names who's BNF tells us it's syntactically valid:
slice_name ::= prefix ( discrete_range )
The prefix of a slice shall be appropriate for a one-dimensional array object. The base type of this array type is the type of the slice.
and semantically the prefix data is not appropriate for a one-dimensional array object.
These comments provide bit more context for the problem although it isn't clear which version the record constraint you used in the reported eventual success:
#Mercury when you add a (VHDL) file to Vivado, it's by default set to use VHDL 93, but not 2008. Go
to the property Window and change the file type from VHDL to VHDL 2008. I'm not sure why it
prints the wrong error message in your case. (Vivado likes to confuse users with wrong error messages
...). Anyhow, it should have reported your feature is only supported in 2008 mode. – Paebbels 3 hours
ago
#Paebbels Thank you, that fixed the problem, maybe add it as a sub note to your response so I can
mark it as accepted answer. You just saved me hours of frustration. And I'm already getting familiar with
Vivado's shenanigans, my Favorit one of which is "ERROR close to ..." which has to be one of the most
useless error messages I have experience in a long time :) – Mercury 3 hours ago
As far as detecting -2008 source, there are no -1993 syntax errors and no -2008 new reserved word, delimiters, separators or graphics characters in either the primary unit or the secondary.
That leaves you at the mercy of semantic analysis which failed. You could also note the unconstrained record element wasn't reported during analysis of the package declaration. It occurred during evaluation of the variable r. All declared objects are required to be constrained. VHDL doesn't have a recital of all features, semantics can be restrictive as well. It's legal in places to have unconstrained elements and objects.
Associating semantics rules found in the text of the standard with an textual element of the particular declaration or statement can be tough and the squeaky wheel gets the lubricant. Record constraints are relatively new to VHDL implementations.
The problem appears to have been one of tool familiarity.
This is the definition of row and column merges from PoC's vectors package, see lines starting at 359:
function slm_merge_rows(slm1 : T_SLM; slm2 : T_SLM) return T_SLM is
constant ROWS : positive := slm1'length(1) + slm2'length(1);
constant COLUMNS : positive := slm1'length(2);
variable slm : T_SLM(ROWS - 1 downto 0, COLUMNS - 1 downto 0);
begin
for i in slm1'range(1) loop
for j in slm1'low(2) to slm1'high(2) loop -- WORKAROUND: Xilinx iSIM work-around, because 'range(2) evaluates to 'range(1); see work-around notes at T_SLM type declaration
slm(i, j) := slm1(i, j);
end loop;
end loop;
for i in slm2'range(1) loop
for j in slm2'low(2) to slm2'high(2) loop -- WORKAROUND: Xilinx iSIM work-around, because 'range(2) evaluates to 'range(1); see work-around notes at T_SLM type declaration
slm(slm1'length(1) + i, j) := slm2(i, j);
end loop;
end loop;
return slm;
end function;
function slm_merge_cols(slm1 : T_SLM; slm2 : T_SLM) return T_SLM is
constant ROWS : positive := slm1'length(1);
constant COLUMNS : positive := slm1'length(2) + slm2'length(2);
variable slm : T_SLM(ROWS - 1 downto 0, COLUMNS - 1 downto 0);
begin
for i in slm1'range(1) loop
for j in slm1'low(2) to slm1'high(2) loop -- WORKAROUND: Xilinx iSIM work-around, because 'range(2) evaluates to 'range(1); see work-around notes at T_SLM type declaration
slm(i, j) := slm1(i, j);
end loop;
for j in slm2'low(2) to slm2'high(2) loop -- WORKAROUND: Xilinx iSIM work-around, because 'range(2) evaluates to 'range(1); see work-around notes at T_SLM type declaration
slm(i, slm1'length(2) + j) := slm2(i, j);
end loop;
end loop;
return slm;
end function;
The definition of T_SLM is:
type T_SLM is array(natural range <>, natural range <>) of std_logic;
Currently, I see no problem in nesting this code in another layer like your unconstrained record.
Edit:
Thie code above requires VHDL 2008 to be enabled in Vivado.
When VHDL files are added to Vivado, it's by default set to use VHDL 93, but not 2008. Go to the property Window and change the file type from VHDL to VHDL 2008``.
The printed error message is misleading and points to a false feature being used. The correct message should be it's a VHDL-2008 feature, please enable it.

pass constant to entity to entity in vhdl

I have an image processing entity that I want to test. I created a package with several stimulus as constant. and I created a driver to apply the stimulus to the DUT.
assuming this is the stimulus package:
package sim_pkg is
type pixel is record
x: std_logic_vector(3 downto 0);
y: std_logic_vector(3 downto 0);
end record;
type array_pixel is array (natural range <>) of pixel;
constant array_1 : array_pixel(0 to 2) :=
(0 => (x"0", x"0"),
1 => (x"1", x"1"),
2 => (x"2", x"2")
);
constant array_2 : array_pixel(0 to 3) :=
(0 => (x"0", x"0"),
1 => (x"1", x"1"),
2 => (x"2", x"2"),
3 => (x"2", x"2")
);
-- more stimulus
...
end package;
and a driver that just applies the requested array to the input of the DUT.
entity img_test is
port(pixel_out : out pixel)
end entity;
architecture foo of img_test is
begin
-- here it supposes to receive a constant name, for example array_1, and apply its element to pixel_out?
end architecture;
I am using Vunit, so I want to send a msg to the driver with the stimulus name. I know how to send a msg but I am having a problem figuring out how to tell the driver which stimuli I want to send.
I know that I can have a procedure to apply the stimulus directly from the testbench but I would like to know if it is possible to have a procedure for example apply_img that takes a stimulus name in my testbench that asks the driver to apply a specific stimulus.
procedure apply_img(start : boolean; some parameter to specify the stimulus);
Is it possible to pass a constant name to another entity in a way that this other entity can use the data object that has this name in vhdl
Pushing and popping the individual elements of the record and creating convenience wrapper subprograms is the recommended way in VUnit but let's explore the options.
What you can do is to store your pixel arrays in an associative array/dictionary type of data structure (https://en.m.wikipedia.org/wiki/Associative_array) where the key is the name you pass to the driver. The driver can then search that data structure for the data associated with the key.
You can create such a data structure from scratch but you can also use the dictionary type shipped with VUnit (https://github.com/VUnit/vunit/blob/master/vunit/vhdl/data_types/src/dict_pkg.vhd). It can only map a string (like your name) to a string so you would have to encode your pixel arrays to string to make that work. What you can do is to store your pixels using the integer vector pointer type (https://github.com/VUnit/vunit/blob/master/vunit/vhdl/data_types/src/integer_vector_ptr_pkg.vhd) and then use its encode/decode functions. Finally I recommend that you write wrapper functions to do everything in one step.

VHDL way to group constants together

Is it possible to group related constants together inside of packages?
What I want to do is to have generic constants but then grouped together with related constants and types. In software like Python this could be done with package inside of package or class to group constants together.
What I want to do is something like this:
library constants;
...
if (some_signal = constants.group_a.SOME_CONSTANT_VALUE) then
...
end if;
Reader can see where the constant is coming from like here group_a.
If I understand the question well you can use records inside your package
package ex_pkg is
type constants_group_1_t is record
CONSTANT1 : integer;
CONSTANT2 : integer;
CONSTANT3 : integer;
CONSTANT4 : integer;
end record constants_group_1_t;
constant constant_group1 : constants_group_1_t := (
CONSTANT1 => 1,
CONSTANT2 => 2,
CONSTANT3 => 3,
CONSTANT4 => 4
);
end package;
then you can use it as
liberary work;
...
if some_integer = work.ex_pkg.constants_group1.CONSTANT1 then
end if;
so basically you declare a new record type containing all the constants that you want to use, which can be any of your chosen types, then creating a constant of the newly created type and assign for each field its value. You can then access it like "record.field" moreover you can define a record of records for as deep abstraction as you want.

Convert enum type to std_logic_vector VHDL

I want to know if it is possible to convert a enum type, like FSM states to std_logic_vector or integer. I'm doing a testbench with OSVVM for a FSM and I want to use the scoreboard package to automatically compare the expected state with the actual one.
Thanks!
To convert to integer, use:
IntVal := StateType'POS(State) ;
From there, it is easy to convert to std_logic_vector, but I prefer to work with integers when possible as they are smaller in storage than std_logic_vector. For verification, it will be easier if you start to think more about integers when the value is less than 32 bits.
If you need it as std_logic_vector, using only numeric_std you can:
Slv8Val := std_logic_vector(to_unsigned(IntVal, Slv8Val'length)) ;
For verification, I liberally use numeric_std_unsigned, so the conversion is a easier:
Slv8Val := to_slv(IntVal, Slv8Val'length) ;
In the event you have an integer and want to convert it back to a enumerated value, you can use 'VAL.
State := StateType'VAL(IntVal) ;
In OSVVM, we use records with resolved values to create a transaction interface. We have a resoled types for integers (osvvm.ResolutionPkg.integer_max). We transfer enumerated values through the record using 'POS (as we put it in) and 'VAL (as we get it out).
Note don't confuse 'VAL with 'VALUE. 'VALUE converts a string to a value - opposite to 'IMAGE.
You of course learn all of this in SynthWorks' OSVVM class :).
Maybe like this...
function my_func(inp : t_my_enum) return integer is
begin
case inp is
when stateA =>
return 1;
when stateB =>
return 2;
when others =>
return 0;
end case;
end function my_func;
... <= my_func(stateB);`

Why do we use functions in VHDL

Functions are obviously less verbose to write than entities. But it implies many drawbacks, including:
No generic keyword equivalent
Only one output possible
It appears that functions can be called recursively. May it not be the case with entities? If so, is there any good reason to use functions except for aesthetic purposes?
Functions can't create hardware directly - they have to exist within an architecture to do so. There's nothing to stop you putting all your functionality into a function (or procedure) and then just calling that within a process though.
Regarding some of your other points:
With procedures you can have multiple inout or out parameters.
Entities can recurse... Consider:
entity recurse is
generic (
depth : integer := 1;
param : integer := 3);
port (
a : in integer;
b : out integer);
end entity recurse;
architecture a1 of recurse is
signal c : integer;
begin
c <= a + 1;
bottom: if depth = param generate
b <= a + 1;
end generate bottom;
mid:if depth /= param generate
recurse_1: entity work.recurse
generic map (
param => param,
depth => depth+1)
port map (
a => c,
b => b);
end generate mid;
end architecture a1;
Not very useful, but it synthesises and simulates just fine.
And finally, of course you only use functions for aesthetic purposes (assuming you include maintainability and readability into the definition of aesthetic, which most programming types do in my experience). You only use enumerated types, entities, records and a whole host of other language features for 'aesthetic purposes'. Even assembly mnemonics are aesthetic! Maybe should return to toggling DIP switches :)
Functions in vhdl make the code easy to maintain and read. generally architectures are very big and while debugging if something is not working you can easily find the problematic function and correct it and no need to analyse the entire architecture body.
in case of small codes it's useless but in more big machines it makes you better understand if you consider it function wise.
There is no rule for this so all comments are welcome.
in short : the advantage of functions are
overloading
operators definition
overloading of operators therefore
Better Structure of code
I can see why you are confused, another good question would be why there's both procedure and function. (VHDL seems quite inelegant sometimes!)
That being said, I use both procedures and functions all the time, although mostly in testbenches. For example, for a testbench for a firewall system I made a while back I wrote a procedure called pd_tb_send_udp_packet() that I use repeatedly in the main process, e.g.,
pd_tb_send_udp_packet("10.10.10.2", 1234, false);
pd_tb_send_udp_packet("10.10.10.1", 1234, true);
pd_tb_send_udp_packet("10.10.10.1", 1235, false);
pd_tb_send_udp_packet("ff02:100::1", 1234, false);
pd_tb_send_udp_packet("ff02:101::1", 1234, true);
This procedure generates a random UDP packet with the given addr/port and sends it to the firewall system, then tests whether it is forwarded or not based on the final boolean parameter. Here are the first lines of it, where I use functions from a library:
if f_atvtb_is_ipv6_addr(dest_ip_addr) then
v_ipv6 := true;
v_ipv6_addr := f_atvtb_ipv6_addr(dest_ip_addr);
else
v_ipv6 := false;
v_ipv4_addr := f_atvtb_ip_addr(dest_ip_addr);
end if;
The latter two return 128 and 32 bit std_logic_vectors from the string input, respectively.
While I could probably do all this without using procedures and functions somehow, it would definitely be a lot more messy.

Resources