aboutsummaryrefslogtreecommitdiffstats
path: root/testsuite
diff options
context:
space:
mode:
authorTristan Gingold <tgingold@free.fr>2022-10-29 10:22:15 +0200
committerTristan Gingold <tgingold@free.fr>2022-10-29 10:22:15 +0200
commit0f79086ca4b9efaec0a6d251d29edd87bfb2ee83 (patch)
tree5d0f16ebb475c575fd7b8008415bf1e3a14365f0 /testsuite
parent36b19897aea1140b0e988fed620ac2597b3b2440 (diff)
downloadghdl-0f79086ca4b9efaec0a6d251d29edd87bfb2ee83.tar.gz
ghdl-0f79086ca4b9efaec0a6d251d29edd87bfb2ee83.tar.bz2
ghdl-0f79086ca4b9efaec0a6d251d29edd87bfb2ee83.zip
testsuite/synth: add a test for #2234
Diffstat (limited to 'testsuite')
-rw-r--r--testsuite/synth/issue2234/neorv32_icache.vhd576
-rw-r--r--testsuite/synth/issue2234/neorv32_package.vhd156
-rw-r--r--testsuite/synth/issue2234/repro.vhdl34
-rwxr-xr-xtestsuite/synth/issue2234/testsuite.sh9
4 files changed, 775 insertions, 0 deletions
diff --git a/testsuite/synth/issue2234/neorv32_icache.vhd b/testsuite/synth/issue2234/neorv32_icache.vhd
new file mode 100644
index 000000000..af834e6de
--- /dev/null
+++ b/testsuite/synth/issue2234/neorv32_icache.vhd
@@ -0,0 +1,576 @@
+-- #################################################################################################
+-- # << NEORV32 - Processor-Internal Instruction Cache >> #
+-- # ********************************************************************************************* #
+-- # Direct mapped (ICACHE_NUM_SETS = 1) or 2-way set-associative (ICACHE_NUM_SETS = 2). #
+-- # Least recently used replacement policy (if ICACHE_NUM_SETS > 1). #
+-- # ********************************************************************************************* #
+-- # BSD 3-Clause License #
+-- # #
+-- # Copyright (c) 2022, 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. #
+-- # ********************************************************************************************* #
+-- # The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting #
+-- #################################################################################################
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library neorv32;
+use neorv32.neorv32_package.all;
+
+entity neorv32_icache is
+ generic (
+ ICACHE_NUM_BLOCKS : natural; -- number of blocks (min 1), has to be a power of 2
+ ICACHE_BLOCK_SIZE : natural; -- block size in bytes (min 4), has to be a power of 2
+ ICACHE_NUM_SETS : natural -- associativity / number of sets (1=direct_mapped), has to be a power of 2
+ );
+ port (
+ -- global control --
+ clk_i : in std_ulogic; -- global clock, rising edge
+ rstn_i : in std_ulogic; -- global reset, low-active, async
+ clear_i : in std_ulogic; -- cache clear
+ miss_o : out std_ulogic; -- cache miss
+ -- host controller interface --
+ host_addr_i : in std_ulogic_vector(31 downto 0); -- bus access address
+ host_rdata_o : out std_ulogic_vector(31 downto 0); -- bus read data
+ host_re_i : in std_ulogic; -- read enable
+ host_ack_o : out std_ulogic; -- bus transfer acknowledge
+ host_err_o : out std_ulogic; -- bus transfer error
+ -- peripheral bus interface --
+ bus_cached_o : out std_ulogic; -- set if cached (!) access in progress
+ bus_addr_o : out std_ulogic_vector(31 downto 0); -- bus access address
+ bus_rdata_i : in std_ulogic_vector(31 downto 0); -- bus read data
+ bus_re_o : out std_ulogic; -- read enable
+ bus_ack_i : in std_ulogic; -- bus transfer acknowledge
+ bus_err_i : in std_ulogic -- bus transfer error
+ );
+end neorv32_icache;
+
+architecture neorv32_icache_rtl of neorv32_icache is
+
+ -- cache layout --
+ constant cache_offset_size_c : natural := index_size_f(ICACHE_BLOCK_SIZE/4); -- offset addresses full 32-bit words
+ constant cache_index_size_c : natural := index_size_f(ICACHE_NUM_BLOCKS);
+ constant cache_tag_size_c : natural := 32 - (cache_offset_size_c + cache_index_size_c + 2); -- 2 additonal bits for byte offset
+
+ -- cache memory --
+ component neorv32_icache_memory
+ generic (
+ ICACHE_NUM_BLOCKS : natural := 4; -- number of blocks (min 1), has to be a power of 2
+ ICACHE_BLOCK_SIZE : natural := 16; -- block size in bytes (min 4), has to be a power of 2
+ ICACHE_NUM_SETS : natural := 1 -- associativity; 1=direct-mapped, 2=2-way set-associative
+ );
+ port (
+ -- global control --
+ clk_i : in std_ulogic; -- global clock, rising edge
+ invalidate_i : in std_ulogic; -- invalidate whole cache
+ -- host cache access (read-only) --
+ host_addr_i : in std_ulogic_vector(31 downto 0); -- access address
+ host_re_i : in std_ulogic; -- read enable
+ host_rdata_o : out std_ulogic_vector(31 downto 0); -- read data
+ -- access status (1 cycle delay to access) --
+ hit_o : out std_ulogic; -- hit access
+ -- ctrl cache access (write-only) --
+ ctrl_en_i : in std_ulogic; -- control interface enable
+ ctrl_addr_i : in std_ulogic_vector(31 downto 0); -- access address
+ ctrl_we_i : in std_ulogic; -- write enable (full-word)
+ ctrl_wdata_i : in std_ulogic_vector(31 downto 0); -- write data
+ ctrl_tag_we_i : in std_ulogic; -- write tag to selected block
+ ctrl_valid_i : in std_ulogic; -- make selected block valid
+ ctrl_invalid_i : in std_ulogic -- make selected block invalid
+ );
+ end component;
+
+ -- cache interface --
+ type cache_if_t is record
+ clear : std_ulogic; -- cache clear
+ host_addr : std_ulogic_vector(31 downto 0); -- cpu access address
+ host_rdata : std_ulogic_vector(31 downto 0); -- cpu read data
+ hit : std_ulogic; -- hit access
+ ctrl_en : std_ulogic; -- control access enable
+ ctrl_addr : std_ulogic_vector(31 downto 0); -- control access address
+ ctrl_we : std_ulogic; -- control write enable
+ ctrl_wdata : std_ulogic_vector(31 downto 0); -- control write data
+ ctrl_tag_we : std_ulogic; -- control tag write enabled
+ ctrl_valid_we : std_ulogic; -- control valid flag set
+ ctrl_invalid_we : std_ulogic; -- control valid flag clear
+ end record;
+ signal cache : cache_if_t;
+
+ -- control engine --
+ type ctrl_engine_state_t is (S_IDLE, S_CACHE_CLEAR, S_CACHE_CHECK, S_CACHE_MISS, S_BUS_DOWNLOAD_REQ, S_BUS_DOWNLOAD_GET,
+ S_CACHE_RESYNC_0, S_CACHE_RESYNC_1, S_BUS_ERROR);
+ type ctrl_t is record
+ state : ctrl_engine_state_t; -- current state
+ state_nxt : ctrl_engine_state_t; -- next state
+ addr_reg : std_ulogic_vector(31 downto 0); -- address register for block download
+ addr_reg_nxt : std_ulogic_vector(31 downto 0);
+ re_buf : std_ulogic; -- read request buffer
+ re_buf_nxt : std_ulogic;
+ clear_buf : std_ulogic; -- clear request buffer
+ clear_buf_nxt : std_ulogic;
+ end record;
+ signal ctrl : ctrl_t;
+
+begin
+
+ -- Sanity Checks --------------------------------------------------------------------------
+ -- -------------------------------------------------------------------------------------------
+ -- configuration --
+ assert not (is_power_of_two_f(ICACHE_NUM_BLOCKS) = false) report "NEORV32 PROCESSOR CONFIG ERROR! i-cache number of blocks <ICACHE_NUM_BLOCKS> has to be a power of 2." severity error;
+ assert not (is_power_of_two_f(ICACHE_BLOCK_SIZE) = false) report "NEORV32 PROCESSOR CONFIG ERROR! i-cache block size <ICACHE_BLOCK_SIZE> has to be a power of 2." severity error;
+ assert not ((is_power_of_two_f(ICACHE_NUM_SETS) = false)) report "NEORV32 PROCESSOR CONFIG ERROR! i-cache associativity <ICACHE_NUM_SETS> has to be a power of 2." severity error;
+ assert not (ICACHE_NUM_BLOCKS < 1) report "NEORV32 PROCESSOR CONFIG ERROR! i-cache number of blocks <ICACHE_NUM_BLOCKS> has to be >= 1." severity error;
+ assert not (ICACHE_BLOCK_SIZE < 4) report "NEORV32 PROCESSOR CONFIG ERROR! i-cache block size <ICACHE_BLOCK_SIZE> has to be >= 4." severity error;
+ assert not ((ICACHE_NUM_SETS = 0) or (ICACHE_NUM_SETS > 2)) report "NEORV32 PROCESSOR CONFIG ERROR! i-cache associativity <ICACHE_NUM_SETS> has to be 1 (direct-mapped) or 2 (2-way set-associative)." severity error;
+
+
+ -- Control Engine FSM Sync ----------------------------------------------------------------
+ -- -------------------------------------------------------------------------------------------
+ ctrl_engine_fsm_sync: process(rstn_i, clk_i)
+ begin
+ if (rstn_i = '0') then
+ ctrl.state <= S_CACHE_CLEAR; -- to reset cache information memory, which does not have an explicit reset
+ ctrl.re_buf <= '0';
+ ctrl.clear_buf <= '0';
+ ctrl.addr_reg <= (others => '-');
+ elsif rising_edge(clk_i) then
+ ctrl.state <= ctrl.state_nxt;
+ ctrl.re_buf <= ctrl.re_buf_nxt;
+ ctrl.clear_buf <= ctrl.clear_buf_nxt;
+ ctrl.addr_reg <= ctrl.addr_reg_nxt;
+ end if;
+ end process ctrl_engine_fsm_sync;
+
+
+ -- Control Engine FSM Comb ----------------------------------------------------------------
+ -- -------------------------------------------------------------------------------------------
+ ctrl_engine_fsm_comb: process(ctrl, cache, clear_i, host_addr_i, host_re_i, bus_rdata_i, bus_ack_i, bus_err_i)
+ begin
+ -- control defaults --
+ ctrl.state_nxt <= ctrl.state;
+ ctrl.addr_reg_nxt <= ctrl.addr_reg;
+ ctrl.re_buf_nxt <= ctrl.re_buf or host_re_i;
+ ctrl.clear_buf_nxt <= ctrl.clear_buf or clear_i; -- buffer clear request from CPU
+
+ -- cache defaults --
+ cache.clear <= '0';
+ cache.host_addr <= host_addr_i;
+ cache.ctrl_en <= '0';
+ cache.ctrl_addr <= ctrl.addr_reg;
+ cache.ctrl_we <= '0';
+ cache.ctrl_wdata <= bus_rdata_i;
+ cache.ctrl_tag_we <= '0';
+ cache.ctrl_valid_we <= '0';
+ cache.ctrl_invalid_we <= '0';
+
+ -- host interface defaults --
+ host_ack_o <= '0';
+ host_err_o <= '0';
+ host_rdata_o <= cache.host_rdata;
+
+ -- peripheral bus interface defaults --
+ bus_addr_o <= ctrl.addr_reg;
+ bus_re_o <= '0';
+
+ -- fsm --
+ case ctrl.state is
+
+ when S_IDLE => -- wait for host access request or cache control operation
+ -- ------------------------------------------------------------
+ if (ctrl.clear_buf = '1') then -- cache control operation?
+ ctrl.state_nxt <= S_CACHE_CLEAR;
+ elsif (host_re_i = '1') or (ctrl.re_buf = '1') then -- cache access
+ ctrl.re_buf_nxt <= '0';
+ ctrl.state_nxt <= S_CACHE_CHECK;
+ end if;
+
+ when S_CACHE_CLEAR => -- invalidate all cache entries
+ -- ------------------------------------------------------------
+ ctrl.clear_buf_nxt <= '0';
+ cache.clear <= '1';
+ ctrl.state_nxt <= S_IDLE;
+
+ when S_CACHE_CHECK => -- finalize host access if cache hit
+ -- ------------------------------------------------------------
+ if (cache.hit = '1') then -- cache HIT
+ host_ack_o <= '1';
+ ctrl.state_nxt <= S_IDLE;
+ else -- cache MISS
+ ctrl.state_nxt <= S_CACHE_MISS;
+ end if;
+
+ when S_CACHE_MISS => --
+ -- ------------------------------------------------------------
+ -- compute block base address --
+ ctrl.addr_reg_nxt <= host_addr_i;
+ ctrl.addr_reg_nxt((2+cache_offset_size_c)-1 downto 2) <= (others => '0'); -- block-aligned
+ ctrl.addr_reg_nxt(1 downto 0) <= "00"; -- word-aligned
+ --
+ ctrl.state_nxt <= S_BUS_DOWNLOAD_REQ;
+
+ when S_BUS_DOWNLOAD_REQ => -- download new cache block: request new word
+ -- ------------------------------------------------------------
+ cache.ctrl_en <= '1'; -- we are in cache control mode
+ bus_re_o <= '1'; -- request new read transfer
+ ctrl.state_nxt <= S_BUS_DOWNLOAD_GET;
+
+ when S_BUS_DOWNLOAD_GET => -- download new cache block: wait for bus response
+ -- ------------------------------------------------------------
+ cache.ctrl_en <= '1'; -- we are in cache control mode
+ --
+ if (bus_err_i = '1') then -- bus error
+ ctrl.state_nxt <= S_BUS_ERROR;
+ elsif (bus_ack_i = '1') then -- ACK = write to cache and get next word
+ cache.ctrl_we <= '1'; -- write to cache
+ if (and_reduce_f(ctrl.addr_reg((2+cache_offset_size_c)-1 downto 2)) = '1') then -- block complete?
+ cache.ctrl_tag_we <= '1'; -- current block is valid now
+ cache.ctrl_valid_we <= '1'; -- write tag of current address
+ ctrl.state_nxt <= S_CACHE_RESYNC_0;
+ else -- get next word
+ ctrl.addr_reg_nxt <= std_ulogic_vector(unsigned(ctrl.addr_reg) + 4);
+ ctrl.state_nxt <= S_BUS_DOWNLOAD_REQ;
+ end if;
+ end if;
+
+ when S_CACHE_RESYNC_0 => -- re-sync host/cache access: cache read-latency
+ -- ------------------------------------------------------------
+ ctrl.state_nxt <= S_CACHE_RESYNC_1;
+
+ when S_CACHE_RESYNC_1 => -- re-sync host/cache access: finalize CPU request
+ -- ------------------------------------------------------------
+ host_ack_o <= '1';
+ ctrl.state_nxt <= S_IDLE;
+
+ when S_BUS_ERROR => -- bus error during download
+ -- ------------------------------------------------------------
+ host_err_o <= '1';
+ ctrl.state_nxt <= S_IDLE;
+
+ when others => -- undefined
+ -- ------------------------------------------------------------
+ ctrl.state_nxt <= S_IDLE;
+
+ end case;
+ end process ctrl_engine_fsm_comb;
+
+ -- signal cache miss to CPU --
+ miss_o <= '1' when (ctrl.state = S_CACHE_MISS) else '0';
+
+ -- cache access in progress --
+ bus_cached_o <= '1' when (ctrl.state = S_BUS_DOWNLOAD_REQ) or (ctrl.state = S_BUS_DOWNLOAD_GET) else '0';
+
+
+ -- Cache Memory ---------------------------------------------------------------------------
+ -- -------------------------------------------------------------------------------------------
+ neorv32_icache_memory_inst: neorv32_icache_memory
+ generic map (
+ ICACHE_NUM_BLOCKS => ICACHE_NUM_BLOCKS, -- number of blocks (min 1), has to be a power of 2
+ ICACHE_BLOCK_SIZE => ICACHE_BLOCK_SIZE, -- block size in bytes (min 4), has to be a power of 2
+ ICACHE_NUM_SETS => ICACHE_NUM_SETS -- associativity; 0=direct-mapped, 1=2-way set-associative
+ )
+ port map (
+ -- global control --
+ clk_i => clk_i, -- global clock, rising edge
+ invalidate_i => cache.clear, -- invalidate whole cache
+ -- host cache access (read-only) --
+ host_addr_i => cache.host_addr, -- access address
+ host_re_i => host_re_i, -- read enable
+ host_rdata_o => cache.host_rdata, -- read data
+ -- access status (1 cycle delay to access) --
+ hit_o => cache.hit, -- hit access
+ -- ctrl cache access (write-only) --
+ ctrl_en_i => cache.ctrl_en, -- control interface enable
+ ctrl_addr_i => cache.ctrl_addr, -- access address
+ ctrl_we_i => cache.ctrl_we, -- write enable (full-word)
+ ctrl_wdata_i => cache.ctrl_wdata, -- write data
+ ctrl_tag_we_i => cache.ctrl_tag_we, -- write tag to selected block
+ ctrl_valid_i => cache.ctrl_valid_we, -- make selected block valid
+ ctrl_invalid_i => cache.ctrl_invalid_we -- make selected block invalid
+ );
+
+end neorv32_icache_rtl;
+
+
+-- ###########################################################################################################################################
+-- ###########################################################################################################################################
+
+
+-- #################################################################################################
+-- # << NEORV32 - Cache Memory >> #
+-- # ********************************************************************************************* #
+-- # Direct mapped (ICACHE_NUM_SETS = 1) or 2-way set-associative (ICACHE_NUM_SETS = 2). #
+-- # Least recently used replacement policy (if ICACHE_NUM_SETS > 1). #
+-- # Read-only for host, write-only for control. All output signals have one cycle latency. #
+-- # #
+-- # Cache sets are mapped to individual memory components - no multi-dimensional memory arrays #
+-- # are used as some synthesis tools have problems to map these to actual BRAM primitives. #
+-- # ********************************************************************************************* #
+-- # BSD 3-Clause License #
+-- # #
+-- # Copyright (c) 2022, 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. #
+-- # ********************************************************************************************* #
+-- # The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting #
+-- #################################################################################################
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library neorv32;
+use neorv32.neorv32_package.all;
+
+entity neorv32_icache_memory is
+ generic (
+ ICACHE_NUM_BLOCKS : natural := 4; -- number of blocks (min 1), has to be a power of 2
+ ICACHE_BLOCK_SIZE : natural := 16; -- block size in bytes (min 4), has to be a power of 2
+ ICACHE_NUM_SETS : natural := 1 -- associativity; 1=direct-mapped, 2=2-way set-associative
+ );
+ port (
+ -- global control --
+ clk_i : in std_ulogic; -- global clock, rising edge
+ invalidate_i : in std_ulogic; -- invalidate whole cache
+ -- host cache access (read-only) --
+ host_addr_i : in std_ulogic_vector(31 downto 0); -- access address
+ host_re_i : in std_ulogic; -- read enable
+ host_rdata_o : out std_ulogic_vector(31 downto 0); -- read data
+ -- access status (1 cycle delay to access) --
+ hit_o : out std_ulogic; -- hit access
+ -- ctrl cache access (write-only) --
+ ctrl_en_i : in std_ulogic; -- control interface enable
+ ctrl_addr_i : in std_ulogic_vector(31 downto 0); -- access address
+ ctrl_we_i : in std_ulogic; -- write enable (full-word)
+ ctrl_wdata_i : in std_ulogic_vector(31 downto 0); -- write data
+ ctrl_tag_we_i : in std_ulogic; -- write tag to selected block
+ ctrl_valid_i : in std_ulogic; -- make selected block valid
+ ctrl_invalid_i : in std_ulogic -- make selected block invalid
+ );
+end neorv32_icache_memory;
+
+architecture neorv32_icache_memory_rtl of neorv32_icache_memory is
+
+ -- cache layout --
+ constant cache_offset_size_c : natural := index_size_f(ICACHE_BLOCK_SIZE/4); -- offset addresses full 32-bit words
+ constant cache_index_size_c : natural := index_size_f(ICACHE_NUM_BLOCKS);
+ constant cache_tag_size_c : natural := 32 - (cache_offset_size_c + cache_index_size_c + 2); -- 2 additional bits for byte offset
+ constant cache_entries_c : natural := ICACHE_NUM_BLOCKS * (ICACHE_BLOCK_SIZE/4); -- number of 32-bit entries (per set)
+
+ -- status flag memory --
+ signal valid_flag_s0 : std_ulogic_vector(ICACHE_NUM_BLOCKS-1 downto 0);
+ signal valid_flag_s1 : std_ulogic_vector(ICACHE_NUM_BLOCKS-1 downto 0);
+ signal valid : std_ulogic_vector(1 downto 0); -- valid flag read data
+
+ -- tag memory --
+ type tag_mem_t is array (0 to ICACHE_NUM_BLOCKS-1) of std_ulogic_vector(cache_tag_size_c-1 downto 0);
+ signal tag_mem_s0 : tag_mem_t;
+ signal tag_mem_s1 : tag_mem_t;
+ type tag_rd_t is array (0 to 1) of std_ulogic_vector(cache_tag_size_c-1 downto 0);
+ signal tag : tag_rd_t; -- tag read data
+
+ -- access status --
+ signal hit : std_ulogic_vector(1 downto 0);
+
+ -- access address decomposition --
+ type acc_addr_t is record
+ tag : std_ulogic_vector(cache_tag_size_c-1 downto 0);
+ index : std_ulogic_vector(cache_index_size_c-1 downto 0);
+ offset : std_ulogic_vector(cache_offset_size_c-1 downto 0);
+ end record;
+ signal host_acc_addr, ctrl_acc_addr : acc_addr_t;
+
+ -- cache data memory --
+ type cache_mem_t is array (0 to cache_entries_c-1) of std_ulogic_vector(31 downto 0);
+ signal cache_data_memory_s0 : cache_mem_t; -- set 0
+ signal cache_data_memory_s1 : cache_mem_t; -- set 1
+
+ -- cache data memory access --
+ type cache_rdata_t is array (0 to 1) of std_ulogic_vector(31 downto 0);
+ signal cache_rdata : cache_rdata_t;
+ signal cache_index : std_ulogic_vector(cache_index_size_c-1 downto 0);
+ signal cache_offset : std_ulogic_vector(cache_offset_size_c-1 downto 0);
+ signal cache_addr : std_ulogic_vector((cache_index_size_c+cache_offset_size_c)-1 downto 0); -- index & offset
+ signal cache_we : std_ulogic; -- write enable (full-word)
+ signal set_select : std_ulogic;
+
+ -- access history --
+ type history_t is record
+ re_ff : std_ulogic;
+ last_used_set : std_ulogic_vector(ICACHE_NUM_BLOCKS-1 downto 0);
+ to_be_replaced : std_ulogic;
+ end record;
+ signal history : history_t;
+
+begin
+
+ -- Access Address Decomposition -----------------------------------------------------------
+ -- -------------------------------------------------------------------------------------------
+ host_acc_addr.tag <= host_addr_i(31 downto 31-(cache_tag_size_c-1));
+ host_acc_addr.index <= host_addr_i(31-cache_tag_size_c downto 2+cache_offset_size_c);
+ host_acc_addr.offset <= host_addr_i(2+(cache_offset_size_c-1) downto 2); -- discard byte offset
+
+ ctrl_acc_addr.tag <= ctrl_addr_i(31 downto 31-(cache_tag_size_c-1));
+ ctrl_acc_addr.index <= ctrl_addr_i(31-cache_tag_size_c downto 2+cache_offset_size_c);
+ ctrl_acc_addr.offset <= ctrl_addr_i(2+(cache_offset_size_c-1) downto 2); -- discard byte offset
+
+
+ -- Cache Access History -------------------------------------------------------------------
+ -- -------------------------------------------------------------------------------------------
+ access_history: process(clk_i)
+ begin
+ if rising_edge(clk_i) then
+ history.re_ff <= host_re_i;
+ if (invalidate_i = '1') then -- invalidate whole cache
+ history.last_used_set <= (others => '1');
+ elsif (history.re_ff = '1') and (or_reduce_f(hit) = '1') and (ctrl_en_i = '0') then -- store last accessed set that caused a hit
+ history.last_used_set(to_integer(unsigned(cache_index))) <= not hit(0);
+ end if;
+ history.to_be_replaced <= history.last_used_set(to_integer(unsigned(cache_index)));
+ end if;
+ end process access_history;
+
+ -- which set is going to be replaced? -> opposite of last used set = least recently used set --
+ set_select <= '0' when (ICACHE_NUM_SETS = 1) else (not history.to_be_replaced);
+
+
+ -- Status flag memory ---------------------------------------------------------------------
+ -- -------------------------------------------------------------------------------------------
+ status_memory: process(clk_i)
+ begin
+ if rising_edge(clk_i) then
+ -- write access --
+ if (invalidate_i = '1') then -- invalidate whole cache
+ valid_flag_s0 <= (others => '0');
+ valid_flag_s1 <= (others => '0');
+ elsif (ctrl_en_i = '1') then
+ if (ctrl_invalid_i = '1') then -- make current block invalid
+ if (set_select = '0') then
+ valid_flag_s0(to_integer(unsigned(cache_index))) <= '0';
+ else
+ valid_flag_s1(to_integer(unsigned(cache_index))) <= '0';
+ end if;
+ elsif (ctrl_valid_i = '1') then -- make current block valid
+ if (set_select = '0') then
+ valid_flag_s0(to_integer(unsigned(cache_index))) <= '1';
+ else
+ valid_flag_s1(to_integer(unsigned(cache_index))) <= '1';
+ end if;
+ end if;
+ end if;
+ -- read access (sync) --
+ valid(0) <= valid_flag_s0(to_integer(unsigned(cache_index)));
+ valid(1) <= valid_flag_s1(to_integer(unsigned(cache_index)));
+ end if;
+ end process status_memory;
+
+
+ -- Tag memory -----------------------------------------------------------------------------
+ -- -------------------------------------------------------------------------------------------
+ tag_memory: process(clk_i)
+ begin
+ if rising_edge(clk_i) then
+ if (ctrl_en_i = '1') and (ctrl_tag_we_i = '1') then -- write access
+ if (set_select = '0') then
+ tag_mem_s0(to_integer(unsigned(cache_index))) <= ctrl_acc_addr.tag;
+ else
+ tag_mem_s1(to_integer(unsigned(cache_index))) <= ctrl_acc_addr.tag;
+ end if;
+ end if;
+ tag(0) <= tag_mem_s0(to_integer(unsigned(cache_index)));
+ tag(1) <= tag_mem_s1(to_integer(unsigned(cache_index)));
+ end if;
+ end process tag_memory;
+
+ -- comparator --
+ comparator: process(host_acc_addr, tag, valid)
+ begin
+ hit <= (others => '0');
+ for i in 0 to ICACHE_NUM_SETS-1 loop
+ if (host_acc_addr.tag = tag(i)) and (valid(i) = '1') then
+ hit(i) <= '1';
+ end if;
+ end loop; -- i
+ end process comparator;
+
+ -- global hit --
+ hit_o <= '1' when (or_reduce_f(hit) = '1') else '0';
+
+
+ -- Cache Data Memory ----------------------------------------------------------------------
+ -- -------------------------------------------------------------------------------------------
+ cache_mem_access: process(clk_i)
+ begin
+ if rising_edge(clk_i) then
+ if (cache_we = '1') then -- write access from control (full-word)
+ if (set_select = '0') or (ICACHE_NUM_SETS = 1) then
+ cache_data_memory_s0(to_integer(unsigned(cache_addr))) <= ctrl_wdata_i;
+ else
+ cache_data_memory_s1(to_integer(unsigned(cache_addr))) <= ctrl_wdata_i;
+ end if;
+ end if;
+ -- read access from host (full-word) --
+ cache_rdata(0) <= cache_data_memory_s0(to_integer(unsigned(cache_addr)));
+ cache_rdata(1) <= cache_data_memory_s1(to_integer(unsigned(cache_addr)));
+ end if;
+ end process cache_mem_access;
+
+ -- data output --
+ host_rdata_o <= cache_rdata(0) when (hit(0) = '1') or (ICACHE_NUM_SETS = 1) else cache_rdata(1);
+
+ -- cache block ram access address --
+ cache_addr <= cache_index & cache_offset;
+
+ -- cache access select --
+ cache_index <= host_acc_addr.index when (ctrl_en_i = '0') else ctrl_acc_addr.index;
+ cache_offset <= host_acc_addr.offset when (ctrl_en_i = '0') else ctrl_acc_addr.offset;
+ cache_we <= '0' when (ctrl_en_i = '0') else ctrl_we_i;
+
+
+end neorv32_icache_memory_rtl;
diff --git a/testsuite/synth/issue2234/neorv32_package.vhd b/testsuite/synth/issue2234/neorv32_package.vhd
new file mode 100644
index 000000000..86c877703
--- /dev/null
+++ b/testsuite/synth/issue2234/neorv32_package.vhd
@@ -0,0 +1,156 @@
+-- #################################################################################################
+-- # << NEORV32 - Main VHDL package file >> #
+-- # ********************************************************************************************* #
+-- # BSD 3-Clause License #
+-- # #
+-- # Copyright (c) 2022, 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. #
+-- # ********************************************************************************************* #
+-- # The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting #
+-- #################################################################################################
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+package neorv32_package is
+
+ -- Architecture Configuration -------------------------------------------------------------
+ -- -------------------------------------------------------------------------------------------
+ -- address space --
+ constant ispace_base_c : std_ulogic_vector(31 downto 0) := x"00000000"; -- default instruction memory address space base address
+ constant dspace_base_c : std_ulogic_vector(31 downto 0) := x"80000000"; -- default data memory address space base address
+
+
+ -- Helper Functions -----------------------------------------------------------------------
+ -- -------------------------------------------------------------------------------------------
+ function index_size_f(input : natural) return natural;
+ function or_reduce_f(a : std_ulogic_vector) return std_ulogic;
+ function and_reduce_f(a : std_ulogic_vector) return std_ulogic;
+ function xor_reduce_f(a : std_ulogic_vector) return std_ulogic;
+ function bit_rev_f(input : std_ulogic_vector) return std_ulogic_vector;
+ function is_power_of_two_f(input : natural) return boolean;
+
+
+end neorv32_package;
+
+package body neorv32_package is
+
+ -- Function: Minimal required number of bits to represent <input> numbers -----------------
+ -- -------------------------------------------------------------------------------------------
+ function index_size_f(input : natural) return natural is
+ begin
+ for i in 0 to natural'high loop
+ if (2**i >= input) then
+ return i;
+ end if;
+ end loop; -- i
+ return 0;
+ end function index_size_f;
+
+
+ -- Function: OR-reduce all bits -----------------------------------------------------------
+ -- -------------------------------------------------------------------------------------------
+ function or_reduce_f(a : std_ulogic_vector) return std_ulogic is
+ variable tmp_v : std_ulogic;
+ begin
+ tmp_v := '0';
+ for i in a'range loop
+ tmp_v := tmp_v or a(i);
+ end loop; -- i
+ return tmp_v;
+ end function or_reduce_f;
+
+ -- Function: AND-reduce all bits ----------------------------------------------------------
+ -- -------------------------------------------------------------------------------------------
+ function and_reduce_f(a : std_ulogic_vector) return std_ulogic is
+ variable tmp_v : std_ulogic;
+ begin
+ tmp_v := '1';
+ for i in a'range loop
+ tmp_v := tmp_v and a(i);
+ end loop; -- i
+ return tmp_v;
+ end function and_reduce_f;
+
+ -- Function: XOR-reduce all bits ----------------------------------------------------------
+ -- -------------------------------------------------------------------------------------------
+ function xor_reduce_f(a : std_ulogic_vector) return std_ulogic is
+ variable tmp_v : std_ulogic;
+ begin
+ tmp_v := '0';
+ for i in a'range loop
+ tmp_v := tmp_v xor a(i);
+ end loop; -- i
+ return tmp_v;
+ end function xor_reduce_f;
+
+
+ -- Function: Bit reversal -----------------------------------------------------------------
+ -- -------------------------------------------------------------------------------------------
+ function bit_rev_f(input : std_ulogic_vector) return std_ulogic_vector is
+ variable output_v : std_ulogic_vector(input'range);
+ begin
+ for i in 0 to input'length-1 loop
+ output_v(input'length-i-1) := input(i);
+ end loop; -- i
+ return output_v;
+ end function bit_rev_f;
+
+ -- Function: Test if input number is a power of two ---------------------------------------
+ -- -------------------------------------------------------------------------------------------
+ function is_power_of_two_f(input : natural) return boolean is
+ variable tmp : unsigned(31 downto 0);
+ begin
+ if (input = 0) then
+ return false;
+ elsif (input = 1) then
+ return true;
+ else
+ tmp := to_unsigned(input, 32);
+ if ((tmp and (tmp - 1)) = 0) then
+ return true;
+ else
+ return false;
+ end if;
+ end if;
+ end function is_power_of_two_f;
+
+ -- Function: Swap all bytes of a 32-bit word (endianness conversion) ----------------------
+ -- -------------------------------------------------------------------------------------------
+ function bswap32_f(input : std_ulogic_vector) return std_ulogic_vector is
+ variable output_v : std_ulogic_vector(input'range);
+ begin
+ output_v(07 downto 00) := input(31 downto 24);
+ output_v(15 downto 08) := input(23 downto 16);
+ output_v(23 downto 16) := input(15 downto 08);
+ output_v(31 downto 24) := input(07 downto 00);
+ return output_v;
+ end function bswap32_f;
+
+
+end neorv32_package;
+
diff --git a/testsuite/synth/issue2234/repro.vhdl b/testsuite/synth/issue2234/repro.vhdl
new file mode 100644
index 000000000..09fcc47fd
--- /dev/null
+++ b/testsuite/synth/issue2234/repro.vhdl
@@ -0,0 +1,34 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+entity repro is
+ port (clk : std_logic;
+ we : std_logic;
+ sel : std_logic;
+ addr : std_logic_vector(7 downto 0);
+ dati : std_logic_vector(31 downto 0);
+ dato_0 : out std_logic_vector(31 downto 0);
+ dato_1 : out std_logic_vector(31 downto 0));
+end repro;
+
+architecture syn of repro is
+ type mem_t is array (0 to 255) of std_logic_vector(31 downto 0);
+ signal mem_0 : mem_t;
+ signal mem_1 : mem_t;
+begin
+ process (clk)
+ begin
+ if rising_edge(clk) then
+ if we = '1' then
+ if sel = '0' then
+ mem_0 (to_integer(unsigned(addr))) <= dati;
+ else
+ mem_1 (to_integer(unsigned(addr))) <= dati;
+ end if;
+ end if;
+ dato_0 <= mem_0 (to_integer(unsigned(addr)));
+ dato_1 <= mem_1 (to_integer(unsigned(addr)));
+ end if;
+ end process;
+end syn;
diff --git a/testsuite/synth/issue2234/testsuite.sh b/testsuite/synth/issue2234/testsuite.sh
new file mode 100755
index 000000000..c811647da
--- /dev/null
+++ b/testsuite/synth/issue2234/testsuite.sh
@@ -0,0 +1,9 @@
+#! /bin/sh
+
+. ../../testenv.sh
+
+# synth --out=verilog --std=08 --no-formal --work=neorv32 -gCONFIG=2 -gDEBUG=False neorv32_bus_keeper.vhd neorv32_busswitch.vhd neorv32_cpu_alu.vhd neorv32_cpu_bus.vhd neorv32_cpu_control.vhd neorv32_cpu_cp_bitmanip.vhd neorv32_cpu_cp_cfu.vhd neorv32_cpu_cp_fpu.vhd neorv32_cpu_cp_muldiv.vhd neorv32_cpu_cp_shifter.vhd neorv32_cpu_decompressor.vhd neorv32_cpu_regfile.vhd neorv32_cpu.vhd neorv32_debug_dm.vhd neorv32_debug_dtm.vhd neorv32_dmem.default.vhd neorv32_fifo.vhd neorv32_icache.vhd neorv32_imem.default.vhd neorv32_litex_core_complex.vhd neorv32_mtime.vhd neorv32_package.vhd neorv32_sysinfo.vhd neorv32_top.vhd neorv32_wishbone.vhd -e neorv32_litex_core_complex
+
+synth --out=verilog -gicache_num_blocks=8 -gicache_block_size=8 -gicache_num_sets=1 --work=neorv32 neorv32_package.vhd neorv32_icache.vhd -e > icache.v
+
+echo "Test successful"