236 lines
11 KiB
VHDL
236 lines
11 KiB
VHDL
|
-- #################################################################################################
|
||
|
-- # << NEORV32 CPU - Load/Store Unit >> #
|
||
|
-- # ********************************************************************************************* #
|
||
|
-- # 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_cpu_lsu is
|
||
|
generic (
|
||
|
AMO_LRSC_ENABLE : boolean -- enable atomic LR/SC operations
|
||
|
);
|
||
|
port (
|
||
|
-- global control --
|
||
|
clk_i : in std_ulogic; -- global clock, rising edge
|
||
|
rstn_i : in std_ulogic := '0'; -- global reset, low-active, async
|
||
|
ctrl_i : in ctrl_bus_t; -- main control bus
|
||
|
-- cpu data access interface --
|
||
|
addr_i : in std_ulogic_vector(XLEN-1 downto 0); -- access address
|
||
|
wdata_i : in std_ulogic_vector(XLEN-1 downto 0); -- write data
|
||
|
rdata_o : out std_ulogic_vector(XLEN-1 downto 0); -- read data
|
||
|
mar_o : out std_ulogic_vector(XLEN-1 downto 0); -- current memory address register
|
||
|
wait_o : out std_ulogic; -- wait for access to complete
|
||
|
ma_load_o : out std_ulogic; -- misaligned load data address
|
||
|
ma_store_o : out std_ulogic; -- misaligned store data address
|
||
|
be_load_o : out std_ulogic; -- bus error on load data access
|
||
|
be_store_o : out std_ulogic; -- bus error on store data access
|
||
|
pmp_fault_i : in std_ulogic; -- PMP read/write access fault
|
||
|
-- data bus --
|
||
|
bus_req_o : out bus_req_t; -- request
|
||
|
bus_rsp_i : in bus_rsp_t -- response
|
||
|
);
|
||
|
end neorv32_cpu_lsu;
|
||
|
|
||
|
architecture neorv32_cpu_lsu_rtl of neorv32_cpu_lsu is
|
||
|
|
||
|
signal mar : std_ulogic_vector(XLEN-1 downto 0); -- memory address register
|
||
|
signal misaligned : std_ulogic; -- misaligned address
|
||
|
signal arbiter_req : std_ulogic; -- pending bus request
|
||
|
signal arbiter_err : std_ulogic; -- access error
|
||
|
|
||
|
begin
|
||
|
|
||
|
-- Access Address -------------------------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
mem_addr_reg: process(rstn_i, clk_i)
|
||
|
begin
|
||
|
if (rstn_i = '0') then
|
||
|
mar <= (others => '0');
|
||
|
misaligned <= '0';
|
||
|
elsif rising_edge(clk_i) then
|
||
|
if (ctrl_i.lsu_mo_we = '1') then
|
||
|
mar <= addr_i; -- memory address register
|
||
|
case ctrl_i.ir_funct3(1 downto 0) is -- alignment check
|
||
|
when "00" => misaligned <= '0'; -- byte
|
||
|
when "01" => misaligned <= addr_i(0); -- half-word
|
||
|
when others => misaligned <= addr_i(1) or addr_i(0); -- word
|
||
|
end case;
|
||
|
end if;
|
||
|
end if;
|
||
|
end process mem_addr_reg;
|
||
|
|
||
|
-- address output --
|
||
|
bus_req_o.addr <= mar;
|
||
|
mar_o <= mar; -- for MTVAL CSR
|
||
|
|
||
|
|
||
|
-- Access Type ----------------------------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
mem_type_reg: process(rstn_i, clk_i)
|
||
|
begin
|
||
|
if (rstn_i = '0') then
|
||
|
bus_req_o.rw <= '0';
|
||
|
bus_req_o.priv <= '0';
|
||
|
bus_req_o.rvso <= '0';
|
||
|
elsif rising_edge(clk_i) then
|
||
|
if (ctrl_i.lsu_mo_we = '1') then
|
||
|
-- read/write --
|
||
|
bus_req_o.rw <= ctrl_i.lsu_rw;
|
||
|
-- privilege level --
|
||
|
bus_req_o.priv <= ctrl_i.lsu_priv;
|
||
|
-- reservation set operation --
|
||
|
if (AMO_LRSC_ENABLE = true) and (ctrl_i.ir_opcode(2) = opcode_amo_c(2)) then
|
||
|
bus_req_o.rvso <= '1';
|
||
|
else
|
||
|
bus_req_o.rvso <= '0';
|
||
|
end if;
|
||
|
end if;
|
||
|
end if;
|
||
|
end process mem_type_reg;
|
||
|
|
||
|
-- source identifier --
|
||
|
bus_req_o.src <= '0'; -- 0 = data access
|
||
|
|
||
|
-- data/instruction fence(.i)
|
||
|
bus_req_o.fence <= ctrl_i.lsu_fence; -- this is valid even without STB being set
|
||
|
|
||
|
|
||
|
-- Data Output - Alignment and Byte Enable ------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
mem_do_reg: process(rstn_i, clk_i)
|
||
|
begin
|
||
|
if (rstn_i = '0') then
|
||
|
bus_req_o.data <= (others => '0');
|
||
|
bus_req_o.ben <= (others => '0');
|
||
|
elsif rising_edge(clk_i) then
|
||
|
if (ctrl_i.lsu_mo_we = '1') then
|
||
|
case ctrl_i.ir_funct3(1 downto 0) is
|
||
|
when "00" => -- byte
|
||
|
bus_req_o.data(07 downto 00) <= wdata_i(7 downto 0);
|
||
|
bus_req_o.data(15 downto 08) <= wdata_i(7 downto 0);
|
||
|
bus_req_o.data(23 downto 16) <= wdata_i(7 downto 0);
|
||
|
bus_req_o.data(31 downto 24) <= wdata_i(7 downto 0);
|
||
|
bus_req_o.ben <= (others => '0');
|
||
|
bus_req_o.ben(to_integer(unsigned(addr_i(1 downto 0)))) <= '1';
|
||
|
when "01" => -- half-word
|
||
|
bus_req_o.data(15 downto 00) <= wdata_i(15 downto 0);
|
||
|
bus_req_o.data(31 downto 16) <= wdata_i(15 downto 0);
|
||
|
if (addr_i(1) = '0') then
|
||
|
bus_req_o.ben <= "0011"; -- low half-word
|
||
|
else
|
||
|
bus_req_o.ben <= "1100"; -- high half-word
|
||
|
end if;
|
||
|
when others => -- word
|
||
|
bus_req_o.data <= wdata_i;
|
||
|
bus_req_o.ben <= "1111";
|
||
|
end case;
|
||
|
end if;
|
||
|
end if;
|
||
|
end process mem_do_reg;
|
||
|
|
||
|
|
||
|
-- Data Input - Alignment and Sign-Extension ----------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
mem_di_reg: process(rstn_i, clk_i)
|
||
|
begin
|
||
|
if (rstn_i = '0') then
|
||
|
rdata_o <= (others => '0');
|
||
|
elsif rising_edge(clk_i) then
|
||
|
if (arbiter_req = '1') then -- pending request
|
||
|
case ctrl_i.ir_funct3(1 downto 0) is
|
||
|
when "00" => -- byte
|
||
|
case mar(1 downto 0) is
|
||
|
when "00" => -- byte 0
|
||
|
rdata_o(7 downto 0) <= bus_rsp_i.data(07 downto 00);
|
||
|
rdata_o(XLEN-1 downto 8) <= (others => ((not ctrl_i.ir_funct3(2)) and bus_rsp_i.data(07))); -- sign-ext
|
||
|
when "01" => -- byte 1
|
||
|
rdata_o(7 downto 0) <= bus_rsp_i.data(15 downto 08);
|
||
|
rdata_o(XLEN-1 downto 8) <= (others => ((not ctrl_i.ir_funct3(2)) and bus_rsp_i.data(15))); -- sign-ext
|
||
|
when "10" => -- byte 2
|
||
|
rdata_o(7 downto 0) <= bus_rsp_i.data(23 downto 16);
|
||
|
rdata_o(XLEN-1 downto 8) <= (others => ((not ctrl_i.ir_funct3(2)) and bus_rsp_i.data(23))); -- sign-ext
|
||
|
when others => -- byte 3
|
||
|
rdata_o(7 downto 0) <= bus_rsp_i.data(31 downto 24);
|
||
|
rdata_o(XLEN-1 downto 8) <= (others => ((not ctrl_i.ir_funct3(2)) and bus_rsp_i.data(31))); -- sign-ext
|
||
|
end case;
|
||
|
when "01" => -- half-word
|
||
|
if (mar(1) = '0') then -- low half-word
|
||
|
rdata_o(15 downto 0) <= bus_rsp_i.data(15 downto 00);
|
||
|
rdata_o(XLEN-1 downto 16) <= (others => ((not ctrl_i.ir_funct3(2)) and bus_rsp_i.data(15))); -- sign-ext
|
||
|
else -- high half-word
|
||
|
rdata_o(15 downto 0) <= bus_rsp_i.data(31 downto 16);
|
||
|
rdata_o(XLEN-1 downto 16) <= (others => ((not ctrl_i.ir_funct3(2)) and bus_rsp_i.data(31))); -- sign-ext
|
||
|
end if;
|
||
|
when others => -- word
|
||
|
rdata_o(XLEN-1 downto 0) <= bus_rsp_i.data(XLEN-1 downto 0);
|
||
|
end case;
|
||
|
end if;
|
||
|
end if;
|
||
|
end process mem_di_reg;
|
||
|
|
||
|
|
||
|
-- Access Arbiter -------------------------------------------------------------------------
|
||
|
-- -------------------------------------------------------------------------------------------
|
||
|
access_arbiter: process(rstn_i, clk_i)
|
||
|
begin
|
||
|
if (rstn_i = '0') then
|
||
|
arbiter_err <= '0';
|
||
|
arbiter_req <= '0';
|
||
|
elsif rising_edge(clk_i) then
|
||
|
arbiter_err <= bus_rsp_i.err or pmp_fault_i; -- buffer stage
|
||
|
if (arbiter_req = '0') then -- idle
|
||
|
arbiter_req <= ctrl_i.lsu_req;
|
||
|
elsif (bus_rsp_i.ack = '1') or (ctrl_i.cpu_trap = '1') then -- normal termination or start of trap handling
|
||
|
arbiter_req <= '0';
|
||
|
end if;
|
||
|
end if;
|
||
|
end process access_arbiter;
|
||
|
|
||
|
-- wait for bus response --
|
||
|
wait_o <= not bus_rsp_i.ack;
|
||
|
|
||
|
-- output access/alignment errors to control unit --
|
||
|
ma_load_o <= arbiter_req and (not ctrl_i.lsu_rw) and misaligned;
|
||
|
be_load_o <= arbiter_req and (not ctrl_i.lsu_rw) and arbiter_err;
|
||
|
ma_store_o <= arbiter_req and ( ctrl_i.lsu_rw) and misaligned;
|
||
|
be_store_o <= arbiter_req and ( ctrl_i.lsu_rw) and arbiter_err;
|
||
|
|
||
|
-- access request (all source signals are driven by registers) --
|
||
|
bus_req_o.stb <= ctrl_i.lsu_req and (not misaligned) and (not pmp_fault_i);
|
||
|
|
||
|
|
||
|
end neorv32_cpu_lsu_rtl;
|