-- ################################################################################################# -- # << 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 (must not exceed ). -- -- 2. Append another pair of "DEV_xx_EN" and "DEV_xx_BASE" generics. -- -- 3. Append these two generics to the according and arrays. -- -- 4. Append another pair of "dev_xx_req_o" and "dev_xx_rsp_i" ports. -- -- 5. Append these two ports to the according and 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;