Line 43... |
Line 43... |
|
|
entity neorv32_cpu_bus is
|
entity neorv32_cpu_bus is
|
generic (
|
generic (
|
CPU_EXTENSION_RISCV_C : boolean := true; -- implement compressed extension?
|
CPU_EXTENSION_RISCV_C : boolean := true; -- implement compressed extension?
|
-- Physical memory protection (PMP) --
|
-- Physical memory protection (PMP) --
|
PMP_USE : boolean := false; -- implement physical memory protection?
|
PMP_USE : boolean := false -- implement physical memory protection?
|
PMP_NUM_REGIONS : natural := 4; -- number of regions (1..4)
|
|
PMP_GRANULARITY : natural := 16 -- granularity (1=8B, 2=16B, 3=32B, ...)
|
|
);
|
);
|
port (
|
port (
|
-- global control --
|
-- global control --
|
clk_i : in std_ulogic; -- global clock, rising edge
|
clk_i : in std_ulogic; -- global clock, rising edge
|
rstn_i : in std_ulogic := '0'; -- global reset, low-active, async
|
rstn_i : in std_ulogic := '0'; -- global reset, low-active, async
|
Line 108... |
Line 106... |
constant pmp_off_mode_c : std_ulogic_vector(1 downto 0) := "00"; -- null region (disabled)
|
constant pmp_off_mode_c : std_ulogic_vector(1 downto 0) := "00"; -- null region (disabled)
|
--constant pmp_tor_mode_c : std_ulogic_vector(1 downto 0) := "01"; -- top of range
|
--constant pmp_tor_mode_c : std_ulogic_vector(1 downto 0) := "01"; -- top of range
|
--constant pmp_na4_mode_c : std_ulogic_vector(1 downto 0) := "10"; -- naturally aligned four-byte region
|
--constant pmp_na4_mode_c : std_ulogic_vector(1 downto 0) := "10"; -- naturally aligned four-byte region
|
constant pmp_napot_mode_c : std_ulogic_vector(1 downto 0) := "11"; -- naturally aligned power-of-two region (>= 8 bytes)
|
constant pmp_napot_mode_c : std_ulogic_vector(1 downto 0) := "11"; -- naturally aligned power-of-two region (>= 8 bytes)
|
|
|
|
-- PMP granularity --
|
|
constant pmp_g_c : natural := index_size_f(pmp_min_granularity_c);
|
|
|
-- PMP configuration register bits --
|
-- PMP configuration register bits --
|
constant pmp_cfg_r_c : natural := 0; -- read permit
|
constant pmp_cfg_r_c : natural := 0; -- read permit
|
constant pmp_cfg_w_c : natural := 1; -- write permit
|
constant pmp_cfg_w_c : natural := 1; -- write permit
|
constant pmp_cfg_x_c : natural := 2; -- execute permit
|
constant pmp_cfg_x_c : natural := 2; -- execute permit
|
constant pmp_cfg_al_c : natural := 3; -- mode bit low
|
constant pmp_cfg_al_c : natural := 3; -- mode bit low
|
Line 138... |
Line 139... |
timeout : std_ulogic_vector(index_size_f(bus_timeout_c)-1 downto 0);
|
timeout : std_ulogic_vector(index_size_f(bus_timeout_c)-1 downto 0);
|
end record;
|
end record;
|
signal i_arbiter, d_arbiter : bus_arbiter_t;
|
signal i_arbiter, d_arbiter : bus_arbiter_t;
|
|
|
-- physical memory protection --
|
-- physical memory protection --
|
type pmp_addr34_t is array (0 to PMP_NUM_REGIONS-1) of std_ulogic_vector(data_width_c+1 downto 0);
|
type pmp_addr_t is array (0 to pmp_num_regions_c-1) of std_ulogic_vector(data_width_c-1 downto 0);
|
type pmp_addr_t is array (0 to PMP_NUM_REGIONS-1) of std_ulogic_vector(data_width_c-1 downto 0);
|
|
type pmp_t is record
|
type pmp_t is record
|
addr_mask : pmp_addr34_t; -- 34-bit physical address
|
addr_mask : pmp_addr_t;
|
region_base : pmp_addr_t; -- masked region base address for comparator
|
region_base : pmp_addr_t; -- region config base address
|
region_i_addr : pmp_addr_t; -- masked instruction access base address for comparator
|
region_i_addr : pmp_addr_t; -- masked instruction access base address for comparator
|
region_d_addr : pmp_addr_t; -- masked data access base address for comparator
|
region_d_addr : pmp_addr_t; -- masked data access base address for comparator
|
i_match : std_ulogic_vector(PMP_NUM_REGIONS-1 downto 0); -- region match for instruction interface
|
i_match : std_ulogic_vector(pmp_num_regions_c-1 downto 0); -- region match for instruction interface
|
d_match : std_ulogic_vector(PMP_NUM_REGIONS-1 downto 0); -- region match for data interface
|
d_match : std_ulogic_vector(pmp_num_regions_c-1 downto 0); -- region match for data interface
|
if_fault : std_ulogic_vector(PMP_NUM_REGIONS-1 downto 0); -- region access fault for fetch operation
|
if_fault : std_ulogic_vector(pmp_num_regions_c-1 downto 0); -- region access fault for fetch operation
|
ld_fault : std_ulogic_vector(PMP_NUM_REGIONS-1 downto 0); -- region access fault for load operation
|
ld_fault : std_ulogic_vector(pmp_num_regions_c-1 downto 0); -- region access fault for load operation
|
st_fault : std_ulogic_vector(PMP_NUM_REGIONS-1 downto 0); -- region access fault for store operation
|
st_fault : std_ulogic_vector(pmp_num_regions_c-1 downto 0); -- region access fault for store operation
|
end record;
|
end record;
|
signal pmp : pmp_t;
|
signal pmp : pmp_t;
|
|
|
-- pmp faults anybody? --
|
-- pmp faults anybody? --
|
signal if_pmp_fault : std_ulogic; -- pmp instruction access fault
|
signal if_pmp_fault : std_ulogic; -- pmp instruction access fault
|
Line 200... |
Line 200... |
-- -------------------------------------------------------------------------------------------
|
-- -------------------------------------------------------------------------------------------
|
mem_do_reg: process(clk_i)
|
mem_do_reg: process(clk_i)
|
begin
|
begin
|
if rising_edge(clk_i) then
|
if rising_edge(clk_i) then
|
if (ctrl_i(ctrl_bus_mo_we_c) = '1') then
|
if (ctrl_i(ctrl_bus_mo_we_c) = '1') then
|
mdo <= wdata_i; -- memory data out register (MDO)
|
mdo <= wdata_i; -- memory data output register (MDO)
|
end if;
|
end if;
|
end if;
|
end if;
|
end process mem_do_reg;
|
end process mem_do_reg;
|
|
|
-- byte enable and output data alignment --
|
-- byte enable and output data alignment --
|
Line 241... |
Line 241... |
-- -------------------------------------------------------------------------------------------
|
-- -------------------------------------------------------------------------------------------
|
mem_out_buf: process(clk_i)
|
mem_out_buf: process(clk_i)
|
begin
|
begin
|
if rising_edge(clk_i) then
|
if rising_edge(clk_i) then
|
if (ctrl_i(ctrl_bus_mi_we_c) = '1') then
|
if (ctrl_i(ctrl_bus_mi_we_c) = '1') then
|
mdi <= d_bus_rdata; -- memory data in register (MDI)
|
mdi <= d_bus_rdata; -- memory data input register (MDI)
|
end if;
|
end if;
|
end if;
|
end if;
|
end process mem_out_buf;
|
end process mem_out_buf;
|
|
|
-- input data alignment and sign extension --
|
-- input data alignment and sign extension --
|
Line 293... |
Line 293... |
d_arbiter.err_bus <= '0';
|
d_arbiter.err_bus <= '0';
|
d_arbiter.timeout <= std_ulogic_vector(to_unsigned(bus_timeout_c, index_size_f(bus_timeout_c)));
|
d_arbiter.timeout <= std_ulogic_vector(to_unsigned(bus_timeout_c, index_size_f(bus_timeout_c)));
|
else -- in progress
|
else -- in progress
|
d_arbiter.timeout <= std_ulogic_vector(unsigned(d_arbiter.timeout) - 1);
|
d_arbiter.timeout <= std_ulogic_vector(unsigned(d_arbiter.timeout) - 1);
|
d_arbiter.err_align <= (d_arbiter.err_align or d_misaligned) and (not ctrl_i(ctrl_bus_derr_ack_c));
|
d_arbiter.err_align <= (d_arbiter.err_align or d_misaligned) and (not ctrl_i(ctrl_bus_derr_ack_c));
|
d_arbiter.err_bus <= (d_arbiter.err_bus or (not or_all_f(d_arbiter.timeout)) or d_bus_err_i) and (not ctrl_i(ctrl_bus_derr_ack_c));
|
d_arbiter.err_bus <= (d_arbiter.err_bus or (not or_all_f(d_arbiter.timeout)) or d_bus_err_i or
|
|
(st_pmp_fault and d_arbiter.wr_req) or (ld_pmp_fault and d_arbiter.rd_req)) and (not ctrl_i(ctrl_bus_derr_ack_c));
|
if (d_bus_ack_i = '1') or (ctrl_i(ctrl_bus_derr_ack_c) = '1') then -- wait for normal termination / CPU abort
|
if (d_bus_ack_i = '1') or (ctrl_i(ctrl_bus_derr_ack_c) = '1') then -- wait for normal termination / CPU abort
|
d_arbiter.wr_req <= '0';
|
d_arbiter.wr_req <= '0';
|
d_arbiter.rd_req <= '0';
|
d_arbiter.rd_req <= '0';
|
end if;
|
end if;
|
end if;
|
end if;
|
Line 344... |
Line 345... |
i_arbiter.err_bus <= '0';
|
i_arbiter.err_bus <= '0';
|
i_arbiter.timeout <= std_ulogic_vector(to_unsigned(bus_timeout_c, index_size_f(bus_timeout_c)));
|
i_arbiter.timeout <= std_ulogic_vector(to_unsigned(bus_timeout_c, index_size_f(bus_timeout_c)));
|
else -- in progress
|
else -- in progress
|
i_arbiter.timeout <= std_ulogic_vector(unsigned(i_arbiter.timeout) - 1);
|
i_arbiter.timeout <= std_ulogic_vector(unsigned(i_arbiter.timeout) - 1);
|
i_arbiter.err_align <= (i_arbiter.err_align or i_misaligned) and (not ctrl_i(ctrl_bus_ierr_ack_c));
|
i_arbiter.err_align <= (i_arbiter.err_align or i_misaligned) and (not ctrl_i(ctrl_bus_ierr_ack_c));
|
i_arbiter.err_bus <= (i_arbiter.err_bus or (not or_all_f(i_arbiter.timeout)) or i_bus_err_i) and (not ctrl_i(ctrl_bus_ierr_ack_c));
|
i_arbiter.err_bus <= (i_arbiter.err_bus or (not or_all_f(i_arbiter.timeout)) or i_bus_err_i or if_pmp_fault) and (not ctrl_i(ctrl_bus_ierr_ack_c));
|
if (i_bus_ack_i = '1') or (ctrl_i(ctrl_bus_ierr_ack_c) = '1') then -- wait for normal termination / CPU abort
|
if (i_bus_ack_i = '1') or (ctrl_i(ctrl_bus_ierr_ack_c) = '1') then -- wait for normal termination / CPU abort
|
i_arbiter.rd_req <= '0';
|
i_arbiter.rd_req <= '0';
|
end if;
|
end if;
|
end if;
|
end if;
|
end if;
|
end if;
|
Line 366... |
Line 367... |
ma_instr_o <= i_arbiter.err_align;
|
ma_instr_o <= i_arbiter.err_align;
|
be_instr_o <= i_arbiter.err_bus;
|
be_instr_o <= i_arbiter.err_bus;
|
|
|
-- instruction bus (read-only) --
|
-- instruction bus (read-only) --
|
i_bus_addr_o <= fetch_pc_i(data_width_c-1 downto 2) & "00"; -- instruction access is always 4-byte aligned (even for compressed instructions)
|
i_bus_addr_o <= fetch_pc_i(data_width_c-1 downto 2) & "00"; -- instruction access is always 4-byte aligned (even for compressed instructions)
|
i_bus_wdata_o <= (others => '0');
|
i_bus_wdata_o <= (others => '0'); -- instruction fetch is read-only
|
i_bus_ben_o <= (others => '0');
|
i_bus_ben_o <= (others => '0');
|
i_bus_we_o <= '0';
|
i_bus_we_o <= '0';
|
i_bus_re_o <= ctrl_i(ctrl_bus_if_c) and (not i_misaligned) and (not if_pmp_fault); -- no actual read when misaligned or PMP fault
|
i_bus_re_o <= ctrl_i(ctrl_bus_if_c) and (not i_misaligned) and (not if_pmp_fault); -- no actual read when misaligned or PMP fault
|
i_bus_fence_o <= ctrl_i(ctrl_bus_fencei_c);
|
i_bus_fence_o <= ctrl_i(ctrl_bus_fencei_c);
|
instr_o <= i_bus_rdata_i;
|
instr_o <= i_bus_rdata_i;
|
Line 382... |
Line 383... |
'1' when (fetch_pc_i(1) = '1') else '0'; -- 32-bit accesses only
|
'1' when (fetch_pc_i(1) = '1') else '0'; -- 32-bit accesses only
|
|
|
|
|
-- Physical Memory Protection (PMP) -------------------------------------------------------
|
-- Physical Memory Protection (PMP) -------------------------------------------------------
|
-- -------------------------------------------------------------------------------------------
|
-- -------------------------------------------------------------------------------------------
|
-- compute address masks --
|
-- compute address masks (ITERATIVE!!!) --
|
pmp_masks: process(clk_i)
|
pmp_masks: process(clk_i)
|
begin
|
begin
|
if rising_edge(clk_i) then -- address configuration (not the actual address check!) has a latency of +1 cycles
|
if rising_edge(clk_i) then -- address mask computation (not the actual address check!) has a latency of max +32 cycles
|
for r in 0 to PMP_NUM_REGIONS-1 loop -- iterate over all regions
|
for r in 0 to pmp_num_regions_c-1 loop -- iterate over all regions
|
pmp.addr_mask(r) <= (others => '0'); -- default
|
pmp.addr_mask(r) <= (others => '0');
|
for i in PMP_GRANULARITY+1 to 33 loop
|
for i in pmp_g_c to data_width_c-1 loop
|
if (i = PMP_GRANULARITY+1) then
|
pmp.addr_mask(r)(i) <= pmp.addr_mask(r)(i-1) or (not pmp_addr_i(r)(i-1));
|
pmp.addr_mask(r)(i) <= '0';
|
|
else -- current bit = not AND(all previous bits)
|
|
pmp.addr_mask(r)(i) <= not and_all_f(pmp_addr_i(r)(i-1 downto PMP_GRANULARITY));
|
|
end if;
|
|
end loop; -- i
|
end loop; -- i
|
end loop; -- r
|
end loop; -- r
|
end if;
|
end if;
|
end process pmp_masks;
|
end process pmp_masks;
|
|
|
|
|
-- compute operands for comparator --
|
-- address access check --
|
pmp_prepare_check:
|
pmp_address_check:
|
for r in 0 to PMP_NUM_REGIONS-1 generate -- iterate over all regions
|
for r in 0 to pmp_num_regions_c-1 generate -- iterate over all regions
|
-- ignore lowest 3 bits of access addresses -> minimal region size = 8 bytes
|
pmp.region_i_addr(r) <= fetch_pc_i and pmp.addr_mask(r);
|
pmp.region_i_addr(r) <= (fetch_pc_i(31 downto 3) & "000") and pmp.addr_mask(r)(33 downto 2);
|
pmp.region_d_addr(r) <= mar and pmp.addr_mask(r);
|
pmp.region_d_addr(r) <= (mar(31 downto 3) & "000") and pmp.addr_mask(r)(33 downto 2);
|
pmp.region_base(r) <= pmp_addr_i(r)(data_width_c+1 downto 2) and pmp.addr_mask(r);
|
pmp.region_base(r) <= pmp_addr_i(r)(33 downto 2) and pmp.addr_mask(r)(33 downto 2);
|
--
|
|
pmp.i_match(r) <= '1' when (pmp.region_i_addr(r)(data_width_c-1 downto pmp_g_c) = pmp.region_base(r)(data_width_c-1 downto pmp_g_c)) else '0';
|
|
pmp.d_match(r) <= '1' when (pmp.region_d_addr(r)(data_width_c-1 downto pmp_g_c) = pmp.region_base(r)(data_width_c-1 downto pmp_g_c)) else '0';
|
end generate; -- r
|
end generate; -- r
|
|
|
|
|
-- check for access address match --
|
|
pmp_addr_check: process (pmp)
|
|
begin
|
|
for r in 0 to PMP_NUM_REGIONS-1 loop -- iterate over all regions
|
|
-- instruction interface --
|
|
pmp.i_match(r) <= '0';
|
|
if (pmp.region_i_addr(r)(31 downto PMP_GRANULARITY+2) = pmp.region_base(r)(31 downto PMP_GRANULARITY+2)) then
|
|
pmp.i_match(r) <= '1';
|
|
end if;
|
|
-- data interface --
|
|
pmp.d_match(r) <= '0';
|
|
if (pmp.region_d_addr(r)(31 downto PMP_GRANULARITY+2) = pmp.region_base(r)(31 downto PMP_GRANULARITY+2)) then
|
|
pmp.d_match(r) <= '1';
|
|
end if;
|
|
end loop; -- r
|
|
end process pmp_addr_check;
|
|
|
|
|
|
-- check access type and regions's permissions --
|
-- check access type and regions's permissions --
|
pmp_check_permission: process(pmp, pmp_ctrl_i, ctrl_i)
|
pmp_check_permission: process(pmp, pmp_ctrl_i, ctrl_i)
|
begin
|
begin
|
for r in 0 to PMP_NUM_REGIONS-1 loop -- iterate over all regions
|
for r in 0 to pmp_num_regions_c-1 loop -- iterate over all regions
|
if ((ctrl_i(ctrl_priv_lvl_msb_c downto ctrl_priv_lvl_lsb_c) = priv_mode_u_c) or (pmp_ctrl_i(r)(pmp_cfg_l_c) = '1')) and -- user privilege level or locked pmp entry -> enforce permissions also for machine mode
|
if ((ctrl_i(ctrl_priv_lvl_msb_c downto ctrl_priv_lvl_lsb_c) = priv_mode_u_c) or (pmp_ctrl_i(r)(pmp_cfg_l_c) = '1')) and -- user privilege level or locked pmp entry -> enforce permissions also for machine mode
|
(pmp_ctrl_i(r)(pmp_cfg_ah_c downto pmp_cfg_al_c) /= pmp_off_mode_c) then -- active entry
|
(pmp_ctrl_i(r)(pmp_cfg_ah_c downto pmp_cfg_al_c) /= pmp_off_mode_c) then -- active entry
|
pmp.if_fault(r) <= pmp.i_match(r) and (not pmp_ctrl_i(r)(pmp_cfg_x_c)); -- fetch access match no execute permission
|
pmp.if_fault(r) <= pmp.i_match(r) and (not pmp_ctrl_i(r)(pmp_cfg_x_c)); -- fetch access match no execute permission
|
pmp.ld_fault(r) <= pmp.d_match(r) and (not pmp_ctrl_i(r)(pmp_cfg_r_c)); -- load access match no read permission
|
pmp.ld_fault(r) <= pmp.d_match(r) and (not pmp_ctrl_i(r)(pmp_cfg_r_c)); -- load access match no read permission
|
pmp.st_fault(r) <= pmp.d_match(r) and (not pmp_ctrl_i(r)(pmp_cfg_w_c)); -- store access match no write permission
|
pmp.st_fault(r) <= pmp.d_match(r) and (not pmp_ctrl_i(r)(pmp_cfg_w_c)); -- store access match no write permission
|