neorv32/rtl/core/neorv32_intercon.vhd

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;