-- 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. -- -- Note: The LOCK signal is ignored, but the crossbar switch will not reassign -- the current bus master until it relinquishes CYC anyways. 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_v3_slave_in_vector(masters - 1 downto 0); slave_o : out wishbone_v3_slave_out_vector(masters - 1 downto 0); -- Slave connections (INTERCON is a master) master_i : in wishbone_v3_master_in_vector(slaves - 1 downto 0); master_o : out wishbone_v3_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_v3_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 if 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_v3_master_out; signal slave_i : in wishbone_v3_slave_in_vector(masters - 1 downto 0); signal granted : in matrix; slave : integer) is variable acc : wishbone_v3_master_out; variable granted_address : wishbone_address; variable granted_bte : wishbone_burst_type; variable granted_cti : wishbone_cycle_type; variable granted_select : wishbone_byte_select; variable granted_data : wishbone_data; begin acc := ( CYC => '0', STB => '0', ADR => (others => '0'), BTE => (others => '0'), CTI => (others => '0'), LOCK => '0', SEL => (others => '0'), WE => '0', DAT => (others => '0')); for master in masters - 1 downto 0 loop granted_address := (others => granted(master, slave)); granted_bte := (others => granted(master, slave)); granted_cti := (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), BTE => acc.BTE or (slave_i(master).BTE and granted_bte), CTI => acc.CTI or (slave_i(master).CTI and granted_cti), SEL => acc.SEL or (slave_i(master).SEL and granted_select), WE => acc.WE or (slave_i(master).WE and granted(master, slave)), LOCK => acc.LOCK or (slave_i(master).LOCK 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 acc.ADR := acc.ADR and not mask(slave); -- Address masking o <= acc; end slave_logic; -- Select the slave pins the master will receive procedure master_logic(signal o : out wishbone_v3_slave_out; signal master_i : in wishbone_v3_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_v3_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;