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.)
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.
The simulation results of the output ("ys") after accessing the 2D-array "row_exchange". Suddenly there are conflicts on some bits
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.
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.