Search code examples
vhdlfpgamodelsim

VHDL - access to 2D array of std_logic_vectors gives unexpected bus conflict


Background: I'm working on a generic systolic array. I created an entity called "systolic_row" to create one row of the systolic array. The data is handed from one row to the next using an array of std_logic_vectors (type "row_exchange_array"). This 1D array is packed into an other array with the size of all generated rows+2 (type "exchange_array")
The reason for +2 is described next: The first entry of the 2D array are the top inputs to the systolic array, which is always set to 0. The other additional entry has the following reason: The most bottom row of the systolic array consists of accumulators (additional to the already generated rows) (enitiy "ACC"). The accumulators work on the same 2D-array structure as the rows. The overall outputs of the systolic array is the last entry of this 2d-array (equal to 1D-array of std_logic_vectors (type "row_exchange_array")).

Overview of the used types in figure below. Blue are the left inputs of the systolic array. Red is the 1D-array, that passes information from one row to the next. Black unifies all red 1D-arrays to a 2D-array that handles all exchanges from top to bottom of systolic array. (So all black lines are red lines at the same time.)

Overview of types

Problem: The results in the 2D array are calculated correctly, but as soon as I access the last entry, to extract the overall result and hand it to the output, I get Bus conflicts, that I can not explain.

The Simulation was conducted with a ROW_WIDTH of 3 and only one row generated (NUM_ROWS = 1).

"row_exchange(0)" are the top inputs of the systolic array, which are always set to 0.
"row_exchange(1)" are the outputs of the only generated row and the inputs to the ACCs.
"row_exchange(2)" are the outputs of the ACCs and the overall output of the systolic array ("ys")

Here the simulation result of the 2D array called "row_exchange" ("ys" is the output). Here the results are correct and as expected. Simulation Result of 2D array

The simulation results of the output ("ys") after accessing the 2D-array "row_exchange". Suddenly there are conflicts on some bits Simulation result of the output extracted from 2D array

VHDL Code (only an excerpt because the whole reproduceable code is way to long):

All Generics are declared as Constants in a configuration file "config_pkg.vhd"

-- load configuration file (Generics and data type declaration )
use work.config_pkg.all;

entity systolic_array is
port (
    clk : in std_logic;
    rstn : in std_logic;

    -- "left side" inputs of the systolic array (arrays with size of number of rows, declared in conifguration-vhdl-file)
    xs : in input_vector_array;
    coeffs : in input_vector_array;
    ies : in input_vector_array;
    
    -- enable to declare then valid input data is provided
    en : in std_logic;
    
    -- output of systolic array (1D array of std_logic_vectors -> also declared in configuration file)
    ys : out row_exchange_array
);
end entity;

architecture behav of systolic_array is

    -- array to handle data from one row to the next
    -- JUST HERE FOR BETTER UNDERSTANDING, ACCTUALLY DECLARED IN CONFIGURATION FILE BECAUSE IT IS THE SAME TYPE AS THE OUTPUT "ys"
    type row_exchange_array is array (0 to ROW_WIDTH-1) of std_logic_vector(DATA_WIDTH-1 downto 0);

    -- 2D array to handle all exchanges in the systolic array (from row to row, ACCs and outputs)
    type exchange_array is array (0 to NUM_ROWS+1) of row_exchange_array;
    signal row_exchange : exchange_array;

begin

    -- first row gets all ys set to 0 as input
    row_exchange(0) <= (others => (others => '0'));
    -- last row is connected to output
    -- THIS IS THE THE PROBLEM SEEMS TO OCCUR
    ys <= row_exchange(NUM_ROWS+1);

    -- generation of the rows
    gen_rows : for j in 0 to NUM_ROWS-1 generate

        inst_row: systolic_row
        port map (
            clk   => clk,              -- clock
            i     => ies(j),           -- "left side" input to systolic array row
            coef  => coeffs(j),        -- "left side" input to systolic array row
            x     => xs(j),            -- "left side" input to systolic array row
            y_in  => row_exchange(j),  -- "top" input to systolic array row
            y_out => row_exchange(j+1) -- "bottom" output of systolic array row
        );

    end generate gen_rows;

    -- generation of the accumulators as the last row of the systolic array
    gen_accs : for j in 0 to ROW_WIDTH-1 generate

        inst_acc: ACC
        port map (
            clk => clk,                        -- clock
            rstn => rstn,                      -- reset (low active)
            en => en_shift_regs(NUM_ROWS+j),   -- enable for the accumulator (signal declaration not shown here, because not part of the problem, I guess)
            T   => row_exchange(NUM_ROWS)(j),  -- input (std_logic_vector)
            y   => row_exchange(NUM_ROWS+1)(j) -- ouput (std_logic_vector)
        );
    
    end generate gen_accs;

end architecture;

I already tried to access/write all std_logic_vectors of the outputs ("ys") individually using a for loop to see if the problem is the access of the 2D-array itself, but it does not seem to be.

I hope my explanation is clear enough. Thank you very much for your help and for trying to understand the complex code. If further Information is needed I am happy to provide it.


Solution

  • I was about to post an little toy example where the same mistake occurs, when I realized the problem.

    The cause of the bus conflict was not in the design files, but in the testbench, which I hastily created for quick compilation checking. The problem was that I was pushing values into the signals that were connected to the outputs of the UUT (see the commented part below, also only an excerpt).

    -- Testbench signals to connect to UUT ports
    signal clk : std_logic := '1';
    signal rstn : std_logic := '0';
    signal en : std_logic := '0';
    signal xs : input_vector_array;
    signal coeffs : input_vector_array;
    signal ies : input_vector_array;
    signal ys : row_exchange_array;
    
    begin
    
    -- ROW WIDTH AND NUMBER OF ROWS IS SET IN CONFIGURATION FILE
    inst_UUT: systolic_array
    generic map (
        DATA_WIDTH => C_DATA_WIDTH
    )
    port map (
        clk => clk,
        rstn => rstn,
        en => en,
        xs => xs,
        coeffs => coeffs,
        ies => ies,
        ys => ys
    );
    
    clk <= not clk after C_CLOCK_PERIOD/2;
    
    -- THOSE ASSIGNMENTS WERE RESPONSIBLE FOR THE BUS CONFLICT
    --ys(0) <= x"01";
    --ys(1) <= x"00";
    --ys(2) <= x"01";
    

    Thank you very much for your help.