Initialize dynamic VHDL array - vhdl

--in the package
type t_array is array (natural range <>) of std_logic_vector (7 downto 0);
type p_array is access t_array;
--in my testbench
variable my_array : p_array := null;
begin
my_array := new t_array(0 to 19);
my_array := ( X"00",X"00",X"00",X"00",X"FF",
X"FF",X"FF",X"FF",X"00",X"00",
X"FF",X"FF",X"FF",X"FF",X"FF",
X"FF",X"FF",X"FF",X"FF",X"FF" );
Error: Target type util_lib.tb_pkg.p_array in variable assignment is different from expression type util_lib.tb_pkg.t_array.
How can I compactly assign all the elements of the array?

(1). Dereference your poincough access type.
my_array.all := (...);
(2) Initialise it from a function
begin
my_array := new_array(20);
The gory details of initialising it can be buried in the function, which could calculate the values algorithmically, copy them from a constant array, or even read the contents from file.
constant initialiser : t_array := (...);
function new_array(length : natural range initialiser'range) return t_array is
variable temp : p_array := new t_array(0 to length - 1);
begin
-- either this
for i in temp'range loop
temp(i) := initialiser(i);
end loop;
-- or simply this
temp.all := initialiser(temp'range);
return temp;
end new_array;
(note the constraint on arguments to new_array : that ensures it won't create an array larger than the initialiser.)

If you like one step, you can also do:
--in my testbench
variable my_array : p_array := null;
begin
my_array := new t_array'( X"00",X"00",X"00",X"00",X"FF",
X"FF",X"FF",X"FF",X"00",X"00",
X"FF",X"FF",X"FF",X"FF",X"FF",
X"FF",X"FF",X"FF",X"FF",X"FF" );
You can also to do this in an initialization:
--in my testbench
variable my_array : p_array := new t_array'(
X"00",X"00",X"00",X"00",X"FF",
X"FF",X"FF",X"FF",X"00",X"00",
X"FF",X"FF",X"FF",X"FF",X"FF",
X"FF",X"FF",X"FF",X"FF",X"FF" );
begin

Your error message:
Error: Target type util_lib.tb_pkg.p_array in variable assignment is different from expression type util_lib.tb_pkg.t_array.
tells us the subtype of the target doesn't match the right hand expression.
That can be cured several ways:
library ieee;
use ieee.std_logic_1164.all;
package initialize is
--in the package
type t_array is array (natural range <>) of std_logic_vector (7 downto 0);
type p_array is access t_array;
end package;
library ieee;
use ieee.std_logic_1164.all;
use work.initialize.all;
entity testbench is
end entity;
architecture fum of testbench is
begin
process
--in my testbench
variable my_array : p_array := null;
begin
my_array := new t_array(0 to 19);
my_array (my_array'range) := (
X"00",X"00",X"00",X"00",X"FF",
X"FF",X"FF",X"FF",X"00",X"00",
X"FF",X"FF",X"FF",X"FF",X"FF",
X"FF",X"FF",X"FF",X"FF",X"FF" );
wait;
end process;
end architecture;
here using a slice name with the range provided by my_array'range.
Without the range the target name is interpreted as an access type name. As a slice name with a discrete range the target name denotes the value of the object type denotes:
IEEE Std 1076-2008 8. Names 8.1 General Paragraph 3 - 4:
Certain forms of name (indexed and selected names, slice names, and attribute names) include a prefix that is a name or a function call. If the prefix of a name is a function call, then the name denotes an element, a slice, or an attribute, either of the result of the function call, or (if the result is an access value) of the object designated by the result. Function calls are defined in 9.3.4.
A prefix is said to be appropriate for a type in either of the following cases:
— The type of the prefix is the type considered.
— The type of the prefix is an access type whose designated type is the type considered.
Here it helps to understand designate is a synonym for denote used to describe the relationship between a value of an access type and the object it references.
paragraph 5:
The evaluation of a name determines the named entity denoted by the name. The evaluation of a name that has a prefix includes the evaluation of the prefix, that is, of the corresponding name or function call. If the type of the prefix is an access type, the evaluation of the prefix includes the determination of the object designated by the corresponding access value. In such a case, it is an error if the value of the prefix is a null access value. It is an error if, after all type analysis (including overload resolution), the name is ambiguous.
In this case you can use a slice name that encompasses the entire array.
You can use a selected name for the access type object designates:
architecture fie of testbench is
begin
process
variable my_array : p_array := null;
begin
my_array := new t_array(0 to 19);
my_array.all := ( X"00",X"00",X"00",X"00",X"FF",
X"FF",X"FF",X"FF",X"00",X"00",
X"FF",X"FF",X"FF",X"FF",X"FF",
X"FF",X"FF",X"FF",X"FF",X"FF" );
wait;
end process;
end architecture;
8.3 Selected names paragraph 5:
For a selected name that is used to denote the object designated by an access value, the suffix shall be the reserved word all. The prefix shall belong to an access type.
Using these methods distinguishes between assignment to an object of an access type (which isn't the type of the composite in the right hand expression) and the allocated object designated by the object of the access type.

Related

vhdl ERRROR: Attribute "range" requires a constrained array prefix

Two questions:
how to get rid of the warning: "Shared variables must be of a protected type." while keeping it as a "shared variable"?
How to fix Attribute "range" requires a constrained array prefix?
First of all, what is a constrained array prefix?
$ vcom.exe -2002 -l test3.vhd
** Warning: test3.vhd(14): (vcom-1236) Shared variables
must be of a protected type.
** Error: test3.vhd(20): (vcom-14402) Attribute "range"
requires a constrained array prefix.
library ieee;
use ieee.std_logic_1164.all;
entity test3 is
end entity;
architecture beh of test3 is
constant dw :integer := 8;
constant depth :integer := 128;
type mem_t is array (integer range<>) of std_logic_vector(dw-1 downto 0);
shared variable ram_block :mem_t(0 to depth-1);
begin
process
variable i:integer;
begin
for i in mem_t'range loop
report integer'image(i);
end loop;
end process;
end architecture;
A protected type in VHDL, is similar to a class in OO programming, where it can have member methods and it can retain state. Since 2002, it is required that shared variables must be of a protected type. By default, most tools only throw a warning to maintain backwards compatibility unless you turn on strict rule checking
So you have two options to remove the warning
revert to VHDL 1993 standard.
Create a protected type.
Your example shows no need for a shared variable. It could be made into a normal (non shared) variable inside the process.
Question 2, I found... But Question 1, I'm still not sure about..
architecture beh of test3 is
constant dw :integer := 8;
constant depth :integer := 128;
type mem_t is array (integer range<>) of std_logic_vector(dw-1 downto 0);
shared variable ram_block :mem_t(0 to depth-1);
begin
process
variable i:integer;
begin
report "left:" & integer'image( ram_block'left);
report "right:" & integer'image( ram_block'right);
for i in ram_block'range loop
report integer'image(i);
end loop;
wait;
end process;
end architecture;

How to extend a record type while remaining backwards compatible with an aggregate?

Many of our modules use an array of configuration descriptor records to configure a Configurable component. The array type, record type and configurable component are declared in a global package that everybody uses.
type config_descriptor_type is record
config0 : integer;
config1 : boolean;
end record;
type config_type is array(natural range <>) of config_descriptor_type;
component Configurable is
generic (
g_CONFIG : config_type
);
end component;
Every module that uses this Configurable has a package containing the configuration information for that module (all existing such configurations must remain unchanged).
constant c_config : config_type(0 to 3) := (
(1, true),
(2, false),
(8, false),
(4, true)
);
This per-module constant is passed to the Configurable instance (the instantiation must also remain unchanged).
config_inst : Configurable
generic map (
g_CONFIG => c_config
);
I am adding some features to Configurable that require an additional field in the config_descriptor_type record.
type new_config_descriptor_type is record
config0 : integer;
config1 : boolean;
config2 : integer; -- this field was added
end record;
I am free to make any changes I like to the global configurable package and entity, as well as to any new modules that use the new feature, but I should not touch any of the existing modules that use the configurable. The new field should obtain some default value if instantiated by an old module.
Is there some way to add such a field without having to modify all of the existing modules that use a configurable?
A type cannot contain a default value, that only comes from an object (signal, variable, constant). And when assigning a value to an object (like the initial value for a constant) all fields will need to be defined.
There is a workaround for this, but will require a code change for all previously defined constants as a one off, which should not need changing again. If you define a basic "init" function where all paramters have a default value, then if you add any more items to the base type then existing code will always return a legal object with all fields assigned.
type config_descriptor_type is record
config0 : integer;
config1 : boolean;
config2 : integer;
end record;
type config_type is array(natural range <>) of config_descriptor_type;
function init_config_descriptor_type( config0 : integer := 0;
config1 : boolean := true;
config2 : integer := 0 ) return config_descriptor_type is
variable r : config_descriptor_type;
begin
r.config0 := config0;
r.config1 := config1;
r.config2 := config2;
return r;
end function;
-- Now you can create configs from the function. Config2 will be default value (0)
constant c_config : config_type(0 to 3) := (
init_config_descriptor_type(1, true),
init_config_descriptor_type(2, false),
init_config_descriptor_type(8, false),
init_config_descriptor_type(4, true)
);

VHDL null file handle

I have a procedure (testbench only, non-synthesisable) which receives data via an AXIS interface and writes it to a byte array. I also want the option to write the received data to file. To do this i've added a file handle such that any test bench using this procedure can declare a file handle and pass it to the procedure and the received data will be written to the given file via the file handle.
Here's the procedure declaration:
procedure AXI_STREAM_RECEIVER
(
-- AXI-Stream Parameters
variable PAYLOAD : inout p_byte_array;
constant SLAVE_READY_BEHAVE : in t_slave_ready_behave := always_ready;
constant READY_GAP_RANGE : in natural := 20;
constant VERIFY_TKEEP : in boolean := false;
file file_handle : text;
-- Interface Clock
signal CLK : in std_logic;
-- Master/Slave I/O
signal axis_tdata : in std_logic_vector;
signal axis_tkeep : in std_logic_vector;
signal axis_tvalid : in std_logic;
signal axis_tlast : in std_logic;
signal axis_tready : out std_logic;
-- Misc.
constant VERIFY_TLAST : in boolean := true;
constant VERBOSE : in boolean := c_verbosity_default;
constant DEBUG_SEVERITY_LEVEL : in severity_level := c_axil_debug_severity_level;
constant DEBUG_PAYLOAD_CONTENT : in boolean := false
);
As I want the write to file to be optional, I was hoping to be able to provide a 'null' file handle as a default when writing to file is not required. I've tried assigning a default but I get:
FILE interface declaration must not contain a default expression
Then i've tried assigning it to 'null' when instanced:
Illegal use of NULL literal.
But then if I leave it with no default and not assigned I get:
No feasible entries for subprogram "AXI_STREAM_RECEIVER"
Anybody know if it's possible to pass in some sort of null file descriptor?
You can achieve this using a package with generics using VHDL-2008. The file handle and procedure are declared separately within the package header. Here's an example:
library ieee;
use ieee.std_logic_1164.all;
library std;
use std.textio.all;
package gen_pkg is
generic (
PRINT_TO_FILE : boolean;
FILE_NAME : string;
FILE_MODE : file_open_kind := write_mode
);
file outfile : text;
procedure TEST_PROCEDURE;
end gen_pkg;
package body gen_pkg is
procedure TEST_PROCEDURE is
variable outline : line;
begin
write(outline,string'("TEST STRING"));
if PRINT_TO_FILE then
file_open(outfile, FILE_NAME, FILE_MODE);
writeline(outfile, outline);
file_close(outfile);
end if;
end TEST_PROCEDURE;
end gen_pkg;
I've only shown a string being written, but use any of the overloaded variants of write in the textio package depending on your required datatype or you can use the VHDL-2008 function to_string which supports conversions of all types.
Then in your testbench, create a new instantiation of the package and access procedures/functions, etc. using the instantiation name:
library ieee;
use ieee.std_logic_1164.all;
library std;
use std.textio.all;
entity tb is
end tb;
architecture arch of tb is
package gp is new work.gen_pkg
generic map (
PRINT_TO_FILE => TRUE,
FILE_NAME => "./test_file.txt",
FILE_MODE => append_mode
);
begin
process begin
for i in 0 to 4 loop
gp.TEST_PROCEDURE;
end loop;
wait;
end process;
end arch;
Please note, if you write to the file more than once, like shown in this example, file mode must be append_mode. In write_mode, the file will be overwritten everytime file_open is called. If you only write to the file once per simulation, write_mode is fine. You can also have multiple new instantiations of your generic package in multiple locations, all writing to the same file, as along as they all use append_mode for the file mode.
Here's the working example on EDA playground setup to use Aldec Riviera Pro 2017.02. A login is required to run it. You must have pop-up blocker disabled in your browser in order to download the output file to inspect. The string "TEST STRING" should be written to the file 5 times.
In VHDL 2008 and earlier, you must always connect a file object in an interface list to another file object. Accessing the object when it is not open will cause an error, but there is no way to detect if the file is open or not. VHDL2019 does add a FILE_STATE function to do this, but I assume you still need to connect it to an existing file object with no defaults allowed.
Would it be easier to pass in a string of the filepath, which can have a default of ""? If it is a null string then dont open the file.

Why is there an apostrophe before a parenthesis in this VHDL function?

I'm new to coding test benches and so there is a lot of new syntax for me to learn. I'm stuck on trying to understand what the apostrophe after "string" is indicating.
It doesn't appear to be an attribute from here. Also, I've never seen a parenthesis after an apostrophe in VHDL.
procedure Shrink_line(L : inout LINE; pos : in integer)
is
variable old_L : LINE := L;
begin
if pos > 1 then
L := new string'(old_L(pos to old_L'high));
Deallocate(old_L);
end if;
end;
It's a qualified expression.
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."
qualified_expression ::=
type_mark ' ( expression )
| type_mark ' aggregate
This qualified expression states both the type and the subtype for the allocator (9.3.7 Allocators) assigned to L in the procedure.
The operand of the qualified expression is the expression old_L(pos to old_L'high).
It's not an aggregate, it doesn't use named association to distinguish it from a parenthesized expression having a single choice (9.3.3 Aggregates).
Because you're not using a reference covering expressions you may not be aware what this allocator can do. Creating an MCVE:
use std.textio.all;
entity foo is
end entity;
architecture fum of foo is
procedure Shrink_line(L : inout LINE; pos : in integer)
is
variable old_L : LINE := L;
begin
if pos > 1 then
L := new string'(old_L(pos to old_L'high));
Deallocate(old_L);
end if;
end;
begin
process
variable L: LINE;
begin
write (L, string'("...shrinking violet"));
Shrink_line(L, 14);
writeline(OUTPUT,L);
wait;
end process;
end architecture;
Running this code in a simulation provides an output:
ghdl -a foo.vhdl
ghdl -e foo
ghdl -r foo
violet
The output comes from the writeline to OUTPUT (which is the File STD_OUTPUT (stdout in POSIX parlance).
9.3.7 Allocators, para 2:
The type of the object created by an allocator is the base type of the type mark given in either the subtype indication or the qualified expression. For an allocator with a subtype indication, the initial value of the created object is the same as the default initial value for an explicitly declared variable of the designated subtype. For an allocator with a qualified expression, this expression defines the initial value of the created object.
Creating the new allocator with a subtype constraint from the old allocator copied the string from the old allocator starting at position pos.
One other thing to note is that a string left bound is 1 by default. Shrink_line counted on that.

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