802 lines
41 KiB
VHDL
802 lines
41 KiB
VHDL
-- #################################################################################################
|
|
-- # << NEORV32 - Processor Bus Infrastructure: 2-to-1 Bus Switch >> #
|
|
-- # ********************************************************************************************* #
|
|
-- # Allows to access a single device bus X by two controller ports A and B. #
|
|
-- # Controller port A has priority over controller port B. #
|
|
-- # ********************************************************************************************* #
|
|
-- # BSD 3-Clause License #
|
|
-- # #
|
|
-- # The NEORV32 RISC-V Processor, https://github.com/stnolting/neorv32 #
|
|
-- # Copyright (c) 2024, Stephan Nolting. All rights reserved. #
|
|
-- # #
|
|
-- # Redistribution and use in source and binary forms, with or without modification, are #
|
|
-- # permitted provided that the following conditions are met: #
|
|
-- # #
|
|
-- # 1. Redistributions of source code must retain the above copyright notice, this list of #
|
|
-- # conditions and the following disclaimer. #
|
|
-- # #
|
|
-- # 2. Redistributions in binary form must reproduce the above copyright notice, this list of #
|
|
-- # conditions and the following disclaimer in the documentation and/or other materials #
|
|
-- # provided with the distribution. #
|
|
-- # #
|
|
-- # 3. Neither the name of the copyright holder nor the names of its contributors may be used to #
|
|
-- # endorse or promote products derived from this software without specific prior written #
|
|
-- # permission. #
|
|
-- # #
|
|
-- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS #
|
|
-- # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF #
|
|
-- # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE #
|
|
-- # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, #
|
|
-- # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE #
|
|
-- # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED #
|
|
-- # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING #
|
|
-- # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED #
|
|
-- # OF THE POSSIBILITY OF SUCH DAMAGE. #
|
|
-- #################################################################################################
|
|
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
use ieee.numeric_std.all;
|
|
|
|
library neorv32;
|
|
use neorv32.neorv32_package.all;
|
|
|
|
entity neorv32_bus_switch is
|
|
generic (
|
|
PORT_A_READ_ONLY : boolean; -- set if port A is read-only
|
|
PORT_B_READ_ONLY : boolean -- set if port B is read-only
|
|
);
|
|
port (
|
|
clk_i : in std_ulogic; -- global clock, rising edge
|
|
rstn_i : in std_ulogic; -- global reset, low-active, async
|
|
a_req_i : in bus_req_t; -- host port A: request bus
|
|
a_rsp_o : out bus_rsp_t; -- host port A: response bus
|
|
b_req_i : in bus_req_t; -- host port B: request bus
|
|
b_rsp_o : out bus_rsp_t; -- host port B: response bus
|
|
x_req_o : out bus_req_t; -- device port request bus
|
|
x_rsp_i : in bus_rsp_t -- device port response bus
|
|
);
|
|
end neorv32_bus_switch;
|
|
|
|
architecture neorv32_bus_switch_rtl of neorv32_bus_switch is
|
|
|
|
-- access arbiter --
|
|
type arbiter_t is record
|
|
state, state_nxt : std_ulogic_vector(1 downto 0);
|
|
a_req, b_req : std_ulogic;
|
|
sel, stb : std_ulogic;
|
|
end record;
|
|
signal arbiter : arbiter_t;
|
|
|
|
-- FSM states --
|
|
constant IDLE : std_ulogic_vector(1 downto 0) := "00";
|
|
constant BUSY_A : std_ulogic_vector(1 downto 0) := "01";
|
|
constant BUSY_B : std_ulogic_vector(1 downto 0) := "10";
|
|
|
|
begin
|
|
|
|
-- Access Arbiter -------------------------------------------------------------------------
|
|
-- -------------------------------------------------------------------------------------------
|
|
arbiter_sync: process(rstn_i, clk_i)
|
|
begin
|
|
if (rstn_i = '0') then
|
|
arbiter.state <= IDLE;
|
|
arbiter.a_req <= '0';
|
|
arbiter.b_req <= '0';
|
|
elsif rising_edge(clk_i) then
|
|
arbiter.state <= arbiter.state_nxt;
|
|
arbiter.a_req <= (arbiter.a_req or a_req_i.stb) and (not arbiter.state(0)); -- clear STB buffer in BUSY_A
|
|
arbiter.b_req <= (arbiter.b_req or b_req_i.stb) and (not arbiter.state(1)); -- clear STB buffer in BUSY_B
|
|
end if;
|
|
end process arbiter_sync;
|
|
|
|
-- fsm --
|
|
arbiter_comb: process(arbiter, a_req_i, b_req_i, x_rsp_i)
|
|
begin
|
|
-- defaults --
|
|
arbiter.state_nxt <= arbiter.state;
|
|
arbiter.sel <= '0';
|
|
arbiter.stb <= '0';
|
|
|
|
-- state machine --
|
|
case arbiter.state is
|
|
|
|
when BUSY_A => -- port A access in progress
|
|
-- ------------------------------------------------------------
|
|
arbiter.sel <= '0';
|
|
if (x_rsp_i.err = '1') or (x_rsp_i.ack = '1') then
|
|
arbiter.state_nxt <= IDLE;
|
|
end if;
|
|
|
|
when BUSY_B => -- port B access in progress
|
|
-- ------------------------------------------------------------
|
|
arbiter.sel <= '1';
|
|
if (x_rsp_i.err = '1') or (x_rsp_i.ack = '1') then
|
|
arbiter.state_nxt <= IDLE;
|
|
end if;
|
|
|
|
when others => -- IDLE: wait for requests
|
|
-- ------------------------------------------------------------
|
|
if (a_req_i.stb = '1') or (arbiter.a_req = '1') then -- request from port A (prioritized)?
|
|
arbiter.sel <= '0';
|
|
arbiter.stb <= '1';
|
|
arbiter.state_nxt <= BUSY_A;
|
|
elsif (b_req_i.stb = '1') or (arbiter.b_req = '1') then -- request from port B?
|
|
arbiter.sel <= '1';
|
|
arbiter.stb <= '1';
|
|
arbiter.state_nxt <= BUSY_B;
|
|
end if;
|
|
|
|
end case;
|
|
end process arbiter_comb;
|
|
|
|
|
|
-- Request Switch -------------------------------------------------------------------------
|
|
-- -------------------------------------------------------------------------------------------
|
|
x_req_o.addr <= a_req_i.addr when (arbiter.sel = '0') else b_req_i.addr;
|
|
x_req_o.rvso <= a_req_i.rvso when (arbiter.sel = '0') else b_req_i.rvso;
|
|
x_req_o.priv <= a_req_i.priv when (arbiter.sel = '0') else b_req_i.priv;
|
|
x_req_o.src <= a_req_i.src when (arbiter.sel = '0') else b_req_i.src;
|
|
x_req_o.rw <= a_req_i.rw when (arbiter.sel = '0') else b_req_i.rw;
|
|
x_req_o.fence <= a_req_i.fence or b_req_i.fence; -- propagate any fence operations
|
|
|
|
x_req_o.data <= b_req_i.data when PORT_A_READ_ONLY else
|
|
a_req_i.data when PORT_B_READ_ONLY else
|
|
a_req_i.data when (arbiter.sel = '0') else b_req_i.data;
|
|
|
|
x_req_o.ben <= b_req_i.ben when PORT_A_READ_ONLY else
|
|
a_req_i.ben when PORT_B_READ_ONLY else
|
|
a_req_i.ben when (arbiter.sel = '0') else b_req_i.ben;
|
|
|
|
x_req_o.stb <= arbiter.stb;
|
|
|
|
|
|
-- Response Switch ------------------------------------------------------------------------
|
|
-- -------------------------------------------------------------------------------------------
|
|
a_rsp_o.data <= x_rsp_i.data;
|
|
a_rsp_o.ack <= x_rsp_i.ack when (arbiter.sel = '0') else '0';
|
|
a_rsp_o.err <= x_rsp_i.err when (arbiter.sel = '0') else '0';
|
|
|
|
b_rsp_o.data <= x_rsp_i.data;
|
|
b_rsp_o.ack <= x_rsp_i.ack when (arbiter.sel = '1') else '0';
|
|
b_rsp_o.err <= x_rsp_i.err when (arbiter.sel = '1') else '0';
|
|
|
|
|
|
end neorv32_bus_switch_rtl;
|
|
|
|
|
|
-- ############################################################################################################################
|
|
-- ############################################################################################################################
|
|
|
|
|
|
-- #################################################################################################
|
|
-- # << NEORV32 - Processor Bus Infrastructure: Section Gateway >> #
|
|
-- # ********************************************************************************************* #
|
|
-- # Bus gateway to distribute the core's access to the processor's main address sections: #
|
|
-- # -> IMEM - internal instruction memory #
|
|
-- # -> DMEM - internal data memory #
|
|
-- # -> XIP - memory-mapped XIP flash #
|
|
-- # -> BOOT - internal bootloader ROM #
|
|
-- # -> IO - internal IO devices #
|
|
-- # All accesses that do not match any of these sections are redirected to the "external" port. #
|
|
-- # The gateway-internal bus monitor ensures that all processor-internal accesses are completed #
|
|
-- # within a fixed time window; a bus error is triggered otherwise. #
|
|
-- # ********************************************************************************************* #
|
|
-- # BSD 3-Clause License #
|
|
-- # #
|
|
-- # The NEORV32 RISC-V Processor, https://github.com/stnolting/neorv32 #
|
|
-- # Copyright (c) 2024, Stephan Nolting. All rights reserved. #
|
|
-- # #
|
|
-- # Redistribution and use in source and binary forms, with or without modification, are #
|
|
-- # permitted provided that the following conditions are met: #
|
|
-- # #
|
|
-- # 1. Redistributions of source code must retain the above copyright notice, this list of #
|
|
-- # conditions and the following disclaimer. #
|
|
-- # #
|
|
-- # 2. Redistributions in binary form must reproduce the above copyright notice, this list of #
|
|
-- # conditions and the following disclaimer in the documentation and/or other materials #
|
|
-- # provided with the distribution. #
|
|
-- # #
|
|
-- # 3. Neither the name of the copyright holder nor the names of its contributors may be used to #
|
|
-- # endorse or promote products derived from this software without specific prior written #
|
|
-- # permission. #
|
|
-- # #
|
|
-- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS #
|
|
-- # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF #
|
|
-- # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE #
|
|
-- # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, #
|
|
-- # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE #
|
|
-- # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED #
|
|
-- # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING #
|
|
-- # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED #
|
|
-- # OF THE POSSIBILITY OF SUCH DAMAGE. #
|
|
-- #################################################################################################
|
|
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
use ieee.numeric_std.all;
|
|
|
|
library neorv32;
|
|
use neorv32.neorv32_package.all;
|
|
|
|
entity neorv32_bus_gateway is
|
|
generic (
|
|
TIMEOUT : natural; -- internal bus timeout cycles
|
|
-- IMEM port --
|
|
IMEM_ENABLE : boolean;
|
|
IMEM_BASE : std_ulogic_vector(31 downto 0);
|
|
IMEM_SIZE : natural;
|
|
-- DMEM port --
|
|
DMEM_ENABLE : boolean;
|
|
DMEM_BASE : std_ulogic_vector(31 downto 0);
|
|
DMEM_SIZE : natural;
|
|
-- XIP port --
|
|
XIP_ENABLE : boolean;
|
|
XIP_BASE : std_ulogic_vector(31 downto 0);
|
|
XIP_SIZE : natural;
|
|
-- BOOT ROM port --
|
|
BOOT_ENABLE : boolean;
|
|
BOOT_BASE : std_ulogic_vector(31 downto 0);
|
|
BOOT_SIZE : natural;
|
|
-- IO port --
|
|
IO_ENABLE : boolean;
|
|
IO_BASE : std_ulogic_vector(31 downto 0);
|
|
IO_SIZE : natural;
|
|
-- EXTERNAL port --
|
|
EXT_ENABLE : boolean
|
|
);
|
|
port (
|
|
-- global control --
|
|
clk_i : in std_ulogic; -- global clock, rising edge
|
|
rstn_i : in std_ulogic; -- global reset, low-active, async
|
|
-- host port --
|
|
main_req_i : in bus_req_t; -- host request
|
|
main_rsp_o : out bus_rsp_t; -- host response
|
|
-- section ports --
|
|
imem_req_o : out bus_req_t;
|
|
imem_rsp_i : in bus_rsp_t;
|
|
dmem_req_o : out bus_req_t;
|
|
dmem_rsp_i : in bus_rsp_t;
|
|
xip_req_o : out bus_req_t;
|
|
xip_rsp_i : in bus_rsp_t;
|
|
boot_req_o : out bus_req_t;
|
|
boot_rsp_i : in bus_rsp_t;
|
|
io_req_o : out bus_req_t;
|
|
io_rsp_i : in bus_rsp_t;
|
|
ext_req_o : out bus_req_t;
|
|
ext_rsp_i : in bus_rsp_t
|
|
);
|
|
end neorv32_bus_gateway;
|
|
|
|
architecture neorv32_bus_gateway_rtl of neorv32_bus_gateway is
|
|
|
|
-- port select --
|
|
signal port_sel : std_ulogic_vector(5 downto 0);
|
|
|
|
-- list of enabled gateway ports --
|
|
type port_en_list_t is array (0 to 5) of boolean;
|
|
constant port_en_list_c : port_en_list_t := (IMEM_ENABLE, DMEM_ENABLE, XIP_ENABLE, BOOT_ENABLE, IO_ENABLE, EXT_ENABLE);
|
|
|
|
-- gateway ports combined as arrays --
|
|
type port_req_t is array (0 to 5) of bus_req_t;
|
|
type port_rsp_t is array (0 to 5) of bus_rsp_t;
|
|
signal port_req : port_req_t;
|
|
signal port_rsp : port_rsp_t;
|
|
|
|
-- summarized response --
|
|
signal int_rsp : bus_rsp_t;
|
|
|
|
-- bus monitor --
|
|
type keeper_t is record
|
|
busy : std_ulogic;
|
|
cnt : std_ulogic_vector(index_size_f(TIMEOUT) downto 0);
|
|
err : std_ulogic;
|
|
halt : std_ulogic;
|
|
end record;
|
|
signal keeper : keeper_t;
|
|
|
|
begin
|
|
|
|
-- Address Section Decoder ----------------------------------------------------------------
|
|
-- -------------------------------------------------------------------------------------------
|
|
port_sel(0) <= '1' when (main_req_i.addr(31 downto index_size_f(IMEM_SIZE)) = IMEM_BASE(31 downto index_size_f(IMEM_SIZE))) and IMEM_ENABLE else '0';
|
|
port_sel(1) <= '1' when (main_req_i.addr(31 downto index_size_f(DMEM_SIZE)) = DMEM_BASE(31 downto index_size_f(DMEM_SIZE))) and DMEM_ENABLE else '0';
|
|
port_sel(2) <= '1' when (main_req_i.addr(31 downto index_size_f(XIP_SIZE)) = XIP_BASE( 31 downto index_size_f(XIP_SIZE))) and XIP_ENABLE else '0';
|
|
port_sel(3) <= '1' when (main_req_i.addr(31 downto index_size_f(BOOT_SIZE)) = BOOT_BASE(31 downto index_size_f(BOOT_SIZE))) and BOOT_ENABLE else '0';
|
|
port_sel(4) <= '1' when (main_req_i.addr(31 downto index_size_f(IO_SIZE)) = IO_BASE( 31 downto index_size_f(IO_SIZE))) and IO_ENABLE else '0';
|
|
|
|
-- accesses to the "void" are redirected to the external bus interface --
|
|
port_sel(5) <= '1' when ((port_sel(4 downto 0) = "00000") and EXT_ENABLE) else '0';
|
|
|
|
|
|
-- Gateway Ports --------------------------------------------------------------------------
|
|
-- -------------------------------------------------------------------------------------------
|
|
imem_req_o <= port_req(0); port_rsp(0) <= imem_rsp_i;
|
|
dmem_req_o <= port_req(1); port_rsp(1) <= dmem_rsp_i;
|
|
xip_req_o <= port_req(2); port_rsp(2) <= xip_rsp_i;
|
|
boot_req_o <= port_req(3); port_rsp(3) <= boot_rsp_i;
|
|
io_req_o <= port_req(4); port_rsp(4) <= io_rsp_i;
|
|
ext_req_o <= port_req(5); port_rsp(5) <= ext_rsp_i;
|
|
|
|
-- bus request --
|
|
request: process(main_req_i, port_sel)
|
|
begin
|
|
for i in 0 to 5 loop
|
|
port_req(i) <= req_terminate_c;
|
|
if port_en_list_c(i) then
|
|
port_req(i) <= main_req_i;
|
|
port_req(i).stb <= main_req_i.stb and port_sel(i);
|
|
end if;
|
|
end loop;
|
|
end process request;
|
|
|
|
-- bus response --
|
|
response: process(port_rsp)
|
|
variable tmp_v : bus_rsp_t;
|
|
begin
|
|
tmp_v := rsp_terminate_c; -- start with all-zero
|
|
for i in 0 to 5 loop -- OR all response signals
|
|
if port_en_list_c(i) then
|
|
tmp_v.data := tmp_v.data or port_rsp(i).data;
|
|
tmp_v.ack := tmp_v.ack or port_rsp(i).ack;
|
|
tmp_v.err := tmp_v.err or port_rsp(i).err;
|
|
end if;
|
|
end loop;
|
|
int_rsp <= tmp_v;
|
|
end process response;
|
|
|
|
-- host response --
|
|
main_rsp_o.data <= int_rsp.data;
|
|
main_rsp_o.ack <= int_rsp.ack;
|
|
main_rsp_o.err <= keeper.err;
|
|
|
|
|
|
-- Bus Monitor (aka "the KEEPER") ---------------------------------------------------------
|
|
-- -------------------------------------------------------------------------------------------
|
|
bus_monitor: process(rstn_i, clk_i)
|
|
begin
|
|
if (rstn_i = '0') then
|
|
keeper.busy <= '0';
|
|
keeper.cnt <= (others => '0');
|
|
keeper.err <= '0';
|
|
keeper.halt <= '0';
|
|
elsif rising_edge(clk_i) then
|
|
keeper.err <= '0'; -- default
|
|
keeper.halt <= port_sel(2) or port_sel(5); -- no timeout if XIP or EXTERNAL access
|
|
if (keeper.busy = '0') then -- bus idle
|
|
keeper.cnt <= std_ulogic_vector(to_unsigned(TIMEOUT, keeper.cnt'length));
|
|
keeper.busy <= main_req_i.stb;
|
|
else -- bus access in progress
|
|
keeper.cnt <= std_ulogic_vector(unsigned(keeper.cnt) - 1);
|
|
if (int_rsp.err = '1') or ((or_reduce_f(keeper.cnt) = '0') and (keeper.halt = '0')) then -- bus error or timeout
|
|
keeper.err <= '1';
|
|
keeper.busy <= '0';
|
|
elsif (int_rsp.ack = '1') then -- normal access termination
|
|
keeper.busy <= '0';
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process bus_monitor;
|
|
|
|
|
|
end neorv32_bus_gateway_rtl;
|
|
|
|
|
|
-- ############################################################################################################################
|
|
-- ############################################################################################################################
|
|
|
|
|
|
-- #################################################################################################
|
|
-- # << NEORV32 - Processor Bus Infrastructure: IO Switch >> #
|
|
-- # ********************************************************************************************* #
|
|
-- # Simple address decoding switch for the processor's internal IO/peripheral devices. #
|
|
-- # ********************************************************************************************* #
|
|
-- # BSD 3-Clause License #
|
|
-- # #
|
|
-- # The NEORV32 RISC-V Processor, https://github.com/stnolting/neorv32 #
|
|
-- # Copyright (c) 2024, Stephan Nolting. All rights reserved. #
|
|
-- # #
|
|
-- # Redistribution and use in source and binary forms, with or without modification, are #
|
|
-- # permitted provided that the following conditions are met: #
|
|
-- # #
|
|
-- # 1. Redistributions of source code must retain the above copyright notice, this list of #
|
|
-- # conditions and the following disclaimer. #
|
|
-- # #
|
|
-- # 2. Redistributions in binary form must reproduce the above copyright notice, this list of #
|
|
-- # conditions and the following disclaimer in the documentation and/or other materials #
|
|
-- # provided with the distribution. #
|
|
-- # #
|
|
-- # 3. Neither the name of the copyright holder nor the names of its contributors may be used to #
|
|
-- # endorse or promote products derived from this software without specific prior written #
|
|
-- # permission. #
|
|
-- # #
|
|
-- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS #
|
|
-- # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF #
|
|
-- # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE #
|
|
-- # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, #
|
|
-- # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE #
|
|
-- # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED #
|
|
-- # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING #
|
|
-- # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED #
|
|
-- # OF THE POSSIBILITY OF SUCH DAMAGE. #
|
|
-- #################################################################################################
|
|
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
use ieee.numeric_std.all;
|
|
|
|
library neorv32;
|
|
use neorv32.neorv32_package.all;
|
|
|
|
entity neorv32_bus_io_switch is
|
|
generic (
|
|
DEV_SIZE : natural; -- size of a single IO device, has to be a power of two
|
|
-- device port enable and base address --
|
|
DEV_00_EN : boolean; DEV_00_BASE : std_ulogic_vector(31 downto 0);
|
|
DEV_01_EN : boolean; DEV_01_BASE : std_ulogic_vector(31 downto 0);
|
|
DEV_02_EN : boolean; DEV_02_BASE : std_ulogic_vector(31 downto 0);
|
|
DEV_03_EN : boolean; DEV_03_BASE : std_ulogic_vector(31 downto 0);
|
|
DEV_04_EN : boolean; DEV_04_BASE : std_ulogic_vector(31 downto 0);
|
|
DEV_05_EN : boolean; DEV_05_BASE : std_ulogic_vector(31 downto 0);
|
|
DEV_06_EN : boolean; DEV_06_BASE : std_ulogic_vector(31 downto 0);
|
|
DEV_07_EN : boolean; DEV_07_BASE : std_ulogic_vector(31 downto 0);
|
|
DEV_08_EN : boolean; DEV_08_BASE : std_ulogic_vector(31 downto 0);
|
|
DEV_09_EN : boolean; DEV_09_BASE : std_ulogic_vector(31 downto 0);
|
|
DEV_10_EN : boolean; DEV_10_BASE : std_ulogic_vector(31 downto 0);
|
|
DEV_11_EN : boolean; DEV_11_BASE : std_ulogic_vector(31 downto 0);
|
|
DEV_12_EN : boolean; DEV_12_BASE : std_ulogic_vector(31 downto 0);
|
|
DEV_13_EN : boolean; DEV_13_BASE : std_ulogic_vector(31 downto 0);
|
|
DEV_14_EN : boolean; DEV_14_BASE : std_ulogic_vector(31 downto 0);
|
|
DEV_15_EN : boolean; DEV_15_BASE : std_ulogic_vector(31 downto 0);
|
|
DEV_16_EN : boolean; DEV_16_BASE : std_ulogic_vector(31 downto 0);
|
|
DEV_17_EN : boolean; DEV_17_BASE : std_ulogic_vector(31 downto 0);
|
|
DEV_18_EN : boolean; DEV_18_BASE : std_ulogic_vector(31 downto 0);
|
|
DEV_19_EN : boolean; DEV_19_BASE : std_ulogic_vector(31 downto 0);
|
|
DEV_20_EN : boolean; DEV_20_BASE : std_ulogic_vector(31 downto 0)
|
|
);
|
|
port (
|
|
-- host port --
|
|
main_req_i : in bus_req_t; -- host request
|
|
main_rsp_o : out bus_rsp_t; -- host response
|
|
-- device ports --
|
|
dev_00_req_o : out bus_req_t; dev_00_rsp_i : in bus_rsp_t;
|
|
dev_01_req_o : out bus_req_t; dev_01_rsp_i : in bus_rsp_t;
|
|
dev_02_req_o : out bus_req_t; dev_02_rsp_i : in bus_rsp_t;
|
|
dev_03_req_o : out bus_req_t; dev_03_rsp_i : in bus_rsp_t;
|
|
dev_04_req_o : out bus_req_t; dev_04_rsp_i : in bus_rsp_t;
|
|
dev_05_req_o : out bus_req_t; dev_05_rsp_i : in bus_rsp_t;
|
|
dev_06_req_o : out bus_req_t; dev_06_rsp_i : in bus_rsp_t;
|
|
dev_07_req_o : out bus_req_t; dev_07_rsp_i : in bus_rsp_t;
|
|
dev_08_req_o : out bus_req_t; dev_08_rsp_i : in bus_rsp_t;
|
|
dev_09_req_o : out bus_req_t; dev_09_rsp_i : in bus_rsp_t;
|
|
dev_10_req_o : out bus_req_t; dev_10_rsp_i : in bus_rsp_t;
|
|
dev_11_req_o : out bus_req_t; dev_11_rsp_i : in bus_rsp_t;
|
|
dev_12_req_o : out bus_req_t; dev_12_rsp_i : in bus_rsp_t;
|
|
dev_13_req_o : out bus_req_t; dev_13_rsp_i : in bus_rsp_t;
|
|
dev_14_req_o : out bus_req_t; dev_14_rsp_i : in bus_rsp_t;
|
|
dev_15_req_o : out bus_req_t; dev_15_rsp_i : in bus_rsp_t;
|
|
dev_16_req_o : out bus_req_t; dev_16_rsp_i : in bus_rsp_t;
|
|
dev_17_req_o : out bus_req_t; dev_17_rsp_i : in bus_rsp_t;
|
|
dev_18_req_o : out bus_req_t; dev_18_rsp_i : in bus_rsp_t;
|
|
dev_19_req_o : out bus_req_t; dev_19_rsp_i : in bus_rsp_t;
|
|
dev_20_req_o : out bus_req_t; dev_20_rsp_i : in bus_rsp_t
|
|
);
|
|
end neorv32_bus_io_switch;
|
|
|
|
architecture neorv32_bus_io_switch_rtl of neorv32_bus_io_switch is
|
|
|
|
-- ------------------------------------------------------------------------------------------- --
|
|
-- How to add another device port --
|
|
-- ------------------------------------------------------------------------------------------- --
|
|
-- 1. Increment <num_devs_physical_c> (must not exceed <num_devs_logical_c>). --
|
|
-- 2. Append another pair of "DEV_xx_EN" and "DEV_xx_BASE" generics. --
|
|
-- 3. Append these two generics to the according <dev_en_list_c> and <dev_base_list_c> arrays. --
|
|
-- 4. Append another pair of "dev_xx_req_o" and "dev_xx_rsp_i" ports. --
|
|
-- 5. Append these two ports to the according <dev_req> and <dev_rsp> array assignments in --
|
|
-- the "Combine Device Ports" section. --
|
|
-- ------------------------------------------------------------------------------------------- --
|
|
|
|
-- module configuration --
|
|
constant num_devs_physical_c : natural := 21; -- actual number of devices, max num_devs_logical_c
|
|
constant num_devs_logical_c : natural := 32; -- logical max number of devices; do not change!
|
|
|
|
-- address bits for access decoding --
|
|
constant abb_lo_c : natural := index_size_f(DEV_SIZE); -- low address boundary bit
|
|
constant abb_hi_c : natural := (index_size_f(DEV_SIZE) + index_size_f(num_devs_logical_c)) - 1; -- high address boundary bit
|
|
|
|
-- list of enabled device ports --
|
|
type dev_en_list_t is array (0 to num_devs_physical_c-1) of boolean;
|
|
constant dev_en_list_c : dev_en_list_t := (
|
|
DEV_00_EN, DEV_01_EN, DEV_02_EN, DEV_03_EN,
|
|
DEV_04_EN, DEV_05_EN, DEV_06_EN, DEV_07_EN,
|
|
DEV_08_EN, DEV_09_EN, DEV_10_EN, DEV_11_EN,
|
|
DEV_12_EN, DEV_13_EN, DEV_14_EN, DEV_15_EN,
|
|
DEV_16_EN, DEV_17_EN, DEV_18_EN, DEV_19_EN,
|
|
DEV_20_EN
|
|
);
|
|
|
|
-- list of device base addresses --
|
|
type dev_base_list_t is array (0 to num_devs_physical_c-1) of std_ulogic_vector(31 downto 0);
|
|
constant dev_base_list_c : dev_base_list_t := (
|
|
DEV_00_BASE, DEV_01_BASE, DEV_02_BASE, DEV_03_BASE,
|
|
DEV_04_BASE, DEV_05_BASE, DEV_06_BASE, DEV_07_BASE,
|
|
DEV_08_BASE, DEV_09_BASE, DEV_10_BASE, DEV_11_BASE,
|
|
DEV_12_BASE, DEV_13_BASE, DEV_14_BASE, DEV_15_BASE,
|
|
DEV_16_BASE, DEV_17_BASE, DEV_18_BASE, DEV_19_BASE,
|
|
DEV_20_BASE
|
|
);
|
|
|
|
-- device ports combined as arrays --
|
|
type dev_req_t is array (0 to num_devs_physical_c-1) of bus_req_t;
|
|
type dev_rsp_t is array (0 to num_devs_physical_c-1) of bus_rsp_t;
|
|
signal dev_req : dev_req_t;
|
|
signal dev_rsp : dev_rsp_t;
|
|
|
|
begin
|
|
|
|
-- Combine Device Ports -------------------------------------------------------------------
|
|
-- -------------------------------------------------------------------------------------------
|
|
dev_00_req_o <= dev_req(00); dev_rsp(00) <= dev_00_rsp_i;
|
|
dev_01_req_o <= dev_req(01); dev_rsp(01) <= dev_01_rsp_i;
|
|
dev_02_req_o <= dev_req(02); dev_rsp(02) <= dev_02_rsp_i;
|
|
dev_03_req_o <= dev_req(03); dev_rsp(03) <= dev_03_rsp_i;
|
|
dev_04_req_o <= dev_req(04); dev_rsp(04) <= dev_04_rsp_i;
|
|
dev_05_req_o <= dev_req(05); dev_rsp(05) <= dev_05_rsp_i;
|
|
dev_06_req_o <= dev_req(06); dev_rsp(06) <= dev_06_rsp_i;
|
|
dev_07_req_o <= dev_req(07); dev_rsp(07) <= dev_07_rsp_i;
|
|
dev_08_req_o <= dev_req(08); dev_rsp(08) <= dev_08_rsp_i;
|
|
dev_09_req_o <= dev_req(09); dev_rsp(09) <= dev_09_rsp_i;
|
|
dev_10_req_o <= dev_req(10); dev_rsp(10) <= dev_10_rsp_i;
|
|
dev_11_req_o <= dev_req(11); dev_rsp(11) <= dev_11_rsp_i;
|
|
dev_12_req_o <= dev_req(12); dev_rsp(12) <= dev_12_rsp_i;
|
|
dev_13_req_o <= dev_req(13); dev_rsp(13) <= dev_13_rsp_i;
|
|
dev_14_req_o <= dev_req(14); dev_rsp(14) <= dev_14_rsp_i;
|
|
dev_15_req_o <= dev_req(15); dev_rsp(15) <= dev_15_rsp_i;
|
|
dev_16_req_o <= dev_req(16); dev_rsp(16) <= dev_16_rsp_i;
|
|
dev_17_req_o <= dev_req(17); dev_rsp(17) <= dev_17_rsp_i;
|
|
dev_18_req_o <= dev_req(18); dev_rsp(18) <= dev_18_rsp_i;
|
|
dev_19_req_o <= dev_req(19); dev_rsp(19) <= dev_19_rsp_i;
|
|
dev_20_req_o <= dev_req(20); dev_rsp(20) <= dev_20_rsp_i;
|
|
|
|
|
|
-- Device Requests ------------------------------------------------------------------------
|
|
-- -------------------------------------------------------------------------------------------
|
|
access_gen:
|
|
for i in 0 to (num_devs_physical_c-1) generate
|
|
|
|
access_gen_enabled:
|
|
if dev_en_list_c(i) generate
|
|
req_gen: process(main_req_i)
|
|
begin
|
|
dev_req(i) <= main_req_i;
|
|
if (main_req_i.addr(abb_hi_c downto abb_lo_c) = dev_base_list_c(i)(abb_hi_c downto abb_lo_c)) then
|
|
dev_req(i).stb <= main_req_i.stb;
|
|
else
|
|
dev_req(i).stb <= '0';
|
|
end if;
|
|
end process req_gen;
|
|
end generate;
|
|
|
|
access_gen_disabled:
|
|
if not dev_en_list_c(i) generate
|
|
dev_req(i) <= req_terminate_c;
|
|
end generate;
|
|
|
|
end generate;
|
|
|
|
|
|
-- Global Response ------------------------------------------------------------------------
|
|
-- -------------------------------------------------------------------------------------------
|
|
bus_response: process(dev_rsp)
|
|
variable tmp_v : bus_rsp_t;
|
|
begin
|
|
tmp_v := rsp_terminate_c; -- start with all-zero
|
|
for i in 0 to (num_devs_physical_c-1) loop -- logical OR of all response signals
|
|
if dev_en_list_c(i) then
|
|
tmp_v.data := tmp_v.data or dev_rsp(i).data;
|
|
tmp_v.ack := tmp_v.ack or dev_rsp(i).ack;
|
|
tmp_v.err := tmp_v.err or dev_rsp(i).err;
|
|
end if;
|
|
end loop;
|
|
main_rsp_o <= tmp_v;
|
|
end process;
|
|
|
|
|
|
end neorv32_bus_io_switch_rtl;
|
|
|
|
|
|
-- ############################################################################################################################
|
|
-- ############################################################################################################################
|
|
|
|
|
|
-- #################################################################################################
|
|
-- # << NEORV32 - Processor Bus Infrastructure: Reservation Set Control >> #
|
|
-- # ********************************************************************************************* #
|
|
-- # Reservation set controller for the A (atomic) ISA extension's LR.W (load-reservate) and SC.W #
|
|
-- # (store-conditional) instructions. Only a single reservation set is supported. #
|
|
-- # The reservation set's granularity can be configured via the GRANULARITY generic. #
|
|
-- # ********************************************************************************************* #
|
|
-- # BSD 3-Clause License #
|
|
-- # #
|
|
-- # The NEORV32 RISC-V Processor, https://github.com/stnolting/neorv32 #
|
|
-- # Copyright (c) 2024, Stephan Nolting. All rights reserved. #
|
|
-- # #
|
|
-- # Redistribution and use in source and binary forms, with or without modification, are #
|
|
-- # permitted provided that the following conditions are met: #
|
|
-- # #
|
|
-- # 1. Redistributions of source code must retain the above copyright notice, this list of #
|
|
-- # conditions and the following disclaimer. #
|
|
-- # #
|
|
-- # 2. Redistributions in binary form must reproduce the above copyright notice, this list of #
|
|
-- # conditions and the following disclaimer in the documentation and/or other materials #
|
|
-- # provided with the distribution. #
|
|
-- # #
|
|
-- # 3. Neither the name of the copyright holder nor the names of its contributors may be used to #
|
|
-- # endorse or promote products derived from this software without specific prior written #
|
|
-- # permission. #
|
|
-- # #
|
|
-- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS #
|
|
-- # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF #
|
|
-- # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE #
|
|
-- # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, #
|
|
-- # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE #
|
|
-- # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED #
|
|
-- # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING #
|
|
-- # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED #
|
|
-- # OF THE POSSIBILITY OF SUCH DAMAGE. #
|
|
-- #################################################################################################
|
|
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
use ieee.numeric_std.all;
|
|
|
|
library neorv32;
|
|
use neorv32.neorv32_package.all;
|
|
|
|
entity neorv32_bus_reservation_set is
|
|
generic (
|
|
GRANULARITY : natural range 4 to natural'high -- reservation set granularity in bytes; has to be power of 2, min 4
|
|
);
|
|
port (
|
|
-- global control --
|
|
clk_i : in std_ulogic; -- global clock, rising edge
|
|
rstn_i : in std_ulogic; -- global reset, low-active, async
|
|
-- external status and control --
|
|
rvs_addr_o : out std_ulogic_vector(31 downto 0);
|
|
rvs_valid_o : out std_ulogic;
|
|
rvs_clear_i : in std_ulogic;
|
|
-- core/cpu port --
|
|
core_req_i : in bus_req_t;
|
|
core_rsp_o : out bus_rsp_t;
|
|
-- system ports --
|
|
sys_req_o : out bus_req_t;
|
|
sys_rsp_i : in bus_rsp_t
|
|
);
|
|
end neorv32_bus_reservation_set;
|
|
|
|
architecture neorv32_bus_reservation_set_rtl of neorv32_bus_reservation_set is
|
|
|
|
-- auto-configuration --
|
|
constant granularity_valid_c : boolean := is_power_of_two_f(GRANULARITY);
|
|
constant granularity_c : natural := cond_sel_natural_f(granularity_valid_c, GRANULARITY, 2**index_size_f(GRANULARITY));
|
|
|
|
-- reservation set granularity address boundary bit --
|
|
constant abb_c : natural := index_size_f(granularity_c);
|
|
|
|
-- reservation set --
|
|
type rsvs_t is record
|
|
state : std_ulogic_vector(01 downto 0);
|
|
addr : std_ulogic_vector(31 downto abb_c);
|
|
valid : std_ulogic;
|
|
match : std_ulogic;
|
|
end record;
|
|
signal rsvs : rsvs_t;
|
|
|
|
-- ACK override for failed SC.W --
|
|
signal ack_local : std_ulogic;
|
|
|
|
begin
|
|
|
|
-- Sanity Checks --------------------------------------------------------------------------
|
|
-- -------------------------------------------------------------------------------------------
|
|
assert not (granularity_valid_c = false) report
|
|
"[NEORV32] Auto-adjusting invalid reservation set granularity configuration." severity warning;
|
|
|
|
|
|
-- Reservation Set Control ----------------------------------------------------------------
|
|
-- -------------------------------------------------------------------------------------------
|
|
rvs_control: process(rstn_i, clk_i)
|
|
begin
|
|
if (rstn_i = '0') then
|
|
rsvs.state <= "00";
|
|
rsvs.addr <= (others => '0');
|
|
elsif rising_edge(clk_i) then
|
|
case rsvs.state is
|
|
|
|
when "10" => -- active reservation: wait for condition to invalidate reservation
|
|
-- --------------------------------------------------------------------
|
|
if (core_req_i.stb = '1') and (core_req_i.rw = '0') and (core_req_i.rvso = '1') then -- another LR instruction overriding the current reservation
|
|
rsvs.addr <= core_req_i.addr(31 downto abb_c);
|
|
end if;
|
|
--
|
|
if (rvs_clear_i = '1') then -- external clear request (highest priority)
|
|
rsvs.state <= "00"; -- invalidate reservation
|
|
elsif (core_req_i.stb = '1') and (core_req_i.rw = '1') then -- write access
|
|
|
|
if (core_req_i.rvso = '1') then -- this is a SC operation
|
|
if (rsvs.match = '1') then -- SC to reservated address
|
|
rsvs.state <= "11"; -- execute SC instruction (reservation still valid)
|
|
else -- SC to any other address
|
|
rsvs.state <= "00"; -- invalidate reservation
|
|
end if;
|
|
|
|
elsif (rsvs.match = '1') then -- normal write to reservated address
|
|
rsvs.state <= "00"; -- invalidate reservation
|
|
end if;
|
|
|
|
end if;
|
|
|
|
when "11" => -- active reservation: invalidate reservation at the end of bus access
|
|
-- --------------------------------------------------------------------
|
|
if (sys_rsp_i.ack = '1') or (sys_rsp_i.err = '1') then
|
|
rsvs.state <= "00";
|
|
end if;
|
|
|
|
when others => -- "0-" no active reservation: wait for new registration request
|
|
-- --------------------------------------------------------------------
|
|
if (core_req_i.stb = '1') and (core_req_i.rw = '0') and (core_req_i.rvso = '1') then -- load-reservate instruction
|
|
rsvs.addr <= core_req_i.addr(31 downto abb_c);
|
|
rsvs.state <= "10";
|
|
end if;
|
|
|
|
end case;
|
|
end if;
|
|
end process rvs_control;
|
|
|
|
-- address match? --
|
|
rsvs.match <= '1' when (core_req_i.addr(31 downto abb_c) = rsvs.addr) else '0';
|
|
|
|
-- reservation valid? --
|
|
rsvs.valid <= rsvs.state(1);
|
|
|
|
-- status for external system --
|
|
rvs_valid_o <= rsvs.valid;
|
|
rvs_addr_o(31 downto abb_c) <= rsvs.addr;
|
|
rvs_addr_o(abb_c-1 downto 0) <= (others => '0');
|
|
|
|
|
|
-- System Bus Interface -------------------------------------------------------------------
|
|
-- -------------------------------------------------------------------------------------------
|
|
|
|
-- gated request --
|
|
bus_request: process(core_req_i, rsvs.valid)
|
|
begin
|
|
sys_req_o <= core_req_i;
|
|
if (core_req_i.rvso = '1') and (core_req_i.rw = '1') then -- SC operation
|
|
sys_req_o.stb <= core_req_i.stb and rsvs.valid; -- write allowed if reservation still valid
|
|
else -- normal memory request or LR
|
|
sys_req_o.stb <= core_req_i.stb;
|
|
end if;
|
|
end process bus_request;
|
|
|
|
-- if a SC.W instruction fails there will be no write-request being send to the bus system
|
|
-- so we need to provide a local ACK to complete the bus access
|
|
ack_override: process(rstn_i, clk_i)
|
|
begin
|
|
if (rstn_i = '0') then
|
|
ack_local <= '0';
|
|
elsif rising_edge(clk_i) then
|
|
ack_local <= core_req_i.rvso and core_req_i.stb and core_req_i.rw and (not rsvs.valid);
|
|
end if;
|
|
end process ack_override;
|
|
|
|
-- response --
|
|
core_rsp_o.err <= sys_rsp_i.err;
|
|
core_rsp_o.ack <= sys_rsp_i.ack or ack_local; -- generate local ACK if SC fails
|
|
-- inject 1 into read data's LSB if SC fails --
|
|
core_rsp_o.data(31 downto 1) <= sys_rsp_i.data(31 downto 1);
|
|
core_rsp_o.data(0) <= sys_rsp_i.data(0) or (core_req_i.rvso and core_req_i.rw and (not rsvs.valid));
|
|
|
|
|
|
end neorv32_bus_reservation_set_rtl;
|