Multiple processes driving an array of records - arrays

The following code showcases an array of records. The particularity is that for each element of the array, the field AR is driven by the process process_AR while the field R is driven by the process process_R.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity test_entity is
end entity;
architecture RTL of test_entity is
-- Try with std_ulogic_vector or std_logic_vector
subtype slv is std_logic_vector;
subtype stdl is slv'element;
type AR_record_t is record
valid : stdl;
addr : slv(15 downto 0);
end record;
type R_record_t is record
ready : stdl;
data : slv(31 downto 0);
end record;
type axil_record_t is record
AR : AR_record_t;
R : R_record_t;
end record;
type array_of_axil_record_t is array(natural range <>) of axil_record_t;
signal axil_read_channel : array_of_axil_record_t(0 to 1);
begin
-- Process only deal with the AR channel
process_AR : process
begin
wait for 20 ps;
axil_read_channel(0).AR <= (valid => '1', addr => X"CAFE");
axil_read_channel(1).AR <= (valid => '0', addr => X"DEAD");
end process;
-- Process only deal with the R channel
process_R : process
begin
wait for 20 ps;
axil_read_channel(0).R <= (ready => '0', data => X"12345678");
axil_read_channel(1).R <= (ready => '1', data => X"89ABCDEF");
end process;
end architecture;
This code works as (I) expected.
However, change the process_AR by the following (using a for loop now):
-- Process only deal with the AR channel
process_AR : process
begin
wait for 20 ps;
for i in axil_read_channel'range loop
axil_read_channel(i).AR <= (valid => '1', addr => X"CAFE");
end loop;
end process;
When using non-resolved types (std_ulogic and std_ulogic_vector), this new code fails:
(vsim-3344) Signal "/test_entity/axil_read_channel(0).R.ready" has multiple drivers but is not a resolved signal.
I guess the for loop does not work because the it is sort of a 'dynamical' assignment and therefore axil_read_channel is considered instead of axil_read_channel(i) ?
On the other hand, the first version of the code (with hard coded '0' and '1') uses sort of 'static' assignment and therefore considers the two elements axil_read_channel(0) and axil_read_channel(1) as two signals and not element of an array ?
When using resolved types (std_logic and std_logic_vector):
What is the reason behind the difference of behavior between the first code and second code ?
Is there a work around not involving for-generate (not applicable to my current design) for synthesis ?

When you use a loop in a process to drive a signal of a composite type (array or record), the elaboration cannot determine which specific objects require a driver at elaboration time, hence it has to assume all objects within the composite type require a driver. This then creates a driver for the entire array/record, rather than each element that would have occured without the loop.
This is what is causing your error when you use resolved/unresolved types. The errors occurs with the unresolved types std_ulogic(_vector) because they are not allowed multiple drivers. The resolved types std_logic(_vector) are allowed multiple drivers and all of the elements undriven by you will have 'U' driven on them.

Addressing your question, is there a work around. Probably. Do your tools support VHDL-2008 aggregates with unconstrained elements? If yes, the following may work (have not tried it):
-- continuing from your above declarations:
type array_of_AR_record_t is array(natural range <>) of AR_record_t;
type array_of_R_record_t is array(natural range <>) of R_record_t;
type axil_record_t is record
AR : array_of_AR_record_t;
R : array_of_AR_record_t;
end record;
signal axil_read_channel : axil_record_t (AR(0 to 1), R(0 to 1)) ;
. . .
-- Process only deal with the AR channel
process_AR : process
begin
wait for 20 ps;
for i in axil_read_channel.AR'range loop
axil_read_channel.AR(i) <= (valid => '1', addr => X"CAFE");
end loop;
end process;
We are looking at language changes in the next revision that could help simplify the declarations. See: https://gitlab.com/IEEE-P1076/VHDL-Issues/-/issues/81

Related

Can we restrict the index order of an array parameter for a procedure?

Lets say we have a behavioural VHDL procedure for a testbench:
procedure jtag_load_data(data : in std_ulogic_vector) is
begin
jtag_clock(1, '1', '0');
jtag_clock(2, '0', '0');
for i in data'length-1 downto 1 loop
jtag_clock(1, '0', data(i));
end loop;
jtag_clock(2, '1', data(0));
jtag_clock(1, '0', data(0));
end procedure;
This procedure can either be called with a constant parameter like so
jtag_load_data("100")
or the parameter can be computed from others like this
jtag_load_data(tmasrc & tdosrc & tm)
Since VHDL arrays don't define the index order, I think the order the bits of data are processed within the for loop depends on the index of the data parameter when the procedure is called.
In case that wasn't clear, say a is of type std_logic_vector(3 downto 0) and b is of type std_logic_vector(0 to 3) and we do the following:
a <= "100";
b <= "100";
jtag_load_data(a);
jtag_load_data(b);
The bits processed for a will be 1, 0, 0 while for b they will be 0, 0, 1.
Back to my original two calling examples, this is a problem for the case where the parameter is a concatenation of std_logic_vectors: The expression tmasrc & tdosrc & tm (all three operands are of type std_logic_vector(n downto 0)) seems to evaluate to a std_logic_vector(0 to m), which reverses the order the bits are processed with respect to the case where I call the procedure with just one of the parameters (or a constant, which seems to default to the downto index ordering).
To fix this ambiguity, we can introduce a variable with known index order ordered_data like so:
procedure jtag_load_data(data : in std_ulogic_vector) is
variable ordered_data : std_ulogic_vector(data'length-1 downto 0);
begin
ordered_data := data;
jtag_clock(1, '1', '0');
jtag_clock(2, '0', '0');
for i in ordered_data'length-1 downto 1 loop
jtag_clock(1, '0', ordered_data(i));
end loop;
jtag_clock(2, '1', ordered_data(0));
jtag_clock(1, '0', ordered_data(0));
end procedure;
But I am wondering if there is a more elegant VHDL syntax to say either
"please interpret all data values passed into the procedure as having decreasing indexes" or
"please produce a std_logic_vector of decreasing indexes when concatenating them as in tmasrc & tdosrc & tm".
If you don't specify a range on a std_ulogic_vector (or std_logic_vector) input to a procedure (or function) then inside the procedure the left hand bit is always 0 and the bit numbers increase left to right. This is because a std_ulogic_vector is declared thus:
type std_ulogic_vector is array (natural range <>) of std_ulogic;
and since the index type is a natural whose left hand value is 0, the left hand element is always numbered 0 with an unconstrained parameter such as this.It is exactly the same for a std_logic_vector, which is declared the same way.
The numbering is not passed in from outside the procedure. Whatever is on the left outside the procedure (the actual) is numbered 0 inside the procedure (the formal).
So, in your example:
In case that wasn't clear, say a is of type std_logic_vector(3 downto
0) and b is of type std_logic_vector(0 to 3) and we do the following:
a <= "100"; b <= "100"; jtag_load_data(a); jtag_load_data(b);
a and b will be processed in exactly the same way; there is no difference; the fact that a is of type std_logic_vector(3 downto 0) and b is of type std_logic_vector(0 to 3) is irrelevant.
So, to answer your specific questions: it is up to you to ensure that the inputs are passed into the procedure in the correct order. If that is no good, then you need to pass in some other parameter to control the direction.

vhdl comparing vector output

I have a vector A that's 64bits long and I want the output B to equal 3 while A is 30-35 and zero elsewhere. I can't figure out the testbench to loop through the vector A as a bit. I've tried several different ways but only got 1/5 of the array to give any output at all. This is as far as I could get without syntax/compile errors.
Main code
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.NUMERIC_STD.ALL;
entity ent is
port(A:in std_logic_vector(5 downto 0);
B:out std_logic_vector(3 downto 0));
end ent;
architecture arch_ent of ent is
begin
with A select
B <= "0011" when "011110",
"0011" when "011111",
"0011" when "100000",
"0011" when "100001",
"0011" when "100010",
"0011" when "100011",
"0000" when others;
end arch_ent;
Testbench
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity tb is
end tb;
architecture arch_tb of tb is
component ent
port(A:in std_logic_vector(5 downto 0);
B:out std_logic_vector(3 downto 0));
end component;
signal A_tb: std_logic_vector(5 downto 0);
signal B_tb: std_logic_vector(3 downto 0);
begin
uut: entity ent port map(A=>A_tb, B=>B_tb);
tb: process
constant period: time := 20ns;
begin
for i in A_tb'range loop
A_tb <= std_logic_vector(to_unsigned(i,6));
wait for period;
assert (B_tb = "0011")
report "test failed" severity error;
end loop;
wait;
end process;
end arch_tb;
In the end I'm trying to plot out the waveform like this:
http://i10.photobucket.com/albums/a142/blargonblop/wave.png
where A will go to 63 and each output is its correct value from 30-35 and 0 elsewhere
The loop parameter you use to specify the number of 'tests' is A_tb'range, which happens to be 5 downto 0, or six tests, i is assigned 5,4,3,2,1 and 0 successively.
You want to specify i in 0 to 2**A-tb'length-1 or i in 0 to 63 to get all 64 possible A_tb 'binary' values.
(A_tb'length = 6, 2**6-1 = 63, where ** is the exponentiation operator, 2 to the 6th power minus 1 equals 63)
I found two syntax errors in your test bench, 20ns where the standard requires a space between 20 and ns:
constant period: time := 20 ns;
And entity ent where that should either be just ent (you have a component declaration ent) or entity work.ent and no need for a component declaration:
uut: ent port map(A=>A_tb, B=>B_tb);
or
uut: entity work.ent port map(A=>A_tb, B=>B_tb);
And in keeping with Russell's answer there is no implied logic replication in a loop other than through synthesis which unravels loop iterations by paralleling logic (the replication). Not all loop statements are intended as synthesis targets.
Test benches are generally not synthesized and are used to write tests (as in your case) for a VHDL model that might be used as a synthesis target.
First, loops are just fine, and common, in testbenches. #Russell's comment applies to RTL code. You can adapt his approach for this problem and make it work. You would need to use 64 as a sentinel (ending) value and do your end of test checks then. Keep in mind though that the most important thing you do is code for readability. Test cases generally run from top to bottom of a process one time.
You loop has some issues in addition to the recommendations #DavidKoontz gave. Specifically,
Your assertion is should not be checked when you expect B to be 0.
Using numeric_std_unsigned (requires VHDL-2008 compile switch) will simplify your conversions.
Keep an error count so you can report pass or failed at the end.
Keep your constants in the architecture or a package
So the modified code is:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.numeric_std_unsigned.all;
entity tb is
end tb;
architecture arch_tb of tb is
constant period: time := 20 ns;
...
begin
...
tb: process
variable ErrorCount :
begin
for i in i in 0 to 2**A-tb'length-1
A_tb <= to_slv(i,6);
wait for period;
if i >= 30 and i <= 35 then
if B_tb /= 3 then
ErrorCount := Error Count + 1 ;
report "B_tb = " & to_string(B_tb) & " Expecting: 0011" severity ERROR ;
end if;
else
if B_tb /= 0 then
ErrorCount := Error Count + 1 ;
report "B_tb = " & to_string(B_tb) & " Expecting: 0000" severity ERROR ;
end if;
end loop;
if ErrorCount = 0 then
report "Test Passed" severity NOTE ;
else
report "Test FAILED. There were " & to_string(ErrorCount) & " Errors " severity NOTE;
end if;
std.env.stop(0) ; -- testbench stops here
end process;
Note that the rules about using (or forbidding usage of) numeric_std_unsigned do not apply to testbenches.
You really should not be using a for loop for this. For loops in VHDL are used to REPLICATE LOGIC, not to do something some number of times. Try something like this in your test bench:
signal r_CLOCK : std_logic := '0';
signal r_INDEX : unsigned(5 downto 0) := (others => '0');
begin
r_CLOCK <= not r_CLOCK after period/2;
process (r_CLOCK)
begin
if rising_edge(r_CLOCK) then
r_INDEX <= r_INDEX + 1;
end if;
end process;
Now simply cast r_INDEX as std_logic_vector and pass it to your ent component.

Initializing an array of records in VHDL

I have an record defined as follows
type ifx_t is
record
data : std_logic_vector(127 downto 0);
address : std_logic_vector (19 downto 0);
WrReq : std_logic;--
RdReq : std_logic; --
end record;
type Array_ifx_t is array (0 to 2) of ifx_t;
And I have to initialize an instance of this array of records and I have tried the following way and it doesn't work
signal pair_in : Array_ifx_t:= (others =>((others =>'0'),(others=>'0'),'0','0'));
Kindly help.
As said in the comment, ModelSim work when compiling the code from question with ModelSim. However, other tools may be more strict about using a typed value for elements in Array_ifx_t.
For a typed assign and use of named record elements, which I think gives getter overview and avoid mistakes by position references, you can make the initialization with:
constant IFX_T_0S : ifx_t := (data => (others =>'0'),
address => (others=>'0'),
WrReq => '0',
RdReq => '0');
signal pair_in : Array_ifx_t:= (others => IFX_T_0S);
My initial reaction to seeing the aggregate in your default value for pair_in was that there were too many 'others', so I independently wrote one using the record type declaration itself:
library ieee;
use ieee.std_logic_1164.all;
package some_record is
type ifx_t is
record
data : std_logic_vector(127 downto 0);
address : std_logic_vector (19 downto 0);
WrReq : std_logic;--
RdReq : std_logic; --
end record;
type Array_ifx_t is array (0 to 2) of ifx_t;
-- positional association in an aggregate used for initialization:
signal pair_in: ifx_t := ((others => '0'), (others => '0'),'0','0');
end package;
And this analyzed successfully. An aggregate has two types of association, positional or named. The above default value expression is positional. With a named association:
signal pair_in: ifx_t := -- named association of record elements:
(
data => (others => '0'),
address => (others =>'0'),
WrReq => '0',
RdReq => '0'
);
You'll notice this bears an uncanny resemblance to the value expression found in Morten's accepted answer's constant declaration, and actually tells the story of compatibility for an aggregate expression.
An aggregate compatible with a record type contains a value expression that is compatible with each element of the record type. This is done for the array elements data and address by providing aggregates for their default values, while WrReq and RdReq are provided with their default values directly.
The extra others statement found in the original attempt would have been appropriate had pair_in been a composite type comprised of an array comprised of ifx_t record type elements.
The LRM (e.g. IEEE Std 1076-1993) has a section on Expressions, a subsection on Aggregates with a further subsection on Record aggregates.
There's also a section on Types, a subsection on Composite types, with a further subsection on Record types.

VHDL read from RAM and store in array delay?

I am currently doing a project in VHDL and since I am not an expert I am having some problems.
I'll try to clarify everything. So let's split in parts.
What I am trying to do is to write certain values in two different RAM memories and then reading from them and storing different values into an array which would be used by a different block to perform MAC filtering.
Here is the RAM code that I am using (is a modification of the code provided by Weijun Zhang) I don't know if I have to post the link here. If somebody needs it 'll post it
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
--------------------------------------------------------------
entity SRAM is
generic( width: integer:=32;
depth: integer:=1024;
addr: integer:=10);
port( clk: in std_logic;
enable: in std_logic;
read_en: in std_logic;
write_en: in std_logic;
read_addr: in std_logic_vector(addr-1 downto 0);
write_addr: in std_logic_vector(addr-1 downto 0);
Data_in: in std_logic_vector(width-1 downto 0);
Data_out: out std_logic_vector(width-1 downto 0)
);
end SRAM;
--------------------------------------------------------------
architecture behav of SRAM is
-- use array to define the bunch of internal temporary signals
type ram_type is array (0 to depth-1) of std_logic_vector(width-1 downto 0);
signal tmp_ram: ram_type:= ((others=> (others=>'0')));
begin
-- read_en Functional Section
process(clk, read_en)
begin
if (clk'event and clk='1') then
if enable='1' then
if read_en='1' then
-- buildin function conv_integer change the type
-- from std_logic_vector to integer
Data_out <= tmp_ram(conv_integer(read_addr));
else
Data_out <= (Data_out'range => 'Z');
end if;
end if;
end if;
end process;
-- write_en Functional Section
process(clk, write_en)
begin
if (clk'event and clk='1') then
if enable='1' then
if write_en='1' then
tmp_ram(conv_integer(write_addr)) <= Data_in;
end if;
end if;
end if;
end process;
end behav;
This RAM code works fine, I can't attach images because I don't have enough reputation (this somehow sounds familiar to me...)
What I wanted to explain with the image, is that at the same moment that I set an address to read, the output value is the value contained in that address.
Now let´s move to the actual problem:
What I am trying to do is to create a block with two of these RAM memories. One of this RAM is used to store the values of the input to be filtered, and the other one is to store the values of the filter coefficients. So the execution would be something like this:
Write only input coefficient into its dedicated memory (address from 1 to 1024)
Write all the new coefficients into their memory (again address from 1 to 1024)
Read from both memories one coefficient and one input value (staring from address 1) and store them in two arrays (in this case array of 4 vectors)
Fill up the arrays (3 cycles more in step 3)
Once the array is full perform the filtering with 4 input values and 4 coefficients (still not implemented)
Start again point 3
I'll try to save all the space that I can removing several lines (initialization and port declaration)
library IEEE; -- declare the library
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;
library work;
use work.mypackage.all; -- use of mypackage to use arrays as inputs
entity MAC_1024 is
port( clk: in std_logic;
enable: in std_logic;
enable_MAC: in std_logic;
rst: in std_logic;
read_input_en: in std_logic;
write_input_en: in std_logic;
read_coeff_en: in std_logic;
write_coeff_en: in std_logic;
X: in std_logic_vector(31 downto 0);
W: in std_logic_vector(31 downto 0);
Yt: out std_logic_vector(31 downto 0);
Yn: out std_logic_vector(31 downto 0)
);
end MAC_1024;
Now declaration of two RAMs
input_RAM: SRAM generic map (width=> t_width, depth=> t_depth, addr=> t_addr)
port map (clk, enable, read_input_en, write_input_en,read_input_addr, write_input_addr, X, saved_input);
coeff_RAM: SRAM generic map (width=> t_width, depth=> t_depth, addr=> t_addr)
port map (clk, enable, read_coeff_en, write_coeff_en,read_coeff_addr, write_coeff_addr, W, saved_coeff);
Here comes the process (the constant one has the value "0000000001")
process (clk, write_input_en, write_coeff_en)
begin
if (clk'event and clk='1') then
if (write_coeff_en='1') then
write_coeff_addr <= cont2;
cont2 <= unsigned(cont2) + unsigned(one);
end if;
if (write_input_en='1') then
i:=0;
write_input_addr <= cont1;
cont1 <= unsigned(cont1) + unsigned(one);
end if;
if (read_input_en='1' and read_coeff_en='1') then
read_input_addr <= cont3;
read_coeff_addr <= cont4;
X_in(i) <= saved_input;
W_in(i) <= saved_coeff;
cont3 <= unsigned(cont3) + unsigned(one);
cont4 <= unsigned(cont4) + unsigned(one);
X_in(i) <= saved_input;
W_in(i) <= saved_coeff;
i:=i+1;
if(i=4) then
i:=0;
end if;
end if;
end if;
end process;
Yn <= X_in(0);
Yt <= saved_input;
As you can see I am using the variable i to start in 0 and be filling up the arrays X_in and W_in. When the value is 4 then the position to place in that array goes back to 0.
The outputs Y_n and Y_t are used to test the functionality. Y_n outputs the value stored in X_in[0] and Y_t the output value of the RAM of the coefficients
So let's say to keep it simple that I have a sequence of input values (X) that is 1,2,3,4....1024 and each of these values are stored in address 1,2,3,4....1024
I would expect to be placing the values in the array following the next sequence:
X_in [Z Z Z 1]
X_in [Z Z 2 1]
X_in [Z 3 2 1]
X_in [4 3 2 1]
X_in [4 3 2 5]
X_in [4 3 6 5]
...
(when read enable is active)
The output Y_n (which reads X_in[0]) would be 1 1 1 1 5 5 5 5 9 9 9 9...
and the output Y_t (which reads RAM output) would be 1 2 3 4 5 6 7 8 9 ...
but what I obtain instead is
Y_n Z Z Z Z 4 4 4 4 8 8 8 8....
Y_t 1 2 3 4 5 6 7 8 9 ... (which is expected)
It looks like a problem with the index i (if read output the value X_in[1] I obtain the values that would correspond to my expected X_in[0] ), but if it was that in the same moment that I obtain in Y_t the value 4 I should obtain the value 4 in Y_n but i still got the previous Z. (bold)
The same happens to the array W_in...
Frankly I am kind of lost, I dont know if there is a problem with i, with delay or with what.
I have tried to be clear but I can understand that is a problem rather complicated to explain
Thank you very much for the help
Update 1:
I don't want to reset the address counters because I want to write in sequential addresses from 1 to 1024. Since the address depth is 1024 is 10 bits i am adding to the address the constant one which is a constant with 10 bits ("0000000001"). Once "1111111111" is reached the next address would be "0000000000". The code right now is prepared for a 1024 FIR filter, later i would try to do more flexible. Also i just want to add one value of input values to the memory but 1024 new coefficients every filter cycle, so the counter for coefficient could be reset after writing operation, but i dont reset the counter for input values because i need to know where i will store this value.
Update 2: I have been reading that the data output appears some cycles after you set the address to read in RAM (normally one cycle). That would be a possible cause for my problem but then why is my RAM code working without any delay???
You shouldn't drive i in two other if statements. Try to Change to if...elseif. Maybe it's not a reason of your bug but it's a basic rule.

Adressing a specific bits in an array of std_logic vector in VHDL

Im new to VHDL.
my problem is that i cant seem to find the correct syntax for writing or reading from an array of std_logic_vector. i init the array as such :
TYPE eleven_samples_in IS ARRAY ( 0 TO 10 ) OF STD_LOGIC_VECTOR( 87 DOWNTO 0 );
and i try addressing it as such :
odd: for i in 1 to 6 generate
node: compare_level
port map(
input => eleven_samples_in(i*2 - 1)(79 DOWNTO 0),
output => eleven_samples_out(i*2 - 1)(79 DOWNTO 0 )
);
end generate odd;
Or :
port map(
input => eleven_samples_in(i*2 - 1,79 DOWNTO 0),
output => eleven_samples_out(i*2 - 1,79 DOWNTO 0 )
);
end generate odd;
But i get an Errors such as :
Error (10409): VHDL Type Conversion error at Median_Filter.vhd(45): converted type of object near text or symbol "eleven_samples_in" must match std_logic_vector type of target object
I searched the web and found nothing that works.
thank you very much for the help .
you create a type eleven_samples_in, and use that directly. This is incorrect.
Instead:
type eleven_samples_in_type is array (0 to 11) of std_logic_vector(89 downto 0);
signal eleven_samples_in : eleven_samples_in_type;
...
Without knowing anything about your compare_levels component, thats as much help as I can be
To answer about the right syntax to access specific bits from an array of std_logic_vector, as I was wondering myself, it happens to be the first you suggested:
s_array(array_index)(vector_bit_index) -- for a single bit
or
s_array(array_index)(last_bit_index downto first_bit_index) -- for a range

Resources