Reading a matrix from file - vhdl

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.

Related

Driving record elements through procedures from different processes in 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

How do I add the bits of a vector and at the same time save the value in a vector signal? I use google translator**

I am trying to know if the amount of "1" in a std_logic_vector is an odd or even number. For that I am trying to use an "if" statement along with a counter, but I don't get the expected result.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.std_logic_unsigned.all;
entity prueba2 is
port (
entrada: in std_logic_vector(0 to 10)
);
end prueba2;
architecture Behavioral of prueba2 is
signal bit1: std_logic_vector(0 to 3):= (others => '0');
begin
prueba: process(entrada)
--variable suma: unsigned(0 to 2):= (others => '0');
begin
for i in 0 to 10 loop
if (entrada(i)= '1') then
bit1 <= bit1+1;
end if;
end loop;
end process;
end Behavioral;
I am not getting errors in the syntax, but for example, by entering a vector "1111111111" I receive as output in the simulation, using ISE design Suite, the value "0001" instead of "1010". I appreciate your corrections and code suggestions.
See IEEE Std 1076-2008 10.5.2.2 Executing a simple assignment statement, 14.7.3.4 Simulation cycle and 14.7.3.4 Signal update.
Signals aren't updated immediately. The current simulation cycle isn't complete until every process sensitive to an event has executed and potentially scheduled signal updates and subsequently suspended. Simulation time is advanced to the time the next signal update is scheduled. Signals are updated at the beginning of a simulation cycle before any suspended processes resume due to a signal event.
Processes both suspend and resume executing wait statements (see 10.2 Wait statement). A process statement (11.3) with a sensitivity list has an implicit wait statement as the last statement with it's sensitivity list having the contents of the process sensitivity list.
The signal bit1 won't update while the loop is executing, there is no implicit or explicit wait statement causing the process to suspend.
To solve the issue use a variable:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.std_logic_unsigned.all; -- arithmetic for std_logic_vector
entity prueba2 is
port (
entrada: in std_logic_vector(0 to 10) := (others => '1')
);
end entity prueba2;
architecture behavioral of prueba2 is
signal bit1: std_logic_vector(0 to 3):= (others => '0');
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;
begin
prueba:
process (entrada) -- sensitivity list implies wait as final statement
-- variable suma: unsigned(0 to 2):= (others => '0');
variable suma: std_logic_vector(0 to 3);
begin
suma := (others => '0'); -- start at 0 every time
for i in entrada'range loop
if entrada(i) = '1' then
-- bit1 <= bit1+1 -- signals don't update until the beginning
-- of the next simulation cycle (at the soonest)
suma := suma + 1; -- variables assignment is immediate
end if;
end loop;
report "bit1 will be assigned " & to_string(suma);
bit1 <= suma; -- both the same length processes communicate with signals
-- wait on entrada; -- implicit wait statement, process suspends
end process;
end architecture behavioral;
You can see the variable has the same length as bit1. That's required, you input entrada has a length of 11 which requires a 4 bit value to accumulate the number of '1's.
There are some embellishments for testing. Some simulators will allow you to simulate a top level with ports as long as inputs have default values or can be forced. Here entrada is supplied with all '1's. The to_string function is provided in revision -2008 of the standard, also provided here for compatibility with earlier revisions. The report statement tells us the value of suma that will be assigned to bit1.
When the design unit is analyzed and elaborated (compiled) then run:
ghdl -a --ieee=synopsys -fexplicit prueba2.vhdl
ghdl -e --ieee=synopsys -fexplicit prueba2
ghdl -r prueba2
prueba2.vhdl:37:9:#0ms:(report note): bit1 will be assigned 1011
The loop has successfully counted all the '1's, and bit1 will have an update scheduled with the same simulation time (here 0 ms). The simulator will execute a follow on simulation cycle (a delta cycle) then without any further schedule signal updates scheduled in any projected output waveform (a queue) the simulation time will advance to TIME'HIGH and simulation will end.
When a simulation starts every process executes at least once following initialization. The event on bit1 will cause a signal value update but there are no processes in the stand alone example sensitive to bit1 and simulation will cease.
The default value on the input entrada, function to_string and the report statement can be removed, they are present for testing, a testbench not having been provided.

VHDL record assignment through for loop

I have a for loop in process, which works fine with std_logic arrays, but not with record arrays. I use Xilinx ISE along with ISIM and the code is vhdl-93. The target will be a Spartan 3.
Here is the record definition:
TYPE spi_rx_t IS RECORD
CS : std_logic;
MOSI : std_logic;
CLK : std_logic;
END RECORD;
constant SYNC_LATCHES : integer := 2;
Here is the array definition and declaration:
type spi_rx_array_t is array (0 to SYNC_LATCHES) of spi_rx_t;
signal spi_in_array : spi_rx_array_t;
Below is the process:
spi_in_array(0).MOSI <= SPI_MOSI;
spi_in_array(0).CLK <= SPI_CLK;
spi_in_array(0).CS <= SPI_CS;
sync_p: process (clk_100)
begin
if rising_edge(clk_100) then
-- for I in 1 to SYNC_LATCHES loop
-- spi_in_array(I) <= spi_in_array(I - 1);
-- end loop;
spi_in_array(1) <= spi_in_array(0);
spi_in_array(2) <= spi_in_array(1);
end if;
end process;
The 2 lines below the commented code works exactly as expected (allowing me to synchronize external signals to clk_100), but I'd rather implement them as a for loop (such the commented one).
However, these commented lines does not produce the same result in my ISIM test bench (spi_in_array stays in unknown state when using the for loop). Why?
Please kindly help me with this.
As commented by Morten Zilmer, this is due to the VHDL concept "longest static prefix". This SO answer is similar to my issue.
In my case, the simplest way to resolve the issue was to move the assignment of the first element of the array into the same process as the for loop. I also had to decrease SYNC_LATCHES constant from 2 to 1, because spi_in_array(0) is now latched with clk_100.
sync_p: process (clk_100)
begin
if rising_edge(clk_100) then
spi_in_array(0).MOSI <= SPI_MOSI;
spi_in_array(0).CLK <= SPI_CLK;
spi_in_array(0).CS <= SPI_CS;
for I in 1 to SYNC_LATCHES-1 loop
spi_in_array(I) <= spi_in_array(I - 1);
end loop;
end if;
end process;

Not able to write the output of testbench to file

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;
use IEEE.STD_LOGIC_ARITH.all;
use IEEE.STD_LOGIC_SIGNED.all;
use std.env.all;
USE IEEE.STD_LOGIC_TEXTIO.ALL;
USE STD.TEXTIO.ALL;
ENTITY tb_top IS
END tb_top;
ARCHITECTURE behavior OF tb_top IS
-- Component Declaration for the Unit Under Test (UUT)
COMPONENT c4b
PORT(
clock : IN std_logic;
reset : IN std_logic;
count : OUT std_logic_vector(3 downto 0)
);
END COMPONENT;
--Inputs
signal clock : std_logic := '0';
signal reset : std_logic := '0';
--Outputs
signal count : STD_LOGIC_vector(3 downto 0) := "0000";
-- Clock period definitions
constant period : time := 100 ns;
-- Opening the file in write mode
file myfile : TEXT open write_mode is "fileio.txt";
BEGIN
-- Instantiate the Unit Under Test (UUT)
uut: c4b PORT MAP (
clock => clock,
reset => reset,
count => count
);
clock_process :process --providing clock to the counter
begin
clock <= '1';
wait for period/2;
clock <= '0';
wait for period/2;
end process;
write_process: process
variable abd: LINE;
--variable val1: integer;
begin
--val1 := CONV_INTEGER(count); --saw this in another program. even they converted a std_logic_vector to integer. didn't work!
loop --tried the infinite loop to check for the value 1111,
if (count = "1111") then --so that as soon as count reaches the value 1111,
finish (0); --it would stop, because i only need to write one entire sequence of the cycle to the file!!
else
write (abd, count);
writeline (myfile, abd);
end if;
end loop;
end process;
-- Stimulus process
stim_process: process
begin
reset <= '0'; --because it only works when reset is 0!
wait for 100 ns;
if (count = "1111") then --the value is written to the text file in a continuous loop,
finish (0); --which makes he file size go to as much as 1 GB
end if; --thus to stop it at exactly one cycle!
end process;
END;
So, basically what I want to do here is let the counter count up from 0000 too 1111 and then write the entire sequence to a text file. But I only wish to write exactly just one cycle. Hence I've added a loop to check the same. Here in the code above I'm not able to simulate the testbench properly. When I don't include the write_process part of the code, the simulation works perfectly, giving me exactly just one cycle! (Simulator result w/o write_process picture no 1). But when I try to use the write_process, not only does it not simulate (Simulator result after adding the write_process) picture no 2, it also writes UUUU continuously to the file, until I close the ISim, and file size goes to at least a few hundred MBs! Please help!
Without the entity/architecture for c4b no one can duplicate your error, it's visible however.
Note that the write process has no sensitivity list nor wait statement and a loop statement that won't exit unless external stimuli is provided - if (count = "1111") then ...
It doesn't seem proper to be using finish in both stim_process and write_process, there's no guarantee of execution order you can lose the last write (if you cure the write process defect).
You have four unused use clauses (numeric_std, std_logic_arith, std_logic_signed, std_logic_textio, with VHDL -2008).
So, basically what I want to do here is let the counter count up from 0000 too 1111 and then write the entire sequence to a text file.
There's nothing in your code that spools up output until your simulation finishes. Writing a line to file textio.txt occurs for events driving the write process.
Adding a model for c4b:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std_unsigned.all;
entity c4b is
port (
clock: in std_logic;
reset: in std_logic;
count: out std_logic_vector(3 downto 0)
);
end entity;
architecture foo of c4b is
begin
process (clock, reset)
begin
if reset = '1' then
count <= (others => '0');
elsif rising_edge (clock) then
count <= count + 1;
end if;
end process;
end architecture;
Removing unused use clauses (not strictly necessary):
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
-- USE IEEE.NUMERIC_STD.ALL;
-- IEEE.STD_LOGIC_ARITH.all;
-- use IEEE.STD_LOGIC_SIGNED.all;
use std.env.all;
-- USE IEEE.STD_LOGIC_TEXTIO.ALL;
USE STD.TEXTIO.ALL;
Changing the write_process to not endlessly loop:
-- write_process: process
-- variable abd: LINE;
-- --variable val1: integer;
-- begin
-- --val1 := CONV_INTEGER(count); --saw this in another program. even they converted a std_logic_vector to integer. didn't work!
-- loop --tried the infinite loop to check for the value 1111,
-- if (count = "1111") then --so that as soon as count reaches the value 1111,
-- finish (0); --it would stop, because i only need to write one entire sequence of the cycle to the file!!
-- else
-- write (abd, count);
-- writeline (myfile, abd);
-- end if;
-- end loop;
-- end process;
write_process:
process
variable abd: LINE;
begin
wait on count;
wait for 100 ns;
write (abd, count);
writeline (myfile, abd);
if count = "1111" then
finish (0);
elsif IS_X(count) then
report "count contains a metavalue and may not increment";
finish (-1);
end if;
end process;
The wait 100 ns; accommodates a reset to insure the counter can increment. It's possible to provide a design description of c4b that doesn't depend on reset. For purposes here, the supplied c4b doesn't do that. The wait 100 ns also provides the sample interval for count, which from the component declaration for c4b is free running, driven by clock events.
Changing the stim_process to not finish and wait instead:
-- -- Stimulus process
-- stim_process: process
-- begin
-- reset <= '0'; --because it only works when reset is 0!
-- wait for 100 ns;
-- if (count = "1111") then --the value is written to the text file in a continuous loop,
-- finish (0); --which makes he file size go to as much as 1 GB
-- end if; --thus to stop it at exactly one cycle!
-- end process;
-- END;
stim_process:
process
begin
reset <= '1';
wait for 100 ns;
reset <= '0';
wait for 100 ns;
wait;
end process;
Notice this also provides a reset interval seen on the following waveform.
And that gives us:
With the contents of textio.txt:
more fileio.txt
0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1101
1110
1111
It's also possible to end simulation after detecting count is "1111" by stopping clock, avoiding the use of the finish procedure. Along with a write procedure supplied using the Synopsys package std_logic_texio:
procedure WRITE(L:inout LINE; VALUE:in STD_LOGIC_VECTOR;
This would allow the use of VHDL simulators complying to VHDL standard revisions earlier than -2008.

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.

Resources