VHDL way to group constants together - vhdl

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.

Related

How to define in testbench 2 different entity work selected by a variable

Is there a way to define in a TestBench file, 2 different entity.work (with port map) and select one of these 2 by a variable or by another way ?
If I write this:
ENTITY_A_GEN : if I USE_ENTITY_A = true generate
entity work.entity_A
generic map (n => n)
port map (
sig1 => sig1,
....etc
);
end generate ENTITY_A_GEN;
It's giving me an error "ERROR: Syntax error near entity"
You can define a constant in the architecture as a boolean, then use a generate statement:
--In arch section
constant USE_ENTITY_A : boolean := true;
--In code body
ENTITY_A_GEN : if USE_ENTITY_A = true generate
--Code to instantiate Entity A
end generate ENTITY_A_GEN;
ENTITY_B_GEN : if USE_ENTITY_A = false generate
--Code to instantiate Entity B
end generate ENTITY_B_GEN;

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.

Variable generic assignment in generate loop

What I want to do doesn't seem particularly complex, but I can't think of a simple way to do it in VHDL.
I have a component with a generic parameter called FOO. I would like to generate 16 of these components and for the first 8 instances I want FOO to be set to 0 and the other 8 instances I want FOO to be set to 4096.
Ideally, I would be able to do something like this:
generate_loop: for I in 0 to 15 generate
begin
comp_inst: my_component
generic map
(
FOO => 0 when I < 8 else 4096
)
port map
(
...
);
end generate;
This is of course not valid VHDL, but that captures the idea of what I'd like to do.
So my question is: is there a way to implement this in a single generate loop (i.e. without having to have 2 separate generate loops with different indices), and if so, how do I do it?
As user1155120 mentions in the comments, the base type of a for-loop is a universal integer. The fractional part of an integer division will be truncated. You can use this fact to realize your specific system, as
for 0<=i<8, i/8=0
for 8<=i<15, i/8=1
Thus the code could be
generate_loop: for I in 0 to 15 generate
begin
comp_inst: entity work.my_component
generic map (FOO => 4096 * (I/8))
port map (
...
);
end generate;
Alternatively, especially useful for more complex situations, you could do what Brian Drummond suggested: write a function
architecture arch of ent is
function gen_FOO(I : natural) return natural is begin
if I<8 then
return 0;
else
return 4096;
end if;
end function;
begin
generate_loop: for I in 0 to 15 generate
begin
comp_inst: entity work.my_component
generic map (FOO => gen_FOO(I))
port map (
...
);
end generate;

VHDL component and outputs based on generic

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;

Resources