Reputation: 946
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:
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.
Upvotes: 2
Views: 1654
Reputation: 946
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):
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;
Upvotes: 1