pma: Check for execute flag on instruction access

When speculation is on it can happen that the core is trying to fetch
from a wrong, speculated address. Depending on the SoC this can cause
lock-ups. Solution until now was to flush all branchprediction which is
quite costly in terms of performance. This commit fixes this and lets
the `frontend` check whether the access is actually legal.
This commit is contained in:
Florian Zaruba 2019-04-22 11:58:33 +02:00
parent 6e4a0742ef
commit 90fb1330a9
6 changed files with 51 additions and 9 deletions

View file

@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased]
### Added
- Chcek execute PMA on instruction frontend
### Changed
- Re-work interrupt and debug subsystem to associate requests during decode. This improves stability on for non-idempotent loads.

View file

@ -33,14 +33,21 @@ package ariane_pkg;
// sure to add a propper parameter check to the `check_cfg` function.
typedef struct packed {
int NrNonIdempotentRules; // Number of non idempotent rules
logic [2**16-1:0][63:0] NonIdempotentAddrBase; // base which needs to match
logic [2**16-1:0][63:0] NonIdempotentAddrMaks; // bit mask which bits to consider when matching the rule
logic [15:0][63:0] NonIdempotentAddrBase; // base which needs to match
logic [15:0][63:0] NonIdempotentAddrMaks; // bit mask which bits to consider when matching the rule
int NrExecuteRegionRules; // Number of regions which have execute property
logic [15:0][63:0] ExecuteRegionAddrBase; // base which needs to match
logic [15:0][63:0] ExecuteRegionAddrMaks; // bit mask which bits to consider when matching the rule
} ariane_cfg_t;
localparam ariane_cfg_t ArianeDefaultConfig = '{
NrNonIdempotentRules: 2,
NonIdempotentAddrBase: {64'b0, 64'b0},
NonIdempotentAddrMaks: {64'b0, 64'b0}
NonIdempotentAddrMaks: {64'b0, 64'b0},
NrExecuteRegionRules: 3,
// DRAM, Boot ROM, Debug Module
ExecuteRegionAddrBase: {64'h8000_0000, 64'h1_0000, 64'h0},
ExecuteRegionAddrMaks: {mask(64'h40000000), mask(64'h10000), mask(64'h1000)}
};
// Function being called to check parameters
@ -48,10 +55,22 @@ package ariane_pkg;
// pragma translate_off
`ifndef VERILATOR
assert(Cfg.NrNonIdempotentRules <= 16);
assert(Cfg.NrExecuteRegionRules <= 16);
`endif
// pragma translate_on
endfunction
// Generate a mask for a given power of two length
function logic [63:0] mask (input logic [63:0] len);
// pragma translate_off
`ifndef VERILATOR
// check that the region we want is actually power of two aligned
assert (2**$clog2(len) == len) else $error("Length must be a power of two");
`endif
// pragma translate_on
return {64{1'b1}} << $clog2(len);
endfunction
// TODO: Slowly move those parameters to the new system.
localparam NR_SB_ENTRIES = 8; // number of scoreboard entries
localparam TRANS_ID_BITS = $clog2(NR_SB_ENTRIES); // depending on the number of scoreboard entries we need that many bits

View file

@ -234,7 +234,7 @@ module ariane #(
.DmBaseAddress ( DmBaseAddress )
) i_frontend (
.flush_i ( flush_ctrl_if ), // not entirely correct
.flush_bp_i ( flush_ctrl_bp ),
.flush_bp_i ( 1'b0 ),
.debug_mode_i ( debug_mode ),
.boot_addr_i ( boot_addr_i ),
.icache_dreq_i ( icache_dreq_cache_if ),

View file

@ -251,7 +251,9 @@ module ex_stage #(
assign lsu_data = lsu_valid_i ? fu_data_i : '0;
load_store_unit lsu_i (
load_store_unit #(
.Cfg ( Cfg )
) lsu_i (
.clk_i,
.rst_ni,
.flush_i,

View file

@ -15,7 +15,8 @@
import ariane_pkg::*;
module load_store_unit #(
parameter int unsigned ASID_WIDTH = 1
parameter int unsigned ASID_WIDTH = 1,
parameter ariane_pkg::ariane_cfg_t Cfg = ariane_pkg::ArianeDefaultConfig
)(
input logic clk_i,
input logic rst_ni,
@ -119,7 +120,8 @@ module load_store_unit #(
mmu #(
.INSTR_TLB_ENTRIES ( 16 ),
.DATA_TLB_ENTRIES ( 16 ),
.ASID_WIDTH ( ASID_WIDTH )
.ASID_WIDTH ( ASID_WIDTH ),
.Cfg ( Cfg )
) i_mmu (
// misaligned bypass
.misaligned_ex_i ( misaligned_exception ),

View file

@ -19,8 +19,9 @@ import ariane_pkg::*;
module mmu #(
parameter int unsigned INSTR_TLB_ENTRIES = 4,
parameter int unsigned DATA_TLB_ENTRIES = 4,
parameter int unsigned ASID_WIDTH = 1
)(
parameter int unsigned ASID_WIDTH = 1,
parameter ariane_pkg::ariane_cfg_t Cfg = ariane_pkg::ArianeDefaultConfig
) (
input logic clk_i,
input logic rst_ni,
input logic flush_i,
@ -178,6 +179,7 @@ module mmu #(
//-----------------------
// Instruction Interface
//-----------------------
logic match_any_execute_region;
// The instruction interface is a simple request response interface
always_comb begin : instr_interface
// MMU disabled: just pass through
@ -235,8 +237,21 @@ module mmu #(
icache_areq_o.fetch_exception = {riscv::INSTR_PAGE_FAULT, {25'b0, update_vaddr}, 1'b1};
end
end
// if it didn't match any execute region throw an `Instruction Access Fault`
if (!match_any_execute_region) begin
icache_areq_o.fetch_exception = {riscv::INSTR_ACCESS_FAULT, icache_areq_o.fetch_paddr, 1'b1};
end
end
always_comb begin : execute_addr_check
// if we don't specify any region we assume everything is accessible
match_any_execute_region = (Cfg.NrExecuteRegionRules == 0);
// check for execute flag on memory
for (int i = 0; i < Cfg.NrExecuteRegionRules; i++) begin
match_any_execute_region |= (Cfg.ExecuteRegionAddrBase[i] & Cfg.ExecuteRegionAddrMaks[i])
== (icache_areq_o.fetch_paddr & Cfg.ExecuteRegionAddrMaks[i]);
end
end
//-----------------------
// Data Interface
//-----------------------