243 lines
8.8 KiB
VHDL
243 lines
8.8 KiB
VHDL
-- An MxS Wishbone crossbar switch.
|
|
--
|
|
-- All masters, slaves, and the crossbar itself must share the same WB clock.
|
|
-- All participants must support the same data bus width.
|
|
--
|
|
-- If a master raises STB_O with an address not mapped by the crossbar,
|
|
-- ERR_I will be raised. If the crossbar has overlapping address ranges,
|
|
-- the lowest numbered slave is selected. If two masters address the same
|
|
-- slave simultaneously, the lowest numbered master is granted access.
|
|
--
|
|
-- The implementation of this crossbar locks a master to a slave so long as
|
|
-- CYC_O is held high. If the master tries to address outside the slave's
|
|
-- address range, ERR_I will be raised.
|
|
--
|
|
-- WARN: !!! NO LICENSE SPECIFIED !!!
|
|
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
use ieee.numeric_std.all;
|
|
use work.wishbone_package.all;
|
|
|
|
entity crossbar is
|
|
generic(
|
|
masters : integer := 2;
|
|
slaves : integer := 1;
|
|
async : boolean := false
|
|
);
|
|
port(
|
|
clk : in std_logic;
|
|
rst : in std_logic;
|
|
-- Master connections (INTERCON is a slave)
|
|
slave_i : in wishbone_slave_in_vector(masters - 1 downto 0);
|
|
slave_o : out wishbone_slave_out_vector(masters - 1 downto 0);
|
|
-- Slave connections (INTERCON is a master)
|
|
master_i : in wishbone_master_in_vector(slaves - 1 downto 0);
|
|
master_o : out wishbone_master_out_vector(slaves - 1 downto 0);
|
|
-- Address of the slaves connected
|
|
address : in wishbone_address_vector(slaves - 1 downto 0);
|
|
mask : in wishbone_address_vector(slaves - 1 downto 0)
|
|
);
|
|
end crossbar;
|
|
|
|
architecture rtl of crossbar is
|
|
type matrix is array (masters - 1 downto 0, slaves downto 0) of std_logic;
|
|
type column is array (masters - 1 downto 0) of std_logic;
|
|
type row is array (slaves downto 0) of std_logic;
|
|
|
|
-- synchronous signals:
|
|
signal previous : matrix; -- Previously connected pairs 1 -
|
|
-- (a)synchronous signals (depending on generic):
|
|
signal granted : matrix; -- The connections to form this cycle 1 selected previous busy
|
|
signal issue : column; -- Did last cycle issue a request
|
|
|
|
procedure main_logic(
|
|
signal granted : out matrix;
|
|
signal issue : out column;
|
|
signal slave_i : in wishbone_slave_in_vector(masters - 1 downto 0);
|
|
signal previous : in matrix) is
|
|
variable acc, tmp : std_logic;
|
|
variable request : matrix; -- Which slaves do the masters address log(S) -
|
|
variable selected : matrix; -- Which master wins arbitration log(M) request
|
|
variable sbusy : row; -- Does the slave's previous connection persist?
|
|
variable mbusy : column; -- Does the master's previous connection persist?
|
|
begin
|
|
-- A slave is busy iff it services an in-progress cycle
|
|
for slave in slaves - 1 downto 0 loop
|
|
acc := '0';
|
|
for master in masters - 1 downto 0 loop
|
|
acc := acc or (previous(master, slave) and slave_i(master).CYC);
|
|
end loop;
|
|
sbusy(slave) := acc;
|
|
end loop;
|
|
sbusy(slaves) := '0'; -- Special case because the 'error device' supports multiple masters
|
|
|
|
-- A master is busy iff it services an in-progress cycle
|
|
for master in masters - 1 downto 0 loop
|
|
acc := '0';
|
|
for slave in slaves downto 0 loop
|
|
acc := acc or previous(master, slave);
|
|
end loop;
|
|
mbusy(master) := acc and slave_i(master).CYC;
|
|
end loop;
|
|
|
|
-- Decode the request address to see if master wants access
|
|
for master in masters - 1 downto 0 loop
|
|
acc := '0';
|
|
for slave in slaves - 1 downto 0 loop
|
|
if (slave_i(master).ADR and mask(slave)) = address(slave) then
|
|
tmp := '1';
|
|
else
|
|
tmp := '0';
|
|
end if;
|
|
acc := acc or tmp;
|
|
request(master, slave) := slave_i(master).CYC and slave_i(master).STB and tmp;
|
|
end loop;
|
|
-- If no slaves match request, bind to 'error device'
|
|
request(master, slaves) := slave_i(master).CYC and slave_i(master).STB and not acc;
|
|
end loop;
|
|
|
|
-- Arbitrate among the requesting masters
|
|
-- Policy: lowest numbered master first
|
|
for slave in slaves - 1 downto 0 loop
|
|
acc := '0';
|
|
-- It is possible to break the chain of LUTs here using a sort of kogge-stone network
|
|
-- This probably only makes sense if you have more than 32 masters
|
|
for master in 0 to masters - 1 loop
|
|
selected(master, slave) := request(master, slave) and not acc;
|
|
acc := acc or request(master, slave);
|
|
end loop;
|
|
end loop;
|
|
|
|
-- Multiple masters can be granted access to the 'error device'
|
|
for master in masters - 1 downto 0 loop
|
|
selected(master, slaves) := request(master, slaves);
|
|
end loop;
|
|
|
|
-- Determine the master granted access
|
|
-- Policy: if cycle still in progress, preserve the previous choice
|
|
for slave in slaves downto 0 loop
|
|
for master in masters - 1 downto 0 loop
|
|
if sbusy(slave) = '1' or mbusy(master) = '1' then
|
|
granted(master, slave) <= previous(master, slave);
|
|
else
|
|
granted(master, slave) <= selected(master, slave);
|
|
end if;
|
|
end loop;
|
|
end loop;
|
|
|
|
-- Record strobe status for virtual error device
|
|
for master in masters - 1 downto 0 loop
|
|
issue(master) <= slave_i(master).CYC and slave_i(master).STB;
|
|
end loop;
|
|
end main_logic;
|
|
|
|
-- Select the master pins the slave will receive
|
|
procedure slave_logic(signal o : out wishbone_master_out;
|
|
signal slave_i : in wishbone_slave_in_vector(masters - 1 downto 0);
|
|
signal granted : in matrix;
|
|
slave : integer) is
|
|
variable acc : wishbone_master_out;
|
|
variable granted_address : wishbone_address;
|
|
variable granted_select : wishbone_byte_select;
|
|
variable granted_data : wishbone_data;
|
|
begin
|
|
acc := (
|
|
CYC => '0',
|
|
STB => '0',
|
|
ADR => (others => '0'),
|
|
SEL => (others => '0'),
|
|
WE => '0',
|
|
DAT => (others => '0'));
|
|
|
|
for master in masters - 1 downto 0 loop
|
|
granted_address := (others => granted(master, slave));
|
|
granted_select := (others => granted(master, slave));
|
|
granted_data := (others => granted(master, slave));
|
|
acc := (
|
|
CYC => acc.CYC or (slave_i(master).CYC and granted(master, slave)),
|
|
STB => acc.STB or (slave_i(master).STB and granted(master, slave)),
|
|
ADR => acc.ADR or (slave_i(master).ADR and granted_address),
|
|
SEL => acc.SEL or (slave_i(master).SEL and granted_select),
|
|
WE => acc.WE or (slave_i(master).WE and granted(master, slave)),
|
|
DAT => acc.DAT or (slave_i(master).DAT and granted_data));
|
|
end loop;
|
|
-- acc.ADR := std_logic_vector(unsigned(acc.ADR) - unsigned(address(slave))); -- Address translation
|
|
o <= acc;
|
|
end slave_logic;
|
|
|
|
-- Select the slave pins the master will receive
|
|
procedure master_logic(signal o : out wishbone_slave_out;
|
|
signal master_i : in wishbone_master_in_vector(slaves - 1 downto 0);
|
|
signal issue : in column;
|
|
signal previous : in matrix;
|
|
signal granted : in matrix;
|
|
master : integer) is
|
|
variable acc : wishbone_slave_out;
|
|
variable granted_data : wishbone_data;
|
|
begin
|
|
acc := (
|
|
ACK => '0',
|
|
ERR => issue(master) and previous(master, slaves), -- Error device connected and strobed?
|
|
RTY => '0',
|
|
STALL => granted(master, slaves),
|
|
DAT => (others => '0'));
|
|
|
|
-- We use inverted logic on STALL so that if no slave granted => stall
|
|
for slave in slaves - 1 downto 0 loop
|
|
granted_data := (others => granted(master, slave));
|
|
acc := (
|
|
ACK => acc.ACK or (master_i(slave).ACK and granted(master, slave)),
|
|
ERR => acc.ERR or (master_i(slave).ERR and granted(master, slave)),
|
|
RTY => acc.RTY or (master_i(slave).RTY and granted(master, slave)),
|
|
STALL => acc.STALL or (not master_i(slave).STALL and granted(master, slave)),
|
|
DAT => acc.DAT or (master_i(slave).DAT and granted_data));
|
|
end loop;
|
|
acc.STALL := not acc.STALL;
|
|
|
|
o <= acc;
|
|
end master_logic;
|
|
begin
|
|
-- If async determine granted devices
|
|
granted_matrix : if async generate
|
|
main_logic(granted, issue, slave_i, previous);
|
|
end generate;
|
|
|
|
granted_driver : if not async generate
|
|
process(clk)
|
|
begin
|
|
if rising_edge(clk) then
|
|
if rst = '1' then
|
|
granted <= (others => (others => '0'));
|
|
issue <= (others => '0');
|
|
else
|
|
main_logic(granted, issue, slave_i, previous);
|
|
end if;
|
|
end if;
|
|
end process;
|
|
end generate;
|
|
|
|
-- Make the slave connections
|
|
slave_matrix : for slave in slaves - 1 downto 0 generate
|
|
slave_logic(master_o(slave), slave_i, granted, slave);
|
|
end generate;
|
|
|
|
-- Make the master connections
|
|
master_matrix : for master in masters - 1 downto 0 generate
|
|
master_logic(slave_o(master), master_i, issue, previous, granted, master);
|
|
end generate;
|
|
|
|
-- Store the current grant to the previous registers
|
|
main : process(clk)
|
|
begin
|
|
if rising_edge(clk) then
|
|
if rst = '1' then
|
|
previous <= (others => (others => '0'));
|
|
else
|
|
previous <= granted;
|
|
end if;
|
|
end if;
|
|
end process main;
|
|
|
|
end rtl;
|