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.
As part of my description, within a wrapper component I generate N number of rom components. These roms are initialized from a text file containing the rom image. I pass the name of the file with which I wish to initialize each component as a generic parameter.
A hopefully sufficient excerpt of the description is as follows:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
package lnx1_types is
type lnx1_cs is array (integer range <>) of std_logic_vector(7 downto 0);
constant rom_count: integer := 9;
end package lnx1_types;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.all;
use STD.textio.all; -- For reading ucode roms from filesystem
use ieee.std_logic_textio.all;
use work.lnx1_types.all;
entity lnx1_uc_rom is
generic ( file_name : string := "" );
port ( uc_addr : in std_logic_vector(7 downto 0);
uc_q : out std_logic_vector(7 downto 0) );
end entity lnx1_uc_rom;
architecture dataflow of lnx1_uc_rom is
type lnx1_rom is array (0 to 2 ** 8 - 1) of std_logic_vector(7 downto 0);
impure function lnx1_load_rom(file_name : in string)
return lnx1_rom
is
file curr_rom_file: text;
variable curr_il : line;
variable curr_hx : std_logic_vector(7 downto 0);
variable rom : lnx1_rom;
variable good : boolean := TRUE;
begin
file_open (curr_rom_file, file_name, READ_MODE);
for i in rom'range(1) loop
if not endfile(curr_rom_file) then
readline(curr_rom_file, curr_il); -- Read line
read(curr_il, curr_hx, good); -- Read hex code
rom(i) := curr_hx;
end if;
end loop;
return rom;
end function lnx1_load_rom;
signal rom: lnx1_rom := lnx1_load_rom(file_name);
begin
uc_q <= rom(to_integer(unsigned(uc_addr)));
end architecture dataflow;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.all;
use work.lnx1_types.all;
entity lnx1_uc is
port ( uc_addr : in std_logic_vector(7 downto 0);
cs_q : out lnx1_cs(rom_count - 1 downto 0)
);
end entity lnx1_uc;
architecture dataflow of lnx1_uc is
component lnx1_uc_rom is
generic ( file_name : string := "" );
port ( uc_addr : in std_logic_vector(7 downto 0);
uc_q : out std_logic_vector(7 downto 0) );
end component lnx1_uc_rom;
type lnx1_rom_names is array (integer range <>) of string;
constant rom_path: lnx1_rom_names := (
0 => "r0.hex",
1 => "r1.hex",
2 => "r2.hex",
3 => "r3.hex",
4 => "r4.hex",
5 => "r5.hex",
6 => "r6.hex",
7 => "r7.hex",
8 => "r8.hex"
);
begin
ucgen: for i in rom_path'range(1) generate
rom0: lnx1_uc_rom
generic map ( rom_path(i) )
port map (
uc_addr => uc_addr,
uc_q => cs_q(i)
);
end generate ucgen;
end architecture dataflow;
entity intro_main is
end intro_main;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.all;
use work.lnx1_types.all;
architecture testbench_lnx1_uc of intro_main is
component lnx1_uc is
port ( uc_addr : in std_logic_vector(7 downto 0);
cs_q : out lnx1_cs(rom_count - 1 downto 0)
);
end component lnx1_uc;
signal uc_addr: std_logic_vector(7 downto 0);
signal cs_q: lnx1_cs(rom_count - 1 downto 0);
begin
uc0: lnx1_uc
port map (
uc_addr => uc_addr,
cs_q => cs_q
);
main: process
variable index: integer range 0 to 255 := 0;
begin
uc_addr <= std_logic_vector(to_unsigned(index, uc_addr'length(1)));
wait for 5 ns;
index := index + 1;
end process main;
end architecture testbench_lnx1_uc;
Although the description synthesis is without errors, attempt at simulation fails with the following message:
[VRFC 10-322] array element type cannot be unconstrained ["...":1080]
[XSIM 43-3321] Static elaboration of top level VHDL design unit intro_main in library work failed.
line 1080 is referring to
type lnx1_rom_names is array (integer range <>) of string;
I accordingly made the following change:
- type lnx1_rom_names is array (integer range <>) of string;
+ type lnx1_rom_names is array (integer range <>) of string(0 to 32);
Now, by definition, lnx_rom_names no longer has an unconstrained element type. However, not only does this change not remove the previous simulation error, it also introduces some very curious errors during synthesis:
[Synth 8-3302] unable to open file 'r0.hexr1.hexr2.hexr3.hexr4.hexr5.' in 'r' mode ["...":1026]
This implies that during the first iteration of the generate loop, rom_path(i) indeed points to the first item, however there is no delimitation of elements at all - simply, the entire linear segment of data beginning at that point is passed, in a string(0 to 32);.
As I write this, I realize that I have two questions, the latter borne out of trying to answer the first:
Why does the unconstrained element type error persist?
Why is the aforementioned array behavior present, namely the passing of a linear block of data of the array, rather than the actual array member?
I suspect the answer in the latter may be due to VHDL simply not initializing the spare slots left in the string(0 to 32), and thus the next element being allocated immediately afterwards; However, I would not believe VHDL to contain such broken functionality.
As user1155120 pointed out in the comments, the issue was due to incorrect length of the array element type, and had I actually tried to simulate first, I would've been greeted by some very helpful error messages:
Thus, changing the line
type lnx1_rom_names is array (integer range <>) of string(1 to 32);
to
type lnx1_rom_names is array (integer range <>) of string(1 to 6);
Removed all errors and simulation produced the expected result.
Also, strings must have a natural index range, so string(0 to n) is invalid, and should be string(1 to n); all following code has been amended accordingly.
Since I invested (or perhaps wasted) so much time into this problem, I thought it would be a waste not to at-least document my findings regarding string concatenation.
During synthesis Vivado decides to concatenate as many sequential array elements as would fit in the mis-sized array element when trying to pass an array element as an argument to, in this case, a generic map member:
...
type lnx1_rom_names is array (integer range <>) of string(1 to 32);
constant rom_path: lnx1_rom_names := (
0 => "r0.hex",
1 => "r1.hex",
2 => "r2.hex",
3 => "r3.hex",
4 => "r4.hex",
5 => "r5.hex",
6 => "r6.hex",
7 => "r7.hex",
8 => "r8.hex",
);
begin
ucgen: for i in rom_path'range generate
rom0: lnx1_romblk
generic map (
file_name => rom_path(i) -- << rom_path(i) evalues to "r0.hexr1.hexr2.hex ... "
) port map (
addr => uc_addr,
data => cs_q(i)
);
end generate ucgen;
...
I fiddled around with the description some more, and discovered that to replicate the concatenation behavior, the total number of characters present in the array of strings must be greater than the string length of a single array element, i.e.:
type lnx1_rom_names is array (integer range <>) of string(1 to 32);
constant rom_path: lnx1_rom_names := (
0 => "r0.hex",
1 => "r1.hex",
2 => "r2.hex",
3 => "r3.hex",
4 => "r4.hex",
5 => "r5.hex"
);
will fail with [Synth 8-3302] unable to open file 'r0.hexr1.hexr2.hexr3.hexr4.hexr5.' in 'r' mode ["D:/...":48]
however
constant rom_path: lnx1_rom_names := (
0 => "r0.hex",
1 => "r1.hex",
2 => "r2.hex",
3 => "r3.hex",
4 => "r4.hex"
);
will not cause that error to appear.
Comparison of log during synthesis/simulation (images):
With concatenation error
Without concatenation error
For reference, below is the description I used. It is slightly different from the OP as I've already had time to work on it and didn't use version control, but still demonstrates the problem.
The top level entity and architecture are at the bottom and should be renamed if appropriate.
Sample r0.hex file: Click here
The contents are not important, simply duplicate and rename to r1, r2... etc.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
package lnx1_types is
type lnx1_cs is array (integer range <>) of std_logic_vector(7 downto 0);
constant rom_count: integer := 2;
end package lnx1_types;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.all;
use STD.textio.all; -- For reading ucode roms from filesystem
use ieee.std_logic_textio.all;
use work.lnx1_types.all;
entity lnx1_romblk is
generic ( file_name : string := "";
awidth : integer := 8;
dwidth : integer := 8 );
port ( addr : in std_logic_vector(AWIDTH-1 downto 0);
data : out std_logic_vector(DWIDTH-1 downto 0) );
end entity lnx1_romblk;
architecture dataflow of lnx1_romblk is
type lnx1_rom is array (0 to 2 ** AWIDTH - 1) of std_logic_vector(DWIDTH-1 downto 0);
impure function lnx1_load_rom(file_name : in string)
return lnx1_rom
is
file curr_rom_file: text;
variable curr_il : line;
variable curr_hx : std_logic_vector(DWIDTH-1 downto 0);
variable rom : lnx1_rom;
variable good : boolean := TRUE;
begin
-- If no filename passed, initailize with 0
if file_name = "" then
for i in rom'range loop
rom(i) := (others => '0');
end loop;
return rom;
end if;
file_open (curr_rom_file, file_name, READ_MODE);
for i in rom'range loop
if not endfile(curr_rom_file) then
readline(curr_rom_file, curr_il); -- Read line
read(curr_il, curr_hx, good); -- Read binary value
rom(i) := curr_hx;
end if;
end loop;
return rom;
end function lnx1_load_rom;
signal rom: lnx1_rom := lnx1_load_rom(file_name);
begin
data <= rom(to_integer(unsigned(addr)));
end architecture dataflow;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.all;
use work.lnx1_types.all;
entity lnx1_uc is
port ( uc_addr : in std_logic_vector(7 downto 0);
cs_q : out lnx1_cs(rom_count - 1 downto 0)
);
end entity lnx1_uc;
architecture dataflow of lnx1_uc is
component lnx1_romblk is -- Needs testbench
generic ( file_name : string := "";
awidth : integer := 8;
dwidth : integer := 8 );
port ( addr : in std_logic_vector(awidth-1 downto 0);
data : out std_logic_vector(dwidth-1 downto 0) );
end component lnx1_romblk;
type lnx1_rom_names is array (integer range <>) of string(1 to 32);
constant rom_path: lnx1_rom_names := (
0 => "r0.hex",
1 => "r1.hex",
2 => "r2.hex",
3 => "r3.hex",
4 => "r4.hex"
-- 5 => "r5.hex" -- Uncomment this line to generate the error.
-- 6 => "r6.hex",
-- 7 => "r7.hex",
-- 8 => "r8.hex",
);
begin
ucgen: for i in rom_path'range generate
rom0: lnx1_romblk
generic map (
file_name => rom_path(i)
) port map (
addr => uc_addr,
data => cs_q(i)
);
end generate ucgen;
end architecture dataflow;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.all;
use work.lnx1_types.all;
-- Here should go the top level entity declaration; I initially created
-- the project with the name "intro_main", so change to whatever is your case.
entity intro_main is
end entity intro_main;
architecture top_level of intro_main is
component lnx1_uc is
port ( uc_addr : in std_logic_vector(7 downto 0);
cs_q : out lnx1_cs(rom_count - 1 downto 0)
);
end component lnx1_uc;
signal uc_addr : std_logic_vector(7 downto 0);
signal cs_q : lnx1_cs(rom_count - 1 downto 0);
begin
uc0: lnx1_uc port map ( uc_addr, cs_q );
end architecture;
I'm running ModelSim 10.3d, and I have this code in a package:
package core_params_types is
type array_1d_logic is array (natural range <>) of std_logic;
type array_1d_logic_vector is array (natural range <>) of std_logic_vector (natural range <>);
type array_2d_logic is array (natural range <>, natural range <>) of std_logic;
type array_2d_logic_vector is array (natural range <>, natural range <>) of std_logic_vector (natural range <>);
function or_reduce_2d_logic(a : array_2d_logic; i : integer) return std_logic;
function or_reduce_2d_logic_vector(a : array_2d_logic_vector; i : integer) return std_logic_vector;
function bitwise_cmp(a : std_logic_vector; b : std_logic_vector) return std_logic;
function bitwise_cmp(a : std_logic; b : std_logic) return std_logic;
function full_adder(a : std_logic_vector; b : std_logic_vector; ci : std_logic) return std_logic_vector;
function sign_extend(a : std_logic_vector; b : integer) return std_logic_vector;
function sign_extend(a : std_logic; b : integer) return std_logic_vector;
function logic_extend(a : std_logic_vector; b : integer) return std_logic_vector;
function logic_extend(a : std_logic; b : integer) return std_logic_vector;
ModelSim spits the following errors:
-- Loading package STANDARD
# -- Loading package TEXTIO
# -- Loading package std_logic_1164
# -- Loading package NUMERIC_STD
# -- Loading package MATH_REAL
# -- Loading package ATTRIBUTES
# -- Loading package std_logic_misc
# -- Compiling package core_params_types
# ** Error: core_params_types.vhd(40): near "<>": syntax error
# ** Error: core_params_types.vhd(42): near "<>": syntax error
# ** Error: core_params_types.vhd(45): (vcom-1136) Unknown identifier "array_2d_logic_vector".
# ** Error: core_params_types.vhd(48): (vcom-1295) Function "bitwise_cmp" has already been defined in this region.
# ** =====> Prior declaration of "bitwise_cmp" is at core_params_types.vhd(47).
# ** Error: core_params_types.vhd(53): (vcom-1295) Function "sign_extend" has already been defined in this region.
# ** =====> Prior declaration of "sign_extend" is at core_params_types.vhd(52).
# ** Error: core_params_types.vhd(55): (vcom-1295) Function "logic_extend" has already been defined in this region.
# ** =====> Prior declaration of "logic_extend" is at core_params_types.vhd(54).
# ** Error: core_params_types.vhd(310): VHDL Compiler exiting
The .do file contains the following commands:
transcript on
if {[file exists rtl_work]} {
vdel -lib rtl_work -all
}
vlib rtl_work
vmap work rtl_work
vcom -2008 -work work {core_params_types.vhd}
vcom -2008 -work work {alu.vhd}
vcom -2008 -work work {tb_alu.vhd}
vcom -2008 -work work {alu.vhd}
vsim -t 1ps -L altera -L lpm -L sgate -L altera_mf -L altera_lnsim -L cyclonev -L rtl_work -L work -voptargs="+acc" tb_alu
add wave *
view structure
view signals
run -all
I run the ModelSim simulation from Quartus, which compiles the code without errors, and generates a circuit. ModelSim says that the functions are already defined. That is correct, but they have different types, so they should be overloaded. And also ModelSim does not understand the declaration of the array types.
A type declaration
type array_1d_logic_vector is array (natural range <>) of std_logic_vector (natural range <>);
is not valid. You do not declare the index type of the element type.
Instead try:
type array_1d_logic_vector is array (natural range <>) of std_logic_vector;
You constrain the element subtype in an object declaration for example:
variable foo: array_1d_logic_vector(0 to 1)(7 downto 0);
Where the element subtype constraint is 7 downto 0 and the array constraint is 0 to 1.
See IEEE Std 1076-2008 5.3.2 Array types, 5.3.2.1 General paragraph 6:
An unbounded array definition in which the element subtype indication denotes either an unconstrained composite subtype or a subtype that is not a composite subtype defines an array type and a name denoting that type. For each object that has the array type, the number of indices, the type and position of each index, and the subtype of the elements are as in the type definition. The index subtype for a given index position is, by definition, the subtype denoted by the type mark of the corresponding index subtype definition. The values of the left and right bounds of each index range are not defined, but shall belong to the corresponding index subtype; similarly, the direction of each index range is not defined. The symbol <> (called a box) in an index subtype definition stands for an undefined range (different objects of the type need not have the same bounds and direction).
There is a code example found in 5.3.2.1 (toward the end).
And if the second form looks easy to mess up, it is. You can declare objects of the same type with elements that have different subtype constraints and are incompatible when their lengths differ.
Without seeing the log file output for any synthesis operations using the original successful synthesis would not be compliant to the VHDL standard.
Without going through everything with a fine tooth comb your test case package declaration doesn't match the line numbers given in example code. It seems like you are re-declaring functions in the package body (noting the line number 310). Try removing the duplicate function declarations.
(You could also provide an actual Minimal, Complete, and Verifiable example, it'd do wonders for telling exactly what's going on).
This question already has answers here:
How to use generic parameters that depend on other generic parameters for entities?
(2 answers)
Closed 6 years ago.
I want to create a VHDL entity with a one generic that changes the width of another generic.
entity lfsr_n is
generic (
WIDTH : integer := 32; -- counter width
POLYNOMIAL : std_logic_vector (WIDTH-1 downto 0) := "1000_0000_0000_0000_0000_0000_0110_0010"
);
Unfortunately, it seems I can't reference an earlier defined generic later in the generic list. Active-HDL gives the following errors:
Error: COMP96_0300: modules/m3_test_load/lfsr_n.vhd : (26, 45): Cannot reference "WIDTH" until the interface list is complete.
Error: COMP96_0077: modules/m3_test_load/lfsr_n.vhd : (26, 66): Undefined type of expression. Expected type 'STD_LOGIC_VECTOR'.
One workaround would be to make POLYNOMIAL a port. But it properly should be a generic since since the value is constant at elaboration time. I know that if I apply a constant to the port, it will synthesize the way I want and optimize the constants values into the module, but I'd like to find someway to make it a generic. Any suggestions how to do this?
If you want the POLYNOMIAL parameter to remain a generic you can specify it as an unconstrained array. You can also dispense with the WIDTH parameter by replacing all references by POLYNOMIAL'range, POLYNOMIAL'length-1 downto 0, or POLYNOMIAL'length as needed.
entity lfsr_n is
generic (
POLYNOMIAL : std_logic_vector := X"FFAA55BB"
);
port (
-- Vector with copied range (defaults to ascending from 0)
state : out std_logic_vector(POLYNOMIAL'range);
-- Vector with forced descending range
state2 : out std_logic_vector(POLYNOMIAL'length-1 downto 0)
);
end entity;
Unconstrained arrays are a powerful feature that help simplify code by implicitly controlling widths rather than needing a dedicated generic parameter. When used effectively they reduce the number of hard-coded array sizes in your source resulting in naturally resizable logic. You can freely change the POLYNOMIAL generic to another value with a different length and the rest of your logic should adapt without any additional effort.
There's an entity declarative part following any generic and any port declaration:
library ieee;
use ieee.std_logic_1164.all;
entity lfsr_n is
generic (
WIDTH: integer := 32 -- counter width
);
port (
foo: integer
);
constant POLYNOMIAL: std_logic_vector (WIDTH-1 downto 0)
:= B"1000_0000_0000_0000_0000_0000_0110_0010";
end entity;
architecture foo of lfsr_n is
begin
end architecture;
This analyzes and elaborates showing the generic is properly used.
You could also note that the literal you assign to the std_logic_vector doesn't adapt to changing WIDTH. I would have thought these '1's stand for tap off locations and you'd expect these could change from one LFSR length to another.
You could convey the 'WIDTH' in the polynomial constant:
library ieee;
use ieee.std_logic_1164.all;
entity lfsr_n is
generic (
-- WIDTH: integer := 32; -- counter width
POLYNOMIAL: std_logic_vector :=
B"1000_0000_0000_0000_0000_0000_0110_0010"
);
port (
foo: integer
);
-- constant POLYNOMIAL: std_logic_vector (WIDTH-1 downto 0)
-- := B"1000_0000_0000_0000_0000_0000_0110_0010";
end entity;
architecture foo of lfsr_n is
signal shft_register: std_logic_vector (0 to POLYNOMIAL'LENGTH-1);
begin
end architecture;
Or:
architecture foo of lfsr_n is
-- signal shft_register: std_logic_vector (0 to POLYNOMIAL'LENGTH-1);
-- or
signal shift_register: std_logic_vector(POLYNOMIAL'RANGE);
I want to be able to specify a number of channels as a generic and use this to specify the range of an array containing further parameters. When compiling, my Aldec compile tell me that 'num_chan' cannot be referenced until the interface list is complete.
Does anyone know a way to achieve this?
ENTITY deframer IS
generic (
num_chan : integer := 2;
ch_low : int_arr(num_chan-1 downto 0) := ( 1, 189);
ch_hi : int_arr(num_chan-1 downto 0) := (127, 189));
In VHDL-2002 (and earlier), a formal generic declared within a given generic
list can't be used to declared other generics in that list, which is the
reason for the error you see.
In VHDL-2008 that is possible, so if the required tools support VHDL-2008 and
that feature ("Referencing generics in generic lists"), then you can indicate
to the tools that VHDL-2008 is used.
A solution for VHDL-2002, is to make the ch_low and ch_hi arrays large
enough to accommodate any value of num_chan, and then fill the unused with a
dummy value, like (assuming num_chan is at most 10, using -1 as dummy value):
generic(
num_chan : integer := 2;
ch_low : int_arr_t(1 to 10) := ( 1, 189, others => -1);
ch_hi : int_arr_t(1 to 10) := (127, 189, others => -1));