Driving record elements through procedures from different processes in VHDL - vhdl

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

Related

emulating systemverilog interfaces in VHDL using record types?

I've seen people using bidirectional buses of record types to emulate the SystemVerilog Interface language structure by driving either an input or output on each field of the record.... I'm just curious why my attempt to do the same things doesn't work? Any ideas how to do this correctly?
--Attemping to emulate SystemVerilog Interfaces using VHDL Record....
----------------------------------------------
-- DUT
----------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
entity dut is
port(
i1 : in std_logic;
i2 : in std_logic;
o1 : out std_logic;
o2 : out std_logic
);
end entity;
architecture beh of dut is
begin
o1 <= '1';
o2 <= '0';
end architecture;
----------------------------------------------
-- TESTBENCH
----------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_textio.all;
use std.textio.all;
entity testbench is
end entity;
architecture beh of testbench is
type sverilog_interface_t is record
field0 : std_logic;
field1 : std_logic;
field2 : std_logic;
field3 : std_logic;
end record;
signal sverilog_interface : sverilog_interface_t;
procedure show_bus(
signal sverilog_interface :inout sverilog_interface_t
) is
variable m :line;
begin
write(m, string'("task0"));
write(m, string'(" x0:")); write(m, sverilog_interface.field0);
write(m, string'(" x1:")); write(m, sverilog_interface.field1);
write(m, string'(" x2:")); write(m, sverilog_interface.field2);
write(m, string'(" x3:")); write(m, sverilog_interface.field3);
writeline(output, m);
end procedure;
procedure task0(
signal sverilog_interface :inout sverilog_interface_t
) is
begin
sverilog_interface.field0 <= '0';
sverilog_interface.field1 <= '1';
wait for 1 ns;
end procedure;
procedure task1(
signal sverilog_interface :inout sverilog_interface_t
) is
begin
sverilog_interface.field0 <= '1';
sverilog_interface.field1 <= '0';
wait for 1 ns;
end procedure;
begin
dut1: entity work.dut(beh)
port map(
i1 => sverilog_interface.field0,
i2 => sverilog_interface.field1,
o1 => sverilog_interface.field2, -- WHy 'U'? should be '0' or '1?
o2 => sverilog_interface.field3 -- WHy 'U'? should be '0' or '1?
);
process begin
wait for 1 ns;
show_bus(sverilog_interface);
task0(sverilog_interface);
show_bus(sverilog_interface);
task1(sverilog_interface);
show_bus(sverilog_interface);
report "end of testcase" severity failure;
end process;
end architecture;
F:\Xilinx\Vivado\2021.2\bin\xvhdl.bat --incr --relax --2008 -f test1.vhd
F:\Xilinx\Vivado\2021.2\bin\xelab.bat testbench -snapshot simout -incr -debug all
F:\Xilinx\Vivado\2021.2\bin\xsim.bat simout --tclbatch simout.sim.tcl --onerror quit --onfinish quit --ieeewarnings
****** xsim v2021.2 (64-bit)
**** SW Build 3367213 on Tue Oct 19 02:48:09 MDT 2021
**** IP Build 3369179 on Thu Oct 21 08:25:16 MDT 2021
** Copyright 1986-2021 Xilinx, Inc. All Rights Reserved.
## run all
task0 x0:U x1:U x2:U x3:U
task0 x0:0 x1:1 x2:U x3:U <-- WHy are x1 and x2 'U'?
task0 x0:1 x1:0 x2:U x3:U <-- WHy are x1 and x2 'U'?
Failure: end of testcase
Time: 3 ns Iteration: 0 Process: /testbench/line__88 File: test1.vhd
$finish called at time : 3 ns : File "test1.vhd" Line 99
exit 0
INFO: [Common 17-206] Exiting xsim at Tue Feb 15 16:02:17 2022...
code:(0)
ok
There are 2 drivers for the fields of your sverilog_interface: the DUT and the testbench process.
The DUT drives only the two last fields (because the mode of the others is in), to '1' and '0', respectively.
The process drives all fields because the sverilog_interface signal is associated with the inout interface declaration of your 3 procedure calls. LRM 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.
The four fields are first driven to 'U', the leftmost value of the std_logic enumerated type, which is used as a default value for uninitialized std_logic signals. The two first fields are later driven to '0' or '1' values. But not the two last fields that are still driven to 'U'.
The resolution function attached to the std_logic type resolves the conflict between the DUT ('0' and '1') and the process ('U' and 'U') as 'U'.
One solution could be to explicitly drive the two last fields to 'Z' (high impedance) in your process:
process begin
sverilog_interface.field2 <= 'Z';
sverilog_interface.field3 <= 'Z';
wait for 1 ns;
...
Value 'Z', when in conflict with anything (except '-'), resolves as the anything. Or to do this in your procedures. Or, as some do (see OSVVM), to define your own resolution function and your own resolved subtype of std_ulogic such that 'U' and anything else resolves as the anything else.
Note: your show_bus procedure does not need mode inout, better use in. And your task0, task1 procedure do not need mode inout, better use out (principle of least privilege). But these modifications alone will not solve your problem: the process is still driving the 4 fields of the sverilog_interface signal.
Note: as noted by #user16145658 another solution would be to modify the default value of sverilog_interface at declaration:
signal sverilog_interface : sverilog_interface_t := ('U', 'U', 'Z', 'Z');
The process would then drive the 2 last fields to 'Z'.
This is a follow up to what Renard posted.
If you want to have some fun and stress test your simulator, you can define sverilog_interface_t as follows:
type sverilog_interface_t is record
field0 : maximum std_ulogic;
field1 : maximum std_ulogic;
field2 : maximum std_ulogic;
field3 : maximum std_ulogic;
end record;
What this does is use the function maximum [std_ulogic_vector return std_ulogic] as a resolution function. Resolution functions can be applied any place there the syntax calls for a subtype_indication. In non-LRM terms, this is also any place where you can put an array constraint on a type.
I caution that many vendors were not expecting maximum to be used as a resolution function, so where I use this technique in OSVVM, I unfortunately use a wrapper function named resolved_max to call maximum. Note that the wrapper function is strictly to maximize tool support without having to have code variations. Maximum was introduced in 2008, so it is past time for simulators to support this.

Reading a matrix from file

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.

how to read image file and convert it to bits in vhdl

I am trying to read an image file using textio package in vhdl.
If i open an .jpg with notepad , i will get some junk data but actually it is ASCII data . Here i am trying to read these ascii data and convert them into bytes.
below is my code:
library ieee;
use ieee.std_logic_1164.all;
use std.textio.all;
use ieee.std_logic_textio.all;
entity file_io is
port (
clk: in std_logic;
Data: out std_logic_vector(7 downto 0)
);
end entity;
architecture behav of file_io is
signal test_data : std_logic_vector(7 downto 0);
use ieee.numeric_std.all;
use std.textio.all;
use ieee.std_logic_textio.all;
begin
File_reader:process(clk)
file f : text open read_mode is "C:\Users\Public\Pictures\Sample Pictures\Chrysanthemum.jpg";
variable L: line;
variable var_int: integer:= 0;
variable var_char: character;
begin
if rising_edge(clk) then
while not endfile(f) loop
readline(f, L);
read(L, var_char);
var_int := character'pos(var_char);
test_data <= std_logic_vector(to_unsigned(var_int, test_data'length));
end loop;
end if;
Data <= test_data;
end process;
end architecture behav;
testbench:
LIBRARY ieee;
use ieee.std_logic_1164.ALL;
use std.textio.all;
ENTITY file_io_test IS
END file_io_test;
ARCHITECTURE behavior OF file_io_test IS
use work.io.all;
signal clk: std_logic := '0';
signal Data: std_logic_vector(7 downto 0);
-- Clock period definitions
constant clk_period : time := 10 ns;
BEGIN
-- Instantiate the Unit Under Test (UUT)
UUT:
entity work.file_io(behav)
port map (
clk => clk,
Data => Data
);
-- Clock process definitions( clock with 50% duty cycle is generated here.
clk_process :process
begin
clk <= '1';
wait for clk_period/2; --for 5 ns signal is '1'.
clk <= '0';
wait for clk_period/2; --for next 5 ns signal is '0'.
end process;
end behavior;
I am getting only one byte in waveform. expected result is : Every clock cycle new character should be rread and new byte should be obtained.
below is waveform:
below is the image I am trying to read:
You have a while loop placed inside the rising_edge part of your process. What happens is that when the first clock edge occurs, the while loop iterates until the end of the file and gives you the last byte of the input image.
Removing the while loop statement should solve your issue.
The question has a fundamental flaw. You can't use textio to read binary values, it's for text.
See IEEE Std 1076-2008 16.4 Package TEXTIO paragraphs 3 (in part) and 4:
Procedures READLINE, WRITELINE, and TEE declared in package TEXTIO read and write entire lines of a file of type TEXT. Procedure READLINE causes the next line to be read from the file and returns as the value of parameter L an access value that designates an object representing that line. If parameter L contains a non-null access value at the start of the call, the procedure may deallocate the object designated by that value. The representation of the line does not contain the representation of the end of the line. ...
The language does not define the representation of the end of a line. An implementation shall allow all possible values of types CHARACTER and STRING to be written to a file. However, as an implementation is permitted to use certain values of types CHARACTER and STRING as line delimiters, it might not be possible to read these values from a TEXT file.
And that can be demonstrated with your Chrysanthemum.jpg:
It is possible in VHDL to read raw characters one at a time (matching your need).
See IEEE Std 1076-2008 5.5 File types:
So all we have to do is declare a file type and we get these procedures defined implicitly.
We can use them to invoke raw read, without any end of line issues caused by textio:
library ieee;
use ieee.std_logic_1164.all;
entity file_io is
port (
clk: in std_logic;
Data: out std_logic_vector(7 downto 0);
done: out boolean
);
end entity;
architecture foo of file_io is
use ieee.numeric_std.all;
begin
File_reader:
process (clk)
-- "C:\Users\Public\Pictures\Sample Pictures\Chrysanthemum.jpg";
constant filename: string := "Chrysanthemum.jpg"; -- local to sim
variable char_val: character;
variable status: FILE_OPEN_STATUS;
variable openfile: boolean; -- FALSE by default
type f is file of character;
file ffile: f;
variable char_count: natural := 0;
begin
if rising_edge (clk) then
if not openfile then
file_open (status, ffile, filename, READ_MODE);
if status /= OPEN_OK then
report "FILE_OPEN_STATUS = " &
FILE_OPEN_STATUS'IMAGE(status)
severity FAILURE;
end if;
report "FILE_OPEN_STATUS = " & FILE_OPEN_STATUS'IMAGE(status);
openfile := TRUE;
else
if not endfile(ffile) then
read(ffile, char_val);
-- report "char_val = " & character'image(char_val);
char_count := char_count + 1;
Data <= std_logic_vector (
to_unsigned(character'pos(char_val),
Data'length) );
end if;
if endfile(ffile) then -- can occur after last character
report "ENDFILE, read " &
integer'image(char_count) & "characters";
done <= TRUE;
FILE_CLOSE(ffile);
end if;
end if;
end if;
end process;
end architecture foo;
library ieee;
use ieee.std_logic_1164.all;
entity file_io_test is
end file_io_test;
architecture behavior of file_io_test is
signal clk: std_logic := '0';
signal data: std_logic_vector(7 downto 0);
signal done: boolean;
constant clk_period: time := 10 ns;
begin
uut:
entity work.file_io(foo)
port map (
clk => clk,
data => data,
done => done
);
clk_process:
process
begin
if not done then
clk <= '1';
wait for clk_period/2;
clk <= '0';
wait for clk_period/2;
else
wait;
end if;
end process;
end architecture behavior;
Now we can have all the characters than can delimit a line show up in our read:
Note that package std.textio is not made visible through any context item.

Procedure call in loop with non-static signal name

In some testbench code I use a procedure to do something with a signal. I then use this procedure multiple times in sequence on different signals. This works fine as long as I explicitly define the signal; as soon as I index signals in a loop it fails with
(vcom-1450) Actual (indexed name) for formal "s" is not a static signal name.
Why is this not possible and how can I work around it?
Probably I could move this to a for ... generate, but then I want do_something to be called in a nicely defined sequence.
library ieee;
use ieee.std_logic_1164.all;
entity test is
end test;
architecture tb of test is
signal foo : std_logic_vector(1 downto 0);
begin
dummy: process is
procedure do_something (
signal s : out std_logic
) is begin
s <= '1';
report "tic";
wait for 1 ns;
-- actually we would do something more interesting here
s <= '0';
report "toc";
end procedure;
begin
-- This works well, but requires manual loop-unrolling
do_something(foo(0));
do_something(foo(1));
-- This should do the same
for i in foo'range loop
-- This is the offending line:
do_something(foo(i));
end loop;
wait; -- for ever
end process dummy;
end architecture tb;
I'm using ModelSim 10.4 PE.
Interestingly, if foo is a variable local to the process, (and s is adjusted to suit) ghdl compiles this. Which highlights the problem in the original version. The "for" loop is required to drive the whole of foo all the time because you can't make signal drivers appear or disappear at will - it can't be ambivalent about which bits it's driving, (and as you can see, the procedure tries to drive different bits at different times).
So if you can readjust your application to allow variable update semantics, and make foo a variable local to the process, that will work. (You would have to copy its value to a signal before every "wait" if you wanted to see the effect!)
Alternatively, pass the entire foo signal and the index to the subprogram, so that the latter always drives all of foo as follows...
(I've also added the missing bits and fixed the spurious concurrent "wait" : in future, PLEASE check your code example actually compiles before posting!)
library ieee;
use ieee.std_logic_1164.all;
entity test is
end test;
architecture tb of test is
signal foo : std_logic_vector(1 downto 0);
begin
dummy: process is
procedure do_something (
signal s : out std_logic_vector(1 downto 0);
constant i : in natural
) is begin
s <= (others => '0');
s(i) <= '1';
report "tic";
wait for 1 ns;
-- actually we would do something more interesting here
s(i) <= '0';
report "toc";
end procedure;
begin
-- This works well, but requires manual loop-unrolling
do_something(foo,0);
do_something(foo,1);
-- This should do the same
for i in foo'range loop
-- This is the offending line:
do_something(foo,i);
end loop;
wait; -- for ever
end process dummy;
end architecture tb;
I share your feelings about this being a silly limitation of the language. Minus the wait and report statements your example certainly has a valid hardware implementation, let alone well defined simulation behavior.
I think this situation can be avoided in most cases. For example, in your simple example you could just copy the contents of the procedure into the process body, or pass the whole vector as Brian proposed. If you really need to do it, this is one workaround:
architecture tb of test is
signal foo : std_logic_vector(1 downto 0);
signal t : std_logic;
signal p : integer := 0;
begin
foo(p) <= t;
dummy: process is
procedure do_something (
signal s : out std_logic
) is begin
s <= '1';
wait for 1 ns;
s <= '0';
end procedure;
begin
for i in foo'range loop
p <= idx;
do_something(t);
wait for 0 ns;
end loop;
wait;
end process dummy;
end architecture tb;
This only works in simulation and will result in one delta cycle delay per iteration, compared to unrolling the loop which finishes in zero time when the procedure contains no wait statements.

VHDL 'generate' FSM states

I have a variable number of modules linked to another module via a signal bus : std_logic_vector(NUM-1 downto 0), with each component using 8 bits, so that:
bus(7 downto 0) = first module
bus(15 downto 8) = second module
As for creating the instances and doing the port mapping, that is easily done with a
INST: for i in 0 to NUM-1 generate
Inst_module port map ( bus => bus(i*8+7 downto i*8) );
end generate INST;
My question:
I would like to be able to interface with each module via a FSM (since it needs to do some other things too), so would like to be able to 'generate' the following code, rather than having to write out each state manually (Where signal empty : std_logic_vector(NUM-1 downto 0) is a status flag for each module)
type state_type is (st0_idle, st1_work0, st1_work1 --,etc.)
signal state : state_type;
begin
process(empty)
begin
if RESET = '1' then
--reset FSM
state <= st0_idle;
else
if CLK'event and CLK='1' then
case state is
when st0_idle =>
if empty(0) = '0' then
state <= st1_work0;
elsif empty(1) = '1' then
state <= st1_work1;
--etc.
end if;
when st1_work0 =>
bus(7 downto 0) <= SOMETHING;
state <= st0_idle;
when st1_work1 =>
bus(15 downto 8) <= SOMETHINGELSE;
state <= st0_idle;
--etc..
end if;
end if;
end process;
As you can see, there is a lot of repetition. But I can't simply put a for-generate inside the case, so what should I do?
One good way to make processes with state machines more readable is to merge common code into procedures defined within the process. For example:
process (empty) is
procedure assign_something (
index : natural;
something : std_logic_vector(7 downto 0)
next_state : state_type
) is
begin
bus(index*8+7 downto index*8) <= something;
state <= next_state;
end procedure;
begin
wait until rising_edge(clk);
case state is
when st0_idle => ...
when st1_work0 => assign_something(0, something, st0_idle);
when st1_work1 => assign_something(1, something_else, st0_idle);
-- ... etc ...
end case;
if reset = '1' then
state <= st0_idle;
end if;
end procedure;
Hopefully you get the idea. Depending on how regular the state machine structure is, you may also want to replace the enumerated state variables that correspond to each index with a simple count or index variable that you keep track of along with the named state.
That's all up to you, but however you do it, using procedures to factor out common code whenever you can will probably make your VHDL much easier to work with.
Applying this change would make the code look something like this:
architecture ...
type state_type is (st_idle, st_work);
signal state : state_type;
signal index : integer range 0 to NUM-1;
...
begin
...
process (empty) is
procedure assign_something (
index : natural;
something : std_logic_vector(7 downto 0)
next_state : state_type
) is
begin
bus(index*8+7 downto index*8) <= something;
state <= next_state;
end procedure;
begin
wait until rising_edge(clk);
case state is
when st_idle =>
for i in 0 to NUM-1 loop
if empty(i) = '1' then
index := i;
exit;
end if;
end loop;
when st_work => assign_something(index, something, st_idle);
end case;
if reset = '1' then
state <= st_idle;
end if;
end procedure;
Obviously this has to be changed to match exactly what you want to do ... =)

Resources