In a VHDL testbench, I have a function that parses a csv file in order to initialize a test array.
When running the function in the elaboration phase to set the values of a constant, the function behaves differently than when using the same function in simulation (e.g. initializing a signal during a reset)
The function code is:
constant cn_DATA_WIDTH : natural := 10;
constant cn_DATA_DEPTH : natural := 400;
type tav_data_array is array (0 to cn_DATA_DEPTH - 1) of std_logic_vector(cn_DATA_WIDTH - 1 downto 0);
function f_init_data_from_file (
s_file_path : in string;
i_column_index : in integer := 0
) return tav_data_array is
-- file parsing
file f_data_file_buf : text is in s_file_path;
variable vl_data_file_line : line;
variable vi_data_entry : integer;
variable vc_comma : character;
variable vb_good_num : boolean;
-- destination
variable vav_data_array : tav_data_array;
begin
-- Skip first line that contains header
readline (f_data_file_buf, vl_data_file_line);
for line_index in vav_data_array'range loop
readline (f_data_file_buf, vl_data_file_line);
read (vl_data_file_line, vi_data_entry, vb_good_num);
assert vb_good_num
report "Failed reading file : " & s_file_path
& ", at line = " & integer'image(line_index)
& ", at column = 0"
severity failure;
for column_index in 1 to i_column_index loop
read(vl_data_file_line, vc_comma);
read(vl_data_file_line, vi_data_entry, vb_good_num);
assert vb_good_num
report "Failed reading file : " & s_file_path
& ", at line = " & integer'image(line_index)
& ", at column = " & integer'image(column_index)
severity failure;
end loop;
vav_data_array(line_index) := std_logic_vector(to_unsigned(vi_data_entry, cn_DATA_WIDTH));
end loop;
return vav_data_array;
end function;
Function usage in elaboration:
constant cav_data : tav_data_array :=
f_init_data_from_file (
s_file_path => "my_path" ,
i_column_index => 3
);
Result: The test array is not initialized with the expected data. The data used for the initialization is column 0 instead of column 3.
Function usage in elaboration:
signal sav_data : tav_data_array;
-- ...
process(clk)
begin
if rising_edge(clk) then
if rst = '1' then
sav_data <=
f_init_data_from_file (
s_file_path => "my_path" ,
i_column_index => 3
);
else
-- ...
end if;
end if;
end process;
In this case, the data used for initialization is the proper one. Column 3 is used as expected.
Is there a reason for this different behavior? Or is this a bug with the tools I am using for simulation (Xilinx Vivado 2018.2)?
EDIT to create MRVE:
As requested, this is a minimal reproducible example. Some of the parameters
Source code:
-- MRVE
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
-- File parsing
use STD.textio.all;
use ieee.std_logic_textio.all;
entity file_bug_mrve is
end file_bug_mrve;
architecture a_file_bug_mrve of file_bug_mrve is
-- Actual data are 12 bits
constant cn_DATA_WIDTH : natural := 12;
constant cn_DATA_DEPTH : natural := 4; -- testbench is planned for 400 high priority cycles
type tav_data_array is array (0 to cn_DATA_DEPTH - 1) of std_logic_vector(cn_DATA_WIDTH - 1 downto 0);
-- Parse csv data file to retrieve all entries at specified columns
function f_init_data_from_file (
s_file_path : in string;
i_column_index : in integer := 0
) return tav_data_array is
-- file parsing
file f_data_file_buf : text is in s_file_path;
variable vl_data_file_line : line;
variable vi_data_entry : integer;
variable vc_comma : character;
variable vb_good_num : boolean;
-- destination
variable vav_data_array : tav_data_array;
begin
-- Skip first line
readline (f_data_file_buf, vl_data_file_line);
for line_index in vav_data_array'range loop
readline (f_data_file_buf, vl_data_file_line);
read (vl_data_file_line, vi_data_entry, vb_good_num);
assert vb_good_num
report "Failed reading file : " & s_file_path
& ", at line = " & integer'image(line_index)
& ", at column = 0"
severity failure;
for column_index in 1 to i_column_index loop
read(vl_data_file_line, vc_comma);
read(vl_data_file_line, vi_data_entry, vb_good_num);
assert vb_good_num
report "Failed reading file : " & s_file_path
& ", at line = " & integer'image(line_index)
& ", at column = " & integer'image(column_index)
severity failure;
end loop;
vav_data_array(line_index) := std_logic_vector(to_unsigned(vi_data_entry, cn_DATA_WIDTH));
end loop;
return vav_data_array;
end function;
constant cs_RSP_DATA_IN_FILE_PATH : string := "../../src/xls/file_bug_mrve.csv";
constant cn_RSP_DATA_IN_COLUMN_INDEX : natural := 3;
-- registers emulation
-- Setting init data to be processed in elaboration does not work
-- Data will be initialized with first column instead of third
signal sav_data : tav_data_array :=
f_init_data_from_file (
s_file_path => cs_RSP_DATA_IN_FILE_PATH ,
i_column_index => cn_RSP_DATA_IN_COLUMN_INDEX
);
-- infa Simulation
constant ct_CLOCK_PERIOD_100 : time := 10 ns;
constant ct_TEST_COMPLETION_TIME : time := ct_CLOCK_PERIOD_100 * 10;
signal s_clk : std_logic;
signal s_rst : std_logic;
signal sb_end : boolean := false;
begin
-- -----------------------------------------------------------------------------------------------------------------
-- Clock process definitions
-- -----------------------------------------------------------------------------------------------------------------
p_Clock_100 : process
begin
s_clk <= '0';
wait for ct_CLOCK_PERIOD_100/2;
s_clk <= '1';
wait for ct_CLOCK_PERIOD_100/2;
if sb_end then
wait;
end if;
end process;
-- -----------------------------------------------------------------------------------------------------------------
-- Main Stimulus process
-- -----------------------------------------------------------------------------------------------------------------
p_stimulous : process
begin
--
s_rst <= '0';
-- 1 clock cycle reset to avoid loading file multiple times
wait until rising_edge(s_clk);
s_rst <= '1';
wait until rising_edge(s_clk);
s_rst <= '0';
wait for ct_TEST_COMPLETION_TIME;
sb_end <= true;
end process;
p_main : process(s_clk)
begin
if rising_edge(s_clk) then
if s_rst = '1' then
-- Setting init data to be processed in simulation works
-- Data will be initialized with forth column as expected
sav_data <=
f_init_data_from_file (
s_file_path => cs_RSP_DATA_IN_FILE_PATH ,
i_column_index => cn_RSP_DATA_IN_COLUMN_INDEX
);
else
-- Nothing to do in MRVE
end if;
end if;
end process p_main;
end a_file_bug_mrve;
CSV file:
Ch0,Ch1,Ch2,Ch3
1,5,9,13
2,6,10,14
3,7,11,15
4,8,12,16
Related
In my architecture I define a signal of some record type. Each of the record elements is driven in one process only.
The whole record signal is passed to procedures. The procedures will later be placed in a package. Passing the record as type "inout" leeds to all accessed record elements resolving to 'X'.
The code runs in Modelsim and is only for verification (no synthesis needed).
Driving the record elements directly works.
Minimal example; Architecture header:
-- Record type
type tr_Data is record
r1_A : std_logic;
r1_B : std_logic;
end record;
-- Signal definition and initialization
signal sr_Data : tr_Data := (
r1_A => '0',
r1_B => '1'
);
Architecture body:
-- This process only modifies sr_Data.r1_A
prcss_A: process
procedure proc_Modify_A(signal d: inout tr_Data) is
begin
d.r1_A <= not d.r1_A;
end procedure;
begin
wait for 1 us;
--sr_Data.r1_A <= not sr_Data.r1_A; -- This works
proc_Modify_A(sr_Data); -- This works not
wait for 1 us;
wait;
end process;
-- This process only modifies sr_Data.r1_B
prcss_B: process
procedure proc_Modify_B(signal d: inout tr_Data) is
begin
d.r1_B <= not d.r1_B;
end procedure;
begin
wait for 1.5 us;
--sr_Data.r1_B <= not sr_Data.r1_B; -- This works
proc_Modify_B(sr_Data); -- This works not
wait for 1 us;
wait;
end process;
This is the Result in Modelsim:
I'm not sure, if this is possible at all. Maybe there are also better solutions to my problem.
Thank's for the help!
This question demonstrates why providing a minimal, complete, and verifiable exampe is desirable for code errors (instead of snippets as here).
Creating a MCVe:
library ieee;
use ieee.std_logic_1164.all;
entity record_process is
end entity;
architecture foo of record_process is
type tr_Data is record
r1_A: std_logic;
r1_B: std_logic;
end record;
signal sr_Data: tr_Data := (r1_A => '0', r1_B => '1');
begin
prcss_A: -- This process only modifies sr_Data.r1_A
process
procedure proc_Modify_A(signal d: inout tr_Data) is
begin
d.r1_A <= not d.r1_A;
end procedure;
begin
wait for 1 us;
-- sr_Data.r1_A <= not sr_Data.r1_A; -- This works
proc_Modify_A(sr_Data); -- This works not
wait for 1 us;
wait;
end process;
prcss_B: -- This process only modifies sr_Data.r1_B
process
procedure proc_Modify_B(signal d: inout tr_Data) is
begin
d.r1_B <= not d.r1_B;
end procedure;
begin
wait for 1.5 us;
--sr_Data.r1_B <= not sr_Data.r1_B; -- This works
proc_Modify_B(sr_Data); -- This works not
wait for 1 us;
wait;
end process;
MONITOR:
process (sr_Data)
begin
report
LF & HT &
"sr_Data.r1_A = " & std_logic'image(sr_Data.r1_A) &
LF & HT &
"sr_Data.r1_B = " & std_logic'image(sr_Data.r1_B);
end process;
end architecture;
we see reported behavior that matches the OP's waveform:
record_process.vhdl:44:9:#0ms:(report note):
sr_Data.r1_A = '0'
sr_Data.r1_B = '1'
record_process.vhdl:44:9:#1us:(report note):
sr_Data.r1_A = 'X'
sr_Data.r1_B = '1'
record_process.vhdl:44:9:#1500ns:(report note):
sr_Data.r1_A = 'X'
sr_Data.r1_B = 'X'
The 'X's are caused by the way drivers are associated with subprogram actual parameters:
IEEE Std 1076-2008
4.2 Subprogram declarations
4.2.2.3 Signal parameters
A process statement contains a driver for each actual signal associated with a formal signal parameter of mode out or inout in a subprogram call. Similarly, a subprogram contains a driver for each formal signal parameter of mode out or inout declared in its subprogram specification.
For a signal parameter of mode inout or out, the driver of an actual signal is associated with the corresponding driver of the formal signal parameter at the start of each call. Thereafter, during the execution of the subprogram body, an assignment to the driver of a formal signal parameter is equivalent to an assignment to the driver of the actual signal.
10.7 Procedure call statement
For each formal parameter of a procedure, a procedure call shall specify exactly one corresponding actual parameter. This actual parameter is specified either explicitly, by an association element (other than the actual open) in the association list or, in the absence of such an association element, by a default expression (see 6.5.2).
There's a driver for the actual associated in whole, here a signal of a record type.
(This also tells you Tricky's answer is valid, which reduces the actual to an element of the whole.)
There's another possible solution
Drop the parameter list:
architecture fum of record_process is
type tr_Data is record
r1_A: std_logic;
r1_B: std_logic;
end record;
signal sr_Data: tr_Data := (r1_A => '0', r1_B => '1');
begin
prcss_A: -- This process only modifies sr_Data.r1_A
process
procedure proc_Modify_A is
begin
sr_Data.r1_A <= not sr_Data.r1_A;
end procedure;
begin
wait for 1 us;
-- sr_Data.r1_A <= not sr_Data.r1_A; -- This works
proc_Modify_A; -- This works not
wait for 1 us;
wait;
end process;
prcss_B: -- This process only modifies sr_Data.r1_B
process
procedure proc_Modify_B is
begin
sr_Data.r1_B <= not sr_Data.r1_B;
end procedure;
begin
wait for 1.5 us;
--sr_Data.r1_B <= not sr_Data.r1_B; -- This works
proc_Modify_B; -- This works not
wait for 1 us;
wait;
end process;
MONITOR:
process (sr_Data)
begin
report
LF & HT &
"sr_Data.r1_A = " & std_logic'image(sr_Data.r1_A) &
LF & HT &
"sr_Data.r1_B = " & std_logic'image(sr_Data.r1_B);
end process;
end architecture;
which produces:
record_process.vhdl:89:9:#0ms:(report note):
sr_Data.r1_A = '0'
sr_Data.r1_B = '1'
record_process.vhdl:89:9:#1us:(report note):
sr_Data.r1_A = '1'
sr_Data.r1_B = '1'
record_process.vhdl:89:9:#1500ns:(report note):
sr_Data.r1_A = '1'
sr_Data.r1_B = '0'
without any driver conflicts, lacking multiple drivers (14.7.2 Drivers) requiring resolution (14.7.3.4 Signal update) during simulation.
14.7.2 Drivers
Every signal assignment statement in a process statement defines a set of drivers for certain scalar signals. There is a single driver for a given scalar signal S in a process statement, provided that there is at least one signal assignment statement in that process statement and that the longest static prefix of the target signal of that signal assignment statement denotes S or denotes a composite signal of which S is a subelement. Each such signal assignment statement is said to be associated with that driver. Execution of a signal assignment statement affects only the associated driver(s).
The longest static prefix is defined in 8. Names:
8.1 General
A static signal name is a static name that denotes a signal. The longest static prefix of a signal name is the name itself, if the name is a static signal name; otherwise, it is the longest prefix of the name that is a static signal name. ...
where the longest static prefix now includes record elements as opposed to actual signals of a record type associated in whole.
This is possible because a subprogram is a sequence of statements (4.2 Subprogram bodies) and the procedures are declared within the scope of the declaration of sr_Data (12.1 Declarative region, 12.2 Scope of declarations, 12.3 Visibility).
The error is here because the procedures take in a record type. When this happens, they create a driver on the entire record, not just the elements driven internally. Hence you have multiple drivers because the same signal is driven from multiple processes.
If you want to drive only the elements, make the procedures take an inout of std_logic type and only pass in the signal record elements. This way, each process is only driving a single element of the record.
-- This process only modifies sr_Data.r1_A
prcss_A: process
procedure proc_Modify_A(signal d: inout std_logic) is
begin
d <= not d;
end procedure;
begin
wait for 1 us;
--sr_Data.r1_A <= not sr_Data.r1_A; -- This works
proc_Modify_A(sr_Data.r1_A); -- This works not
wait for 1 us;
wait;
end process;
-- This process only modifies sr_Data.r1_B
prcss_B: process
procedure proc_Modify_B(signal d: inout std_logic) is
begin
d <= not d;
end procedure;
begin
wait for 1.5 us;
--sr_Data.r1_B <= not sr_Data.r1_B; -- This works
proc_Modify_B(sr_Data.r1_B); -- This works not
wait for 1 us;
wait;
end process;
No time now for an explanation but the following solution should achieve exactly what you are asking for:
Architecture header:
-- Record type
type tr_Data is record
r1_A : std_logic;
r1_B : std_logic;
end record;
constant TR_DATA_INIT : tr_Data := (
r1_A => 'Z',
r1_B => 'Z'
);
-- Signal definition and initialization
signal sr_Data : tr_Data := (
r1_A => '0',
r1_B => '1'
);
Architecture body:
-- This process only modifies sr_Data.r1_A
prcss_A: process
procedure proc_keep_A(signal d: inout tr_Data) is
begin
d <= TR_DATA_INIT;
d.r1_A <= d.r1_A;
end procedure;
procedure proc_Modify_A(signal d: inout tr_Data) is
begin
d.r1_A <= not d.r1_A;
end procedure;
begin
proc_keep_A(sr_Data); -- needed only at the very beginning of the process
wait for 1 us;
--sr_Data.r1_A <= not sr_Data.r1_A; -- This works
proc_Modify_A(sr_Data); -- This works too
wait for 1 us;
wait;
end process;
-- This process only modifies sr_Data.r1_B
prcss_B: process
procedure proc_keep_B(signal d: inout tr_Data) is
begin
d <= TR_DATA_INIT;
d.r1_B <= d.r1_B;
end procedure;
procedure proc_Modify_B(signal d: inout tr_Data) is
begin
d.r1_B <= not d.r1_B;
end procedure;
begin
proc_keep_B(sr_Data); -- needed only at the very beginning of the process
wait for 1.5 us;
--sr_Data.r1_B <= not sr_Data.r1_B; -- This works
proc_Modify_B(sr_Data); -- This works too
wait for 1 us;
wait;
end process;
Thanks for all the detailed informations. I learned a lot through that.
Based on your answers I found a solution. For my purpose, it is to use a resolved type and init it at the beginning of each process as Giampietro Seu suggested. This also enables me to put non-resolved types like integer, time etc. in the record.
Here is the full reproducable minimal example.
Entity:
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use work.pkg_Minimal.all;
entity eMinimalExample is
end entity;
architecture aMinimal of eMinimalExample is
-- Signal of resolved subtype
signal sr_Data : rtr_Data;
begin
-- This process only modifies sr_Data.r1_A / ri_A
prcss_A: process
begin
proc_Init_A(sr_Data); -- Only needed once
wait for 1 us;
proc_Modify_A(sr_Data);
wait for 1 us;
wait;
end process;
-- This process only modifies sr_Data.r1_B / ri_B
prcss_B: process
begin
proc_Init_B(sr_Data); -- Only needed once
wait for 1.5 us;
proc_Modify_B(sr_Data);
wait for 1 us;
wait;
end process;
prcss_Monitor: process (sr_Data)
begin
report
LF & HT &
"sr_Data.r1_A = " & std_logic'image(sr_Data.r1_A) &
LF & HT &
"sr_Data.r1_B = " & std_logic'image(sr_Data.r1_B) &
LF & HT &
"sr_Data.ri_A = " & integer'image(sr_Data.ri_A) &
LF & HT &
"sr_Data.ri_B = " & integer'image(sr_Data.ri_B);
end process;
end architecture;
Package:
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
package pkg_Minimal is
-- Record type
type tr_Data is record
r1_A : std_logic;
ri_A : integer;
r1_B : std_logic;
ri_B : integer;
end record;
-- Array of record
type ta_Data is array(natural range <>) of tr_Data;
-- Resolution function for record
function f_resolve_Data (ca_Data: in ta_Data) return tr_Data;
-- Resolved record subtype
subtype rtr_Data is f_resolve_Data tr_Data;
-- Init A with init values and B with recessive values
procedure proc_Init_A (signal d: inout rtr_Data);
-- Init B with init values and A with recessive values
procedure proc_Init_B (signal d: inout rtr_Data);
-- Modify A only
procedure proc_Modify_A (signal d: inout rtr_Data);
-- Modify B only
procedure proc_Modify_B (signal d: inout rtr_Data);
end package;
package body pkg_Minimal is
-- Record init values
constant cr_Data_Init : tr_Data := (
r1_A => '0',
ri_A => 10,
r1_B => '1',
ri_B => 20
);
-- Recessive values for resolution function
constant cr_Data_Recessive : tr_Data := (
r1_A => 'Z',
ri_A => integer'low, -- Lowest value is burned,
r1_B => 'Z', -- we can't use it anymore
ri_B => integer'low
);
-- Resolution function for record
function f_resolve_Data (constant ca_Data: in ta_Data) return tr_Data is
variable vr_Data : tr_Data := cr_Data_Recessive;
begin
for i in ca_Data'range loop
if cr_Data_Recessive.r1_A /= ca_Data(i).r1_A then
vr_Data.r1_A := ca_Data(i).r1_A;
end if;
if cr_Data_Recessive.r1_B /= ca_Data(i).r1_B then
vr_Data.r1_B := ca_Data(i).r1_B;
end if;
if cr_Data_Recessive.ri_A /= ca_Data(i).ri_A then
vr_Data.ri_A := ca_Data(i).ri_A;
end if;
if cr_Data_Recessive.ri_B /= ca_Data(i).ri_B then
vr_Data.ri_B := ca_Data(i).ri_B;
end if;
end loop;
return vr_Data;
end function;
procedure proc_Init_A(signal d: inout rtr_Data) is
begin
d <= cr_Data_Recessive;
d.r1_A <= cr_Data_Init.r1_A;
d.ri_A <= cr_Data_Init.ri_A;
end procedure;
procedure proc_Init_B(signal d: inout rtr_Data) is
begin
d <= cr_Data_Recessive;
d.r1_B <= cr_Data_Init.r1_B;
d.ri_B <= cr_Data_Init.ri_B;
end procedure;
procedure proc_Modify_A(signal d: inout rtr_Data) is
begin
d.r1_A <= not d.r1_A;
d.ri_A <= d.ri_A + 1;
end procedure;
procedure proc_Modify_B(signal d: inout rtr_Data) is
begin
d.r1_B <= not d.r1_B;
d.ri_B <= d.ri_B + 1;
end procedure;
end package body;
Output:
** Note:
sr_Data.r1_A = '0'
sr_Data.r1_B = '1'
sr_Data.ri_A = 10
sr_Data.ri_B = 20
Time: 0 ps Iteration: 0 Instance: /eminimalexample
** Note:
sr_Data.r1_A = '1'
sr_Data.r1_B = '1'
sr_Data.ri_A = 11
sr_Data.ri_B = 20
Time: 1 us Iteration: 1 Instance: /eminimalexample
** Note:
sr_Data.r1_A = '1'
sr_Data.r1_B = '0'
sr_Data.ri_A = 11
sr_Data.ri_B = 21
Time: 1500 ns Iteration: 1 Instance: /eminimalexample
I am fairly new to VHDL and I am running some snippets from a code I was given to see what it is doing. There is a custom array type I want to see in the console, but I get and error when I try to write it.
entity hello_world is
end entity hello_world;
library STD;
library IEEE;
use IEEE.std_logic_1164.all;
use STD.textio.all;
use IEEE.std_logic_textio.all;
use IEEE.numeric_std.all;
architecture test of hello_world is
type row_type is array(0 to 2) of std_logic_vector(3 downto 0);
type new_type is array(0 to 1) of row_type;
signal max_new : new_type := (others => (others => (others => '0')));
begin
my_print : process is
variable my_line : line;
begin
write(my_line, string'("Value of max_new"));
write(my_line, max_new);
writeline(output, my_line);
wait;
end process my_print;
end architecture test;
The error I get while running the simulation is:
Error: type error near 'max_new': expected type 'std_ulogic'. Error: formal 'l' of mode inout must have an associated actual. Error: formal 'value' has no actual or default value. Error: indexed name prefix type 'void' is not an array type
If I understood correctly, row type is an array of size 3, in each position I have a vector made of 4 bits. new_type is an array of size 2, in each position I have a row_type, which is an array of size 3 with a 4 bits vector in each position. Is this correct? Since it is initialized to 0, I expect to see only that.
I am using Vivado 2018.3 for the simulation.
Any help would be highly appreciated!
You could also author your own write procedure:
entity hello_world is
end entity hello_world;
-- library STD;
library IEEE;
use IEEE.std_logic_1164.all;
use STD.textio.all;
use IEEE.std_logic_textio.all;
-- use IEEE.numeric_std.all;
architecture test of hello_world is
type row_type is array(0 to 2) of std_logic_vector(3 downto 0);
type new_type is array(0 to 1) of row_type;
signal max_new : new_type := (others => (others => (others => '0')));
procedure write (l: inout line; new_type_val: in new_type) is
begin
write (l, string'("("));
for i in new_type'range loop
write (l, string'("("));
for j in row_type'range loop
write (l, string'(""""));
write(l, new_type_val(i)(j));
write (l, string'(""""));
if j /= row_type'right then
write(l, string'(","));
end if;
end loop;
write (l, string'(")"));
if i /= new_type'right then
write(l, string'(","));
end if;
end loop;
write (l, string'(")"));
end procedure;
begin
my_print:
process is
variable my_line: line;
begin
write(my_line, string'("Value of max_new = "));
write(my_line, max_new);
writeline(output, my_line);
wait;
end process my_print;
end architecture test;
ghdl -r hello_world
Value of max_new = (("0000","0000","0000"),("0000","0000","0000"))
using preexisting write procedure overloads as building blocks.
You can also use type specific conversions to strings eliminating the dependence on Synopsys package std_logic_textio as well as introducing the ability to use report statements:
library ieee;
use ieee.std_logic_1164.all;
use std.textio.all;
architecture no_std_logic_textio of hello_world is
type row_type is array(0 to 2) of std_logic_vector(3 downto 0);
type new_type is array(0 to 1) of row_type;
signal max_new : new_type := (others => (others => (others => '0')));
-- For VHDL version < -2008:
function to_string (inp: std_logic_vector) return string is
variable image_str: string (1 to inp'length);
alias input_str: std_logic_vector (1 to inp'length) is inp;
begin
for i in input_str'range loop
image_str(i) := character'VALUE(std_ulogic'IMAGE(input_str(i)));
end loop;
return image_str;
end function;
function to_string (nt: new_type) return string is
variable l: line;
begin
write (l, string'("("));
for i in new_type'range loop
write (l, string'("("));
for j in row_type'range loop
write (l, '"' & to_string(nt(i)(j)) & '"');
if j /= row_type'right then
write(l, string'(","));
end if;
end loop;
write (l, string'(")"));
if i /= new_type'right then
write(l, string'(","));
end if;
end loop;
write (l, string'(")"));
return l.all;
end function;
begin
my_print:
process is
variable my_line: line;
begin
write (my_line, string'("value of max_new = "));
write(my_line, to_string(max_new));
writeline(output, my_line);
report LF & HT & "max_new = " & to_string(max_new);
wait;
end process my_print;
end architecture no_std_logic_textio;
And this demonstrates both writing to output and a report statement:
ghdl -r hello_world
value of max_new = (("0000","0000","0000"),("0000","0000","0000"))
hello_world.vhdl:95:9:#0ms:(report note):
max_new = (("0000","0000","0000"),("0000","0000","0000"))
The function write of std.textio can take the following arguments as value (https://www.hdlworks.com/hdl_corner/vhdl_ref/VHDLContents/TEXTIOPackage.htm) :
bit
bit_vector
boolean
character
integer
real
string
time
IEEE.std_logic_textio add std_logic and his derivated to this list but Array is not handled by write.
You can print your array like that :
my_print : process is
variable my_line : line;
begin
write(my_line, string'("Value of max_new"));
for I in 0 to 1 loop
for J in 0 to 2 loop
write(my_line, max_new(I)(J));
end loop;
end loop;
writeline(output, my_line);
wait;
end process my_print;
I am trying to read a matrix from a file, it contains the colors of the pixels of a gray scale image. After this I will have to put the data I read into a register and perform some operations on it. When I am trying to simulate my code, the data is read alright in the "data" signal, but when I try to store an element, I read in the "test" signal to show it in the "last_elem" output it shows -2147483648. What am I doing wrong and why can't I access the data I read?
entity Main is
Port(
rstb:in std_logic;
clk:in std_logic;
row:out integer;
col:out integer;
last_elem:out integer
);
end Main;
architecture Behavioral of Main is
type matrixData is array(0 to 240, 0 to 217) of integer;
signal data:matrixData;
signal test:integer;
begin
process(clk)
file read_file:text;
VARIABLE file_line:LINE;
VARIABLE row_counter:INTEGER:=0;
variable rows:integer;
variable cols:integer;
variable read_value:integer;
begin
file_open(read_file, "D:\Faculta\An 3 Sem 2\SCS\image.txt", read_mode);
readline(read_file,file_line);
read(file_line, rows);
read(file_line, cols);
row<=rows;
col<=cols;
for i in 0 to rows-1 loop
readline(read_file,file_line);
for j in 0 to cols-1 loop
read(file_line, read_value);
data(i,j)<=read_value;
end loop;
end loop;
test<=data(0,0);
last_elem<=test;
file_close(read_file);
end process;
end Behavioral;
Providing a Minimal, Complete, and Verifiable example for your code:
library ieee;
use ieee.std_logic_1164.all;
use std.textio.all;
entity main is
port (
rstb: in std_logic;
clk: in std_logic;
row: out integer;
col: out integer;
last_elem: out integer
);
end entity main;
architecture behavioral of main is
-- type matrixdata is array (0 to 240, 0 to 217) of integer;
type matrixdata is array (0 to 3, 0 to 2) of integer;
signal data: matrixdata;
signal test: integer;
begin
process (clk)
file read_file: text;
variable file_line: line;
variable row_counter: integer := 0;
variable rows: integer;
variable cols: integer;
variable read_value: integer;
begin
--file_open(read_file, "d:\faculta\an 3 sem 2\scs\image.txt", read_mode);
file_open (read_file, "image.txt", read_mode);
readline (read_file, file_line);
read (file_line, rows);
read (file_line, cols);
row <= rows;
col <= cols;
for i in 0 to rows - 1 loop
readline (read_file, file_line);
for j in 0 to cols - 1 loop
read(file_line, read_value);
data(i,j) <= read_value;
end loop;
end loop;
test <= data(0, 0);
last_elem <= test;
file_close(read_file);
end process;
end behavioral;
library ieee;
use ieee.std_logic_1164.all;
entity main_tb is
end entity;
architecture foo of main_tb is
component main is
port (
rstb: in std_logic;
clk: in std_logic;
row: out integer;
col: out integer;
last_elem: out integer
);
end component main;
signal rstb: std_logic := '1';
signal clk: std_logic := '0';
signal row: integer;
signal col: integer;
signal last_elem: integer;
begin
DUT:
main
port map (
rstb => rstb,
clk => clk,
row => row,
col => col,
last_elem => last_elem
);
WAVEFORM_DISPLAY_STIMULUS:
process
begin
wait for 0 ns;
wait for 1 ns;
wait;
end process;
end architecture;
Modified for a smaller two dimensional matrixdata type and supplied with a an input file:
images.txt
4 3
0 0 0
1 1 1
2 2 2
3 3 3
We see:
Where the matrix values appear to be read in correctly. The last_elem signal has the default value (INTEGER'LOW, -2147483648) as does test, both declarations lacking an initial value.
Neither Data nor test are in the process sensitivity list assignments will not occur in the same simulation cycle an assignment is scheduled. Every process executes at least once.
There's also an issue with the process sensitivity list. The process will execute for each event on clk, repetitively opening the file, reading in data and closing the file.
The proper fix for these two issues would be to remove the process sensitivity list and add a final wait statement (wait;) after the file_close. Also add a wait statement with zero incremental simulation time (e.g. wait for 0 ns;) prior to each signal assignment depending on a previous signal assignment value in the process.
There's also the issue of error detection during TEXTIO operations (file_open, read_line). You've gone to all the trouble to close the file. How about checking for a successful file open and ENDFILE before executing procedure read_line?
process
file read_file: text;
variable file_line: line;
variable row_counter: integer := 0;
variable rows: integer;
variable cols: integer;
variable read_value: integer;
variable open_status: FILE_OPEN_STATUS; -- ADDED
begin
--file_open(read_file, "d:\faculta\an 3 sem 2\scs\image.txt", read_mode);
-- file_open (read_file, "image.txt", read_mode);
file_open (open_status, read_file, "image.txt", read_mode);
case open_status is -- ADDED
when OPEN_OK =>
report "read_file opened for read" severity NOTE;
when STATUS_ERROR =>
report "read_file already open" severity FAILURE;
when NAME_ERROR =>
report "read_file file name not found" severity FAILURE;
when MODE_ERROR =>
report "read_file can't be opened for read" severity FAILURE;
end case;
if endfile(read_file) then -- ADDED
report "can't read first line from read_file"
severity FAILURE;
end if;
readline (read_file, file_line);
read (file_line, rows);
read (file_line, cols);
row <= rows;
col <= cols;
for i in 0 to rows - 1 loop
if endfile(read_file) then -- ADDED
report "can't read line for all rows from read_file"
severity FAILURE;
end if;
readline (read_file, file_line);
for j in 0 to cols - 1 loop
read(file_line, read_value);
data(i,j) <= read_value;
end loop;
end loop;
wait for 0 ns; -- ADDED causes a delta cycle, data is updated
test <= data(0, 0);
wait for 0 ns; -- ADDED causes a delta cycle, test is updated
last_elem <= test;
file_close(read_file);
wait; -- ADDED
end process;
This loads the array once at initialization and last_elem value will be updated after successive delta simulation cycles.
A wait statement causes an executing process to suspend until a condition is met. A wait statement without a condition assumes a timeout clause of TIME'HIGH.
A wait statement with a timeout clause (here 0 ns for the first two) will suspend the process until the condition is met. The incremental delay is equivalent to the current simulation time plus the time expression.
Because there's a further event scheduled for the current simulation time, a delta simulation cycle will occur in which the process resumes. At the beginning of each simulation cycle projected output waveform values scheduled for the current simulation time cause signal updates where the effective value of the signal can be evaluated (as in an assignment statement). This will occur for both of the first two wait statements.
Hitting the final wait statement without a timeout clause assume a timeout clause of TIME'HIGH, the process won't resume again during simulation.
You can prove the open_status and endfile tests work by manipulating the host file system (path and permissions) and image.txt file contents.
With an additional change to the testbench:
CLOCK:
process
begin
wait for 5 ns;
clk <= not clk;
if now > 40 ns then
wait;
end if;
end process;
-- WAVEFORM_DISPLAY_STIMULUS:
-- process
-- begin
-- wait for 0 ns;
-- wait for 1 ns;
-- wait;
-- end process;
You can demonstrate the code appears functional through simulation:
When simulating you'll only start the process at the first line once, only resume twice waiting on signal updates and only get one message from
report "read_file opened for read" severity NOTE;
when evaluating open_status.
As Tricky points out in a comment test is assigned the value of Data(0, 0) which is then assigned to last_element and actually contains the first element of the matrix.
Say it was for a FSM and I wanted to print out the current state every clock cycle. How would I go about doing so? What datatype would I need?
Yes, waveforms in e.g. ModelSim can show string, and that can be very handy. A quick example below:
architecture sim of tb is
signal info : string(1 to 20);
function string_fill(msg : string; len : natural) return string is
variable res_v : string(1 to len);
begin
res_v := (others => ' '); -- Fill with spaces to blank all for a start
res_v(1 to msg'length) := msg;
return res_v;
end function;
begin
process is
begin
info <= string_fill("Hello VHDL", info'length);
wait for 100 ns;
info <= string_fill("Hi Bren", info'length);
wait for 100 ns;
info <= string_fill("end of sim", info'length);
wait for 100 ns;
wait;
end process;
end architecture;
Which shows:
Note that string manipulation can be a little tricky in VHDL, but if you just need some for simple debug output, then the above may do it.
I am using modelsim. I wrote simple code but i am getting error.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity clk_counter is
port(output : out bit;
clk : in bit
);
end clk_counter;
architecture rtl of clk_counter_arch is
signal clock_counter_output_flag: bit;
constant clock_max_count : integer := 20000;
begin
process (clock_counter_output_flag, clk,CLK'event )
variable clock_count : integer := 0;
--constant clock_max_count : integer := 20000;
variable clock_out : bit := 0;
-- wait until CLK'event and CLK='1';
begin
if (CLK'event and CLK='1') then
clock_count := clock_count+1;
if (clock_count = clock_max_count) then
clock_out := 1;
else
clock_out := 0;
end if
end if
clock_counter_output_flag <= clock_out;
end process;
END Architecture;
Error messege:
# ** Error: (vcom-11) Could not find work.clk_counter_arch.
#
# ** Error: C:/Modeltech_pe_edu_10.4a/examples/work/src/clk_counter(13): VHDL Compiler exiting
Your entity name is clk_counter and you have defined architecture rtl of clk_counter_arch. Hence, you are getting error. Change clk_counter_arch to clk_counter.
Second, you should end architecture as end rtl.
Also, why are you using two additional variables clock_out and clock_counter_output_flag ? If you want that values as output of your code, you should simply write
if (CLK'event and CLK='1') then
clock_count := clock_count+1;
if (clock_count = clock_max_count) then
output<='1';
else
output <='0';
end if;
end if;