How can i fill and display a matrix ? [VHDL] - vhdl

I have a datain as an std_logic_vector and i want fill a matrix with datain's bits and then display it.
How can i fill and display the matrix ?
Here is my code:
signal datain : std_logic_vector(39 downto 0) := "1111011101100110011001010110011001100110";
for i1 in 1 to 5 loop
for j1 in 1 to 8 loop
for j2 in datain'range loop
mat1(i1,j1)<=datain(j2);
end loop;
end loop;
end loop;
------- display the matrix
for i2 in 1 to 5 loop
for i3 in 1 to 8 loop
for i4 in dataout'range loop
dataout(i4) <= mat1(i2,i3);
end loop;
end loop;
end loop;
Thank you,

First we construct a Minimal, Complete and Verifiable example from your code snippets:
library ieee;
use ieee.std_logic_1164.all;
entity abir is
end entity;
architecture foo of abir is
type mat_type is array (1 to 5, 1 to 8) of std_logic;
signal mat1: mat_type;
signal datain : std_logic_vector(39 downto 0) :=
"1111011101100110011001010110011001100110";
signal dataout: std_logic_vector (39 downto 0); -- MISSING
-- this function is predefined in VHDL -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;
begin
INITIALIZE_MATRIX:
process -- (datain)
begin
for i1 in 1 to 5 loop
for j1 in 1 to 8 loop
for j2 in datain'range loop
mat1(i1,j1)<=datain(j2);
end loop;
end loop;
end loop;
wait; -- Do only once, depends on the initial value of datain
end process; -- the wait statement can be removed if you add sensitivity
------- display the matrix
MATRIX_T0_DATAOUT:
process (mat1)
begin
for i2 in 1 to 5 loop
for i3 in 1 to 8 loop
for i4 in dataout'range loop
dataout(i4) <= mat1(i2,i3);
end loop;
end loop;
end loop;
end process;
DISPLAY_DATAOUT:
process -- (dataout)
begin -- wait statements so only disply valid datout
wait for 0 ns; -- first delta cycle all 'U's (dataout uninitialized)
wait for 0 ns; -- second delta cycle all 'U's (mat1 uninitialized)
report LF &
HT & "datain = " & to_string(datain) & LF &
HT & "dataout = " & to_string(dataout);
wait on dataout;
end process;
end architecture;
The function to_string is predefined in VHDL -2008, this MCVE should work with tools compliant earlier revisions of the VHDL standard.
It's specific to demonstrating your code. It gives:
ghdl -a abir.vhdl
ghdl -e abir
ghdl -r abir
abir.vhdl:58:9:#0ms:(report note):
datain = 1111011101100110011001010110011001100110
dataout = 0000000000000000000000000000000000000000
So there's something wrong with your nested loops (you can also verify this with a waveform viewer to determine mat1 is indeed all '0's).
So the cause of this is the very inner loops. With datain you assign each element of matrix mat1(i,j) N times where N is the length of datain (range of j2). With dataout you assign each indexed element of dataout (i4) every matrix element of mat(i2,i3).
So is it possible to have three loops performing these assignments?
Well, no.
In the INITIALIZE_MATRIX process every (i,j) location of mat1 was overwritten with all the index values of datain. Only the last one took affect. This filled the matrix with all '0's.
In the MATRIX_TO_DATAOUT process all the dataout indexes were 'scheduled' to have the last mat1(i2,i3) value of each i3 loop iteration, settling on the last loop iteration value of i2 and i3, a '0'.
We can modify the two sets of loops to decrement j2 or i4 as variables directly (the ranges of datain and dataout are in in descending order):
INITIALIZE_MATRIX:
process -- (datain)
variable j2: natural range datain'range;
begin
j2 := datain'LEFT; -- so the process can execute again.
for i1 in 1 to 5 loop
for j1 in 1 to 8 loop
-- for j2 in datain'range loop
mat1(i1,j1) <= datain(j2);
if j2 /= datain'RIGHT then
j2 := j2 - 1; -- datain has descending range
end if;
-- end loop;
end loop;
end loop;
wait; -- Do only once, depends on the initial value of datain
end process; -- the wait statement can be removed if you add sensitivity
------- display the matrix
MATRIX_T0_DATAOUT:
process (mat1)
variable i4: natural range dataout'range;
begin
i4 := dataout'LEFT; -- so the process can execute again
for i2 in 1 to 5 loop
for i3 in 1 to 8 loop
-- for i4 in dataout'range loop
dataout(i4) <= mat1(i2,i3);
if i4 /= dataout'RIGHT then
i4 := i4 - 1; -- dataout has descending range
end if;
-- end loop;
end loop;
end loop;
end process;
And that gives us:
abir.vhdl:68:9:#0ms:(report note):
datain = 1111011101100110011001010110011001100110
dataout = 1111011101100110011001010110011001100110
Where we find dataout matches datain. (A good thing.)
So the issue was the three nested loops in each process were incorrect. We wanted to manage the pointers to the input and output arrays separately.
We also manage the assignments to the variables j2 or i4 to prevent a bounds violation using if statements to prevent j2 or i4 being decremented when the variable assignment would be out of the value range of the variable. A bounds check failure on assignment would abort the simulation.
Note that signal assignment results in a value being written to a projected output waveform (a queue). Signal updates don't occur before any pending process has run and suspended. There's only one value for any time in the projected output waveform. (including the current simulation time).
These two modified processes could be used as the basis of conversion functions:
architecture fum of abir is
type mat_type is array (1 to 5, 1 to 8) of std_logic;
signal mat1: mat_type;
signal datain : std_logic_vector(39 downto 0) :=
"1111011101100110011001010110011001100110";
signal dataout: std_logic_vector (39 downto 0); -- MISSING
-- this function is predefined in VHDL -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_matrix (inp: std_logic_vector) return mat_type is
alias input: std_logic_vector(0 to inp'length - 1) is inp; -- ascending
variable mat: mat_type;
variable inptr: natural range 0 to inp'length;
begin
assert input'length = mat'length(1) * mat'length(2)
report LF &
"to_matrix call, input length (" &
integer'image(inp'length) & ") " &
"/= " & integer'image( mat'length(1) * mat'length(2))
severity FAILURE;
for i in mat'range(1) loop -- first dimension
for j in mat'range(2) loop -- second dimension
mat(i,j) := input(inptr);
inptr := inptr + 1; -- inptr range allows last increment
end loop;
end loop;
return mat;
end function;
function to_std_logic_vector (mat: mat_type) return std_logic_vector is
variable retval:
std_logic_vector(0 to mat'length(1) * mat'length(2) - 1);
variable outptr: natural range 0 to retval'length;
begin
for i in mat'range(1) loop -- first dimension
for j in mat'range(2) loop -- second dimension
retval(outptr) := mat(i,j);
outptr := outptr + 1; -- outptr range allows last increment
end loop;
end loop;
return retval;
end function;
begin
INITIALIZE_MATRIX:
mat1 <= to_matrix(datain);
MATRIX_T0_DATAOUT:
dataout <= to_std_logic_vector(mat1);
DISPLAY_DATAOUT:
process -- (dataout)
begin -- wait statements so only disply valid datout
wait for 0 ns; -- first delta cycle all 'U's (dataout uninitialized)
wait for 0 ns; -- second delta cycle all 'U's (mat1 uninitialized)
report LF &
HT & "datain = " & to_string(datain) & LF &
HT & "dataout = " & to_string(dataout);
wait for 1 ns;
wait on dataout;
end process;
end architecture;
The two functions are dependent only on the matrix type declaration. You can change the mat_type declaration without having to modify declarations or the any of the sequence of statements found in the functions.
The new architecture with the to_matrix[std_logic_vector return mat_type] and to_std_logic_vector[mat_type return std_logic_vector] function calls provides the same answer as the MCVE with the corrected process statements.

Related

Use alias-like variable in for loops

Is it possible to create an alias variable/signal to improve readability of for loops in VHDL processes?
For instance, consider the following module which contains a process with inner for loops (code is for example purpose, I haven't test it):
library ieee;
use ieee.std_logic_1164.all;
entity MyModule is
port (
clk : in std_logic;
inData : in std_logic_vector(7 downto 0);
outData : out std_logic_vector(7 downto 0));
end MyModule;
architecture functional of MyModule is
type sample_vector is array (natural range <>) of std_logic_vector(9 downto 0);
type data_t is record
samples : sample_vector(3 downto 0);
-- other elements...
end record data_t;
type data_vector is array (natural range <>) of data_t;
signal data : data_vector(1 downto 0);
begin -- functional
process (clk)
begin -- process
if clk'event and clk = '1' then
-- Set outData(N) to '1' if at least 1 of the last 10 values of inData(N) was '1'
for d in data'RANGE loop
for s in data(0).samples'RANGE loop
data(d).samples(s)(9 downto 1) <= data(d).samples(s)(8 downto 0);
data(d).samples(s)(0) <= inData(d * 4 + s);
outData(d * 4 + s) <= '0';
for b in data(d).samples(s)'RANGE loop
if data(d).samples(s)(b) = '1' then
outData(d * 4 + s) <= '1';
end if;
end loop;
end loop;
end loop;
end if;
end process;
end functional;
Having to use data(d).samples(s) every time I need to reference that signal is cumbersome, so I'd rather use an alias-like variable, something like that instead (inspired from generate syntax, idx part is just a bonus):
-- Set outData(N) to '1' if at least 1 of the last 10 values of inData(N) was '1'
for d in data'RANGE loop
for s in data(0).samples'RANGE loop
alias sample : std_logic_vector(9 downto 0) is data(d).samples(s);
constant idx : integer := d * 4 + s;
begin
sample(9 downto 1) <= sample(8 downto 0);
sample(0) <= inData(idx);
outData(idx) <= '0';
for b in sample'RANGE loop
if sample(b) = '1' then
outData(idx) <= '1';
end if;
end loop;
end loop;
end loop;
Of course, this does not work. So, is there any way to achieve something like that in VHDL, or do we always have to specify the full signal "path" each time?
I could replace the loop body with a procedure, but having to declare the procedure code in a (far away) different place of the file reduces readability even more. I could also use a for ... generate construct, but this will create 1 process for each iteration and prevent me from using common process variables inside the iteration.
As indicated in question comments, this can be achieve using process variables:
process (clk)
variable sample : std_logic_vector(9 downto 0);
variable idx : integer;
begin -- process
if clk'event and clk = '1' then
-- Set outData(N) to '1' if at least 1 of the last 10 values of inData(N) was '1'
for d in data'RANGE loop
for s in data(0).samples'RANGE loop
-- Helpers
sample := data(d).samples(s);
idx := d * 4 + s;
outData(idx) <= '0';
for b in sample'RANGE loop
if sample(b) = '1' then
outData(idx) <= '1';
end if;
end loop;
sample(9 downto 1) <= sample(8 downto 0);
sample(0) <= inData(idx);
-- Do not forget to apply changes
data(d).samples(s) <= sample;
end loop;
end loop;
end if;
end process;
Of course, using process variables implies changing the operations order to get the same behavior.
Since process variables are read and written in the loops, I was worried the synthesis tools would believe the result of iteration N was dependent on the result of iteration N-1, and make implements the iterations in series (instead of in parallel). However, after unrolling the loop (which is what synthesis tools do), it gets clear the synthesis tools will see sample and idx values are not re-used between iterations.

Easy Count-Down Counters Integer vs Unsigned

Rather than having to build counters as follows -
signal my_counter : unsigned(3 downto 0) := to_unsigned(9, 4);
signal reset_value : unsigned(3 downto 0) := to_unsigned(9, 4);
--...
--...
process(clk)
begin
if rising_edge(clk) then
counter <= counter - 1;
if counter = 0 then
counter <= reset_value;
-- raise flag telling other logic to do stuff
end if;
end if;
end process;
Could you do this with an integer count down with a range? and therefore you wouldn't need to have the reset_value? Could this look something like...
signal my_counter_int : integer range 0 to 9 := 9;
--...
--...
process(clk)
begin
if rising_edge(clk) then
counter <= counter - 1;
if counter = 0 then
-- raise flag telling other logic to do stuff
end if;
end if;
end process;
I'm just seeing whether you can have an implied roll over to 9 with an integer with a set range.
Create your own subtype from integer with the proper range (0–9) and define the counter variable to be of that type:
subtype MY_COUNTER_TYPE is integer range 0 to 9;
signal counter : MY_COUNTER_TYPE := 9;
Declare a "rolling decrement" function for your own subtype, which folds the value back to the highest possible value in the range if the decrement would decrease the value under the range:
function r_decrement(val : MY_COUNTER_TYPE) return MY_COUNTER_TYPE is
begin
if val = MY_COUNTER_TYPE'LOW then
return MY_COUNTER_TYPE'HIGH;
else
return val - 1;
end if;
end function;
Now you can use the rolling decrement function of the type without worrying about the resetting the counter manually or checking if the decrement would result in the signal being out of the allowed range:
if rising_edge(clk) then
counter <= r_decrement(counter); -- "rolling" decrement
if counter = 0;
-- raise flag telling other logic to do stuff
end if;
end if;
So yes, it is possible. And if you're using a lot of counters like this in your design, you may avoid writing a lot of redundant code when checking the counter limits and resetting the value back to reset_value manually.

Assigning subranges of vectors in different processes

I would like to control different sub-ranges of vectors from different processes.
type t_regs is array (integer range <>) of std_logic_vector(15 downto 0);
...
signal regs : t_regs(NUM_REGS-1 downto 0);
...
process (clk, nreset)
begin
for i in 0 to NUM_REGS-1 loop
if (nreset = '0') then
regs(i)(15) <= '1';
elsif falling_edge(clk) then
-- set regs(i)(15)
end if;
end loop;
end process;
process (clk, nreset)
begin
for i in 0 to NUM_REGS-1 loop
if (nreset = '0') then
regs(i)(14 downto 10) <= (others => '0');
elsif falling_edge(clk) then
-- set regs(i)(14 downto 10)
end if;
end loop;
end process;
-- Set other bits of regs
...
Even though the sub-ranges of the vectors being set in each process do not overlap, when simulating this code the values of regs are undefined. I would have expected each bit of the register to be treated as an independent flip-flop.

Write followed by Read in VHDL process

The following code is for a very simple program in VHDL.
entity ent is
port(
clk: in std_logic;
out_value: out std_logic;
);
end entity ent;
architecture ent_arch of ent is
signal counter: std_logic_vector(3 downto 0);
begin
process(clk)
begin
if rising_edge(clk) then
counter <= counter + 1;
if counter = 10 then
counter <= (others => '0') ;
end if;
end if;
end process;
end ent_arch;
Imagine counter = 9 and we enter in the if statement (if rising_edge(clk)). The first statement counter <= counter + 1 will assign 10 to counter. My question is "Is the if statetement (if counter = 10) evaluted as true in this entrance or in the next entrance of the process?". In other words "For this comparison in this entrance of the process, is counter = 10 due to the previous statement?"
Thanks a lot for any answer!
Signal assignments are always delayed. You didn't specified a delay explicitly using the after keyword, thus the assignment is delayed for one delta cycle (same as after 0 fs). That means, that the value of the expression on the right side of this statement:
counter <= counter + 1;
will be assigned to counter at the beginning of the next simulation cycle.
But, all remaining statements in your process, are executed in the current simulation cycle. Thus, this read of the counter value,
if counter = 10 then
will still use the old value, so that, the counter will be reseted when it is already 10 at the rising clock edge. The counter counts from 0 to 10 inclusive.
The delay of at least one delta cycle, easily allows to swap the content of registers:
-- within declarative part of architecture
signal reg_a, reg_b : std_logic;
-- within architecture body
process(clock)
begin
if rising_edge(clock) then
reg_a <= reg_b;
reg_b <= reg_a; -- assign old value of reg_a !
end if;
end process;

VHDL infinite loop

I'm writing a small piece of code to take a 32 bit input and output 2 bits at a time. I believe I'm having infinite loop problems from the while loop, based on simulation attempts. Everything looks right to me, compared to other examples of loops I've looked at. Any clue what I could be doing wrong?
library ieee;
use ieee.std_logic_1164.all;
entity regA is
port(mpcnd: in std_logic_vector(31 downto 0);
clk: in std_logic;
twobits: out std_logic_vector(1 downto 0));
end regA;
architecture behavior of regA is
begin
process
variable count: integer;
begin
count := 0;
while (count < 32) loop
if rising_edge(clk) then
twobits(0) <= mpcnd(count);
twobits(1) <= mpcnd(count+1);
count := count + 2;
end if;
end loop;
end process;
end behavior;
for a process you need either a sensitivity list or a wait statement within. a (not synthesisable but simulatable) version of your process could look as follows:
process
variable count: integer;
begin
count := 0;
while (count < 32) loop
wait until rising_edge(clk);-- if rising_edge(clk) then
twobits(0) <= mpcnd(count);
twobits(1) <= mpcnd(count+1);
count := count + 2;
--end if;
end loop;
end process;

Resources