Synthesizable multidimensional arrays in VHDL
Asked Answered
I

2

10

I need to use multidimensional arrays to represent matrices in my design. I have tried the two available options:

  1. Declaring array of arrays

    type t11 is array (0 to c1_r2) of std_logic_vector(31 downto 0);
    type t1 is array (0 to r1) of t11; --r1*c1_r2 matrix
    
  2. Multidimensional arrays.

    type matrix  is array (natural range <>, natural range <>)
                   of std_logic_vector(31 downto 0);
    

However, in both the cases my post synthesis simulation in xilinx gives me the error "Sliced name is allowed only on single-dimensional arrays". What is the correct way of using multidimensional arrays in synthesizable vhdl design? Any inputs would be welcome.

I am using the XST Synthesizer that comes with Xilinx ISE. I am indexing both i and j, as my matrix dimension is m * n * 32.

My net a_in in the entity

    a_in: in  matrix (0 to size - 1, 0 to size - 1);

got modified to

    a_in : in STD_LOGIC_VECTOR3 ( 1 downto 0 , 1 downto 0 , 31 downto 0 );

In my program, I access values from the matrix inside two generate statements for k and m as:

    add_instx: add 
            port map (
                a => a_in(k,m),
                b => b_in(k,m),
                clk => clk,
                sclr => clr,
                ce => start,
                result => temp_out(k,m),
                rdy => add_over(k,m)
            );

My test bench input for a_in is given as

    a_in <= (("00111111100000000000000000000000", "00000000000000000000000000000000"),("00000000000000000000000000000000", "00111111100000000000000000000000"));

My synthesis generated warnings of the type: Xst:387 - The KEEP property attached to the net may hinder timing optimization. You may achieve better results by removing this property. However, I have not set any keep property and I am not sure where to look for this property. Please help! Thanks a lot.

I apologize for not adding the complete code. Please find below the code and package.

    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    use IEEE.NUMERIC_STD.ALL;
    use work.mat_pak.all;


    entity newproj is
        generic ( size:  natural := 2 );
        port ( 
            clk:                in  std_logic;
            clr:                in  std_logic;
            start:              in  std_logic;
            a_in:               in  matrix (0 to size - 1, 0 to size - 1);
            b_in:               in  matrix (0 to size - 1, 0 to size - 1);
            aplusb:             out matrix (0 to size - 1, 0 to size - 1);
            parallel_add_done:  out std_logic);
    end newproj;

    architecture Behavioral of newproj is
    COMPONENT add --This is a 32 bit floating point add IP core
      PORT (
        a : IN STD_LOGIC_VECTOR(31 DOWNTO 0);
        b : IN STD_LOGIC_VECTOR(31 DOWNTO 0);
        clk : IN STD_LOGIC;
         sclr : IN STD_LOGIC;
         ce : IN STD_LOGIC;
        result : OUT STD_LOGIC_VECTOR(31 DOWNTO 0);
         rdy: OUT STD_LOGIC
      );
    END COMPONENT;

        signal temp_out: matrix (0 to size - 1, 0 to size - 1) :=  (others => (others => (others => '0')));  
        signal add_over: bmatrix (0 to size - 1, 0 to size - 1) := (others => (others => '0'));  

    begin

        g0: 
            for k in  0 to mat_dim generate 
            g0x: 
                for m in 0 to mat_dim generate
                add_instx: add 
                    port map (
                        a => a_in(k,m),
                        b => b_in(k,m),
                        clk => clk,
                        sclr => clr,
                        ce => start,
                        result => temp_out(k,m),
                        rdy => add_over(k,m)
                    );
            end generate;   
        end generate;

        aplusb <= temp_out;

         p1_add:
        process (add_over)
            variable check_all_done: std_logic;
        begin
            check_all_done := '1';
            for k in 0 to mat_dim loop
                for m in 0 to mat_dim loop
                    check_all_done := check_all_done and add_over(k)(m);
                end loop;
            end loop;
            parallel_add_done <= check_all_done;
        end process;

    end Behavioral;

The package used here is:

    library IEEE;
    use IEEE.STD_LOGIC_1164.all;
    use IEEE.NUMERIC_STD.ALL;


    package mat_pak is

        CONSTANT mat_dim : natural := 2;

        type matrix  is array (natural range <>, natural range <>)
                   of std_logic_vector(31 downto 0);
        type bmatrix is array (natural range <>, natural range <>) 
                   of std_logic;                      


    end mat_pak;

The post synthesis simulation model file modified the entity in terms of ordering and data type, on its own. The entity looks as below:

    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    library UNISIM;
    use UNISIM.VCOMPONENTS.ALL;
    use UNISIM.VPKG.ALL;

    entity newproj is
      port (
        clk : in STD_LOGIC := 'X'; 
        clr : in STD_LOGIC := 'X'; 
        start : in STD_LOGIC := 'X'; 
        parallel_add_done : out STD_LOGIC; 
        a_in : in STD_LOGIC_VECTOR3 ( 1 downto 0 , 1 downto 0 , 31 downto 0 ); 
        b_in : in STD_LOGIC_VECTOR3 ( 1 downto 0 , 1 downto 0 , 31 downto 0 ); 
        aplusb : out STD_LOGIC_VECTOR3 ( 1 downto 0 , 1 downto 0 , 31 downto 0 ) 
      );
    end newproj;
Intermingle answered 4/6, 2015 at 18:18 Comment(7)
What synthesis tool are you using?Serotherapy
Show us how you are trying to use the said multi-dimensional array. Are you trying to index both i,j indicies? mysignal(i)(j)? Or something else?Murphey
I'd amplify Josh's comment. show us where and how i and j are used and declared. The above isn't a Minimal, Complete, and Verifiable example. Show us the nested generate statements, too. Your problem likely stems from never assigning parts of your matrix, it sounds like something is begin eaten because it's got a fixed value. See the Constraints Guide (cgd.pdf). See Keep and Keep Hierarchy (in particular). Slices are only of one dimensional arrays (See IEEE Std 1076-2008, 8.5 Slice names).Raddled
Reading Paebbels plug for his PoC library (and he made no claim that was incorrect in his answer), it brings attention to the lack of a declaration for STD_LOGIC_VECTOR3 the type mark in the (signal declaration for a_in. Your question is not a Minimal, Complete, and Verifiable example. The problem can't be reproduced nor fully understood.Raddled
Apologies Mr. David Koontz for incomplete information in my question. I have added the program and the package data. The std_logic_vector3 was auto-generated after synthesis by the Xilinx synthesis tool. As suggested by you I will check the constraints guide as well. Any more inputs would be great! Thank you very much.Intermingle
More remarks: You should not initialize signals, unless they map to registers: temp_out, add_over.Ovate
Analyzing your pre-synthesis code shows an error in process p1_add for the expression add_over(k)(m) on the right hand side of the assignment to variable check_all_done. signal add_over is of type bmatrix which has two indices while the expression shows one and an index into add_over(k), missing the second index. The expression should be add_over(k,m) as in other usage. Your problem may stem in part from Xilinx' willingness to synthesis VHDL code containing an error. The accepted answer doesn't appear to directly relate to the problem. Is this the actual code synthesized?Raddled
O
12

Your first array is not a multi dimensional array, it's a 2-times nested 1 dimensional array.

Your example:

type t11 is array (0 to c1_r2) of std_logic_vector(31 downto 0);
type t1 is array (0 to r1) of t11;

This definition is more clear:

subtype t_dim1 is std_logic_vector(31 downto 0);
type t_dim1_vector is array(natural range <>) of t_dim1;
subtype t_dim2 is t_dim1_vector(0 to c1_r2);
type t_dim3_vector is array(natural range <>) of t_dim2;
subtype t_dim3 is t_dim3_vector(0 to r1);

You can access this structure by indexing each dimension:

signal matrix3 : t_dim3;
signal matrix2 : t_dim2;
signal matrix1 : t_dim1;
signal slv : std_logic_vector(31 downto 0);
signal sl : std_logic;

matrix2 <= matrix3(i);
matrix1 <= matrix2(j);
matrix1 <= matrix3(i)(j);
slv <= matrix3(i)(j);
sl <= matrix3(i)(j)(k);

You can also slice each dimension:

signal matrix3 : t_dim3;
signal slice3 : t_dim3_vector(0 to 3);
signal slice2 : t_dim2_vector(0 to 3);
signal slv : std_logic_vector(7 downto 0);

slice3 <= matrix3(4 to 7);
slice2 <= matrix3(i)(2 to 5);
slice2 <= slice3(i)(2 to 5);
slv <= matrix3(i)(j)(15 downto 8);

Your second example:

type matrix is array (natural range <>, natural range <>) of std_logic_vector(31 downto 0);

This is a 2-dimensional array with a nested 1-dimensional array. This structure can be accessed as follows:

signal mat : matrix(0 to r1, p to c1_r2);
signal slv : std_logic_vector(31 downto 0);
signal sl : std_logic;

slv <= mat(i, j);
sl <= mat(i, j)(k);

Since VHDL-2008 slicing is also allowed in multi dimensional array. Before VHDL-2008 you have to employ functions for this job.

Have a look into my PoC.vectors package to see ways on how you can handle 1- and multi dimensional arrays.

Ovate answered 4/6, 2015 at 22:53 Comment(5)
Notice Soumya didn't show the declaration for the type mark STD_LOGIC_VECTOR3 used in the declaration for a_in. It's likely he's specifying a slice of a slice, not legal - (8.5 "The prefix of a slice shall be appropriate for a one-dimensional array object. The base type of this array type is the type of the slice.") A slice of a slice has no declared type of a one-dimensional array object, and if you provided one no type conversion (noting your mention of using functions) or means other than aggregation of the created slice of a slice type, which would mean an aggregate of elements.Raddled
And this doesn't demean the validity of the methodology outlined in your answer. The problem is that the question is unclear.Raddled
Thank you very much Mr. Paebbels. I shall look at your PoC.vectors package. I am still not clear with the definitions and will have to go through your post in detail. Thank you once again.Intermingle
Some languages have no multi dimensional arrays. In this case it can be emulated by embedding whole arrays as elements of another array. The user can use this (in my case 2D) structure for x and y abstraction. The difference to real multi dimensional arrays is the internal structure in memory and the element indexing in the language: (x)(y) -> select an entire column and then an element in that column vs. (x, y) - > select an element in column x and row y.Ovate
See these 2 images as comparison: 1D array in 1D array and real 2D arrayOvate
O
0

In the mean time Xilinx has added some information in it's [Vivado synthesis user guide (UG901)](xilinx website offline at time of writing)

For example:

-- 3-D Ram Inference Example (Single port)
-- Compile this file in VHDL2008 mode
-- File:rams_sp_3d.vhd
library ieee;
use ieee.std_logic_1164.all;

package mypack is
    type myarray_t is array(integer range<>) of std_logic_vector;
    type mem_t is array(integer range<>) of myarray_t;
end package;

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.mypack.all;

entity rams_sp_3d is
    generic (
        NUM_RAMS : integer := 2;
        A_WID : integer := 10;
        D_WID : integer := 32
    );
    port (
        clk : in std_logic;
        we : in std_logic_vector(NUM_RAMS-1 downto 0);
        ena : in std_logic_vector(NUM_RAMS-1 downto 0);
        addr : in myarray_t(NUM_RAMS-1 downto 0)(A_WID-1 downto 0);
        din : in myarray_t(NUM_RAMS-1 downto 0)(D_WID-1 downto 0);
        dout : out myarray_t(NUM_RAMS-1 downto 0)(D_WID-1 downto 0)
    );
end rams_sp_3d;

architecture arch of rams_sp_3d is
    signal mem : mem_t(NUM_RAMS-1 downto 0)(2**A_WID-1 downto 0)(D_WID-1 downto 0);
begin
    process(clk)
    begin
        if(clk’event and clk=’1’) then
            for i in 0 to NUM_RAMS-1 loop
                if(ena(i) = ‘1’) then
                    if(we(i) = ‘1’) then
                        mem(i)(to_integer(unsigned(addr(i)))) <= din(i);
                    end if;
                    dout(i) <= mem(i)(to_integer(unsigned(addr(i))));
                end if;
            end loop;
        end if;
    end process;
end arch;
Openwork answered 16/3, 2017 at 10:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.