Skip to content

Commit

Permalink
Handle address translation for misaligned loads and stores better
Browse files Browse the repository at this point in the history
Refactor the LOAD and STORE instruction so they split misaligned
accesses into multiple sub-accesses and perform address translation
separately. This means we should handle the case where a misaligned
access straddles a page boundary in a sensible way, even if we don't
yet cover the full range of possibilities allowed for any RISC-V
implementation.

There are options for the order in which misaligned happen, i.e. from
high-to-low or from low-to-high as well as the granularity of the splitting,
either all the way to bytes or to the largest aligned size.

In addition tidy up the implementation in a few ways:

- Very long lines on the LOAD encdec were fixed by adding a helper

- Add some linebreaks in the code so it reads as less claustrophobic

- Ensure we use the same names for arguments in encdec/execute/assembly.
  Previously we used 'size' and 'width'. I opted for 'width' consistently.
  • Loading branch information
Alasdair committed Jul 29, 2024
1 parent 2078d87 commit 58824c6
Show file tree
Hide file tree
Showing 11 changed files with 249 additions and 46 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ SAIL_VM_SRCS += riscv_vmem_pte.sail
SAIL_VM_SRCS += riscv_vmem_ptw.sail
SAIL_VM_SRCS += riscv_vmem_tlb.sail
SAIL_VM_SRCS += riscv_vmem.sail
SAIL_VM_SRCS += riscv_vmem_utils.sail

# Non-instruction sources
PRELUDE = prelude.sail $(SAIL_XLEN) $(SAIL_FLEN) $(SAIL_VLEN) prelude_mem_metadata.sail prelude_mem.sail
Expand Down
10 changes: 10 additions & 0 deletions c_emulator/riscv_platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ bool sys_enable_bext(unit u)
return rv_enable_bext;
}

bool sys_misaligned_order_decreasing(unit u)
{
return rv_misaligned_order_decreasing;
}

bool sys_misaligned_to_byte(unit u)
{
return rv_misaligned_to_byte;
}

uint64_t sys_pmp_count(unit u)
{
return rv_pmp_count;
Expand Down
3 changes: 3 additions & 0 deletions c_emulator/riscv_platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ bool sys_enable_writable_fiom(unit);
bool sys_enable_vext(unit);
bool sys_enable_bext(unit);

bool sys_misaligned_order_decreasing(unit);
bool sys_misaligned_to_byte(unit);

uint64_t sys_pmp_count(unit);
uint64_t sys_pmp_grain(unit);

Expand Down
4 changes: 4 additions & 0 deletions c_emulator/riscv_platform_impl.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
uint64_t rv_pmp_count = 0;
uint64_t rv_pmp_grain = 0;

/* Defaults for misaligned access behavior in the generated simulator */
bool rv_misaligned_order_decreasing = false;
bool rv_misaligned_to_byte = false;

bool rv_enable_svinval = false;
bool rv_enable_zcb = false;
bool rv_enable_zfinx = false;
Expand Down
3 changes: 3 additions & 0 deletions c_emulator/riscv_platform_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
extern uint64_t rv_pmp_count;
extern uint64_t rv_pmp_grain;

extern bool rv_misaligned_order_decreasing;
extern bool rv_misaligned_to_byte;

extern bool rv_enable_svinval;
extern bool rv_enable_zcb;
extern bool rv_enable_zfinx;
Expand Down
16 changes: 16 additions & 0 deletions c_emulator/riscv_sim.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ enum {
OPT_PMP_GRAIN,
OPT_ENABLE_SVINVAL,
OPT_ENABLE_ZCB,
OPT_MISALIGNED_ORDER_DEC,
OPT_MISALIGNED_TO_BYTE,
};

static bool do_dump_dts = false;
Expand Down Expand Up @@ -127,6 +129,8 @@ static struct option options[] = {
{"enable-misaligned", no_argument, 0, 'm' },
{"pmp-count", required_argument, 0, OPT_PMP_COUNT },
{"pmp-grain", required_argument, 0, OPT_PMP_GRAIN },
{"misaligned-order-decreasing", no_argument, 0, OPT_MISALIGNED_ORDER_DEC},
{"misaligned-to-byte", no_argument, 0, OPT_MISALIGNED_TO_BYTE },
{"enable-next", no_argument, 0, 'N' },
{"ram-size", required_argument, 0, 'z' },
{"disable-compressed", no_argument, 0, 'C' },
Expand Down Expand Up @@ -315,6 +319,18 @@ static int process_args(int argc, char **argv)
}
rv_pmp_grain = pmp_grain;
break;
case OPT_MISALIGNED_ORDER_DEC:
fprintf(stderr,
"misaligned access virtual addresses will be translated in "
"decreasing order.\n");
rv_misaligned_order_decreasing = true;
break;
case OPT_MISALIGNED_TO_BYTE:
fprintf(stderr,
"misaligned accesses will be split into individual "
"byte operations.\n");
rv_misaligned_to_byte = true;
break;
case 'C':
fprintf(stderr, "disabling RVC compressed instructions.\n");
rv_enable_rvc = false;
Expand Down
1 change: 1 addition & 0 deletions model/prelude.sail
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ default Order dec

$include <smt.sail>
$include <option.sail>
$include <result.sail>
$include <arith.sail>
$include <string.sail>
$include "mapping.sail"
Expand Down
96 changes: 50 additions & 46 deletions model/riscv_insts_base.sail
Original file line number Diff line number Diff line change
Expand Up @@ -299,8 +299,8 @@ union clause ast = LOAD : (bits(12), regidx, regidx, bool, word_width, bool, boo

/* unsigned loads are only present for widths strictly less than xlen,
signed loads also present for widths equal to xlen */
mapping clause encdec = LOAD(imm, rs1, rd, is_unsigned, size, false, false) if (size_bytes(size) < sizeof(xlen_bytes)) | (not(is_unsigned) & size_bytes(size) <= sizeof(xlen_bytes))
<-> imm @ rs1 @ bool_bits(is_unsigned) @ size_enc(size) @ rd @ 0b0000011 if (size_bytes(size) < sizeof(xlen_bytes)) | (not(is_unsigned) & size_bytes(size) <= sizeof(xlen_bytes))
function valid_load_encdec(width : word_width, is_unsigned : bool) -> bool =
(size_bytes(width) < sizeof(xlen_bytes)) | (not(is_unsigned) & size_bytes(width) <= sizeof(xlen_bytes))

val extend_value : forall 'n, 0 < 'n <= xlen. (bool, bits('n)) -> xlenbits
function extend_value(is_unsigned, value) = if is_unsigned then zero_extend(value) else sign_extend(value)
Expand All @@ -313,9 +313,8 @@ function is_aligned(vaddr : xlenbits, width : word_width) -> bool =
DOUBLE => vaddr[2..0] == zeros(),
}

// Return true if the address is misaligned and we don't support misaligned access.
function check_misaligned(vaddr : xlenbits, width : word_width) -> bool =
not(plat_enable_misaligned_access()) & not(is_aligned(vaddr, width))
mapping clause encdec = LOAD(imm, rs1, rd, is_unsigned, width, false, false) if valid_load_encdec(width, is_unsigned)
<-> imm @ rs1 @ bool_bits(is_unsigned) @ size_enc(width) @ rd @ 0b0000011 if valid_load_encdec(width, is_unsigned)

function clause execute(LOAD(imm, rs1, rd, is_unsigned, width, aq, rl)) = {
let offset : xlenbits = sign_extend(imm);
Expand All @@ -326,21 +325,30 @@ function clause execute(LOAD(imm, rs1, rd, is_unsigned, width, aq, rl)) = {

/* Get the address, X(rs1) + offset.
Some extensions perform additional checks on address validity. */
match ext_data_get_addr(rs1, offset, Read(Data), width_bytes) {
Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); RETIRE_FAIL },
match ext_data_get_addr(rs1, offset, Read(Data), size_bytes(width)) {
Ext_DataAddr_Error(e) => {
ext_handle_data_check_error(e);
RETIRE_FAIL
},

Ext_DataAddr_OK(vaddr) => {
if check_misaligned(vaddr, width)
then { handle_mem_exception(vaddr, E_Load_Addr_Align()); RETIRE_FAIL }
else match translateAddr(vaddr, Read(Data)) {
TR_Failure(e, _) => { handle_mem_exception(vaddr, e); RETIRE_FAIL },
TR_Address(paddr, _) =>

match mem_read(Read(Data), paddr, width_bytes, aq, rl, false) {
MemValue(result) => { X(rd) = extend_value(is_unsigned, result); RETIRE_SUCCESS },
MemException(e) => { handle_mem_exception(vaddr, e); RETIRE_FAIL },
},
if check_misaligned(vaddr, width) then {
handle_mem_exception(vaddr, E_Load_Addr_Align());
return RETIRE_FAIL
};

match vmem_read(vaddr, width_bytes, aq, rl, false) {
Err(vaddr, e) => {
handle_mem_exception(vaddr, e);
RETIRE_FAIL
},

Ok(data) => {
X(rd) = extend_value(is_unsigned, data);
RETIRE_SUCCESS
}
}
},
}
}
}

Expand All @@ -362,17 +370,15 @@ mapping maybe_u = {
false <-> ""
}

mapping clause assembly = LOAD(imm, rs1, rd, is_unsigned, size, aq, rl)
<-> "l" ^ size_mnemonic(size) ^ maybe_u(is_unsigned) ^ maybe_aq(aq) ^ maybe_rl(rl) ^ spc() ^ reg_name(rd) ^ sep() ^ hex_bits_signed_12(imm) ^ "(" ^ reg_name(rs1) ^ ")"
mapping clause assembly = LOAD(imm, rs1, rd, is_unsigned, width, aq, rl)
<-> "l" ^ size_mnemonic(width) ^ maybe_u(is_unsigned) ^ maybe_aq(aq) ^ maybe_rl(rl) ^ spc() ^ reg_name(rd) ^ sep() ^ hex_bits_signed_12(imm) ^ "(" ^ reg_name(rs1) ^ ")"

/* ****************************************************************** */
union clause ast = STORE : (bits(12), regidx, regidx, word_width, bool, bool)

mapping clause encdec = STORE(imm7 @ imm5, rs2, rs1, size, false, false) if size_bytes(size) <= sizeof(xlen_bytes)
<-> imm7 : bits(7) @ rs2 @ rs1 @ 0b0 @ size_enc(size) @ imm5 : bits(5) @ 0b0100011 if size_bytes(size) <= sizeof(xlen_bytes)
mapping clause encdec = STORE(imm7 @ imm5, rs2, rs1, width, false, false) if size_bytes(width) <= sizeof(xlen_bytes)
<-> imm7 : bits(7) @ rs2 @ rs1 @ 0b0 @ size_enc(width) @ imm5 : bits(5) @ 0b0100011 if size_bytes(width) <= sizeof(xlen_bytes)

/* NOTE: Currently, we only EA if address translation is successful.
This may need revisiting. */
function clause execute (STORE(imm, rs2, rs1, width, aq, rl)) = {
let offset : xlenbits = sign_extend(imm);
let width_bytes = size_bytes(width);
Expand All @@ -382,33 +388,31 @@ function clause execute (STORE(imm, rs2, rs1, width, aq, rl)) = {

/* Get the address, X(rs1) + offset.
Some extensions perform additional checks on address validity. */
match ext_data_get_addr(rs1, offset, Write(Data), width_bytes) {
Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); RETIRE_FAIL },
Ext_DataAddr_OK(vaddr) =>
if check_misaligned(vaddr, width)
then { handle_mem_exception(vaddr, E_SAMO_Addr_Align()); RETIRE_FAIL }
else match translateAddr(vaddr, Write(Data)) {
TR_Failure(e, _) => { handle_mem_exception(vaddr, e); RETIRE_FAIL },
TR_Address(paddr, _) => {
let eares = mem_write_ea(paddr, width_bytes, aq, rl, false);
match (eares) {
MemException(e) => { handle_mem_exception(vaddr, e); RETIRE_FAIL },
MemValue(_) => {
let rs2_val = X(rs2);
match mem_write_value(paddr, width_bytes, rs2_val[width_bytes * 8 - 1 .. 0], aq, rl, false) {
MemValue(true) => RETIRE_SUCCESS,
MemValue(false) => internal_error(__FILE__, __LINE__, "store got false from mem_write_value"),
MemException(e) => { handle_mem_exception(vaddr, e); RETIRE_FAIL }
}
}
}
match ext_data_get_addr(rs1, offset, Write(Data), size_bytes(width)) {
Ext_DataAddr_Error(e) => {
ext_handle_data_check_error(e);
RETIRE_FAIL
},

Ext_DataAddr_OK(vaddr) => {
if check_misaligned(vaddr, width) then {
handle_mem_exception(vaddr, E_SAMO_Addr_Align());
return RETIRE_FAIL
};

match vmem_write_from_register(vaddr, width_bytes, unsigned(rs2), aq, rl, false) {
Ok() => RETIRE_SUCCESS,
Err(vaddr, e) => {
handle_mem_exception(vaddr, e);
return RETIRE_FAIL
}
}
}
}
}

mapping clause assembly = STORE(imm, rs2, rs1, size, aq, rl)
<-> "s" ^ size_mnemonic(size) ^ maybe_aq(aq) ^ maybe_rl(rl) ^ spc() ^ reg_name(rs2) ^ sep() ^ hex_bits_signed_12(imm) ^ opt_spc() ^ "(" ^ opt_spc() ^ reg_name(rs1) ^ opt_spc() ^ ")"
mapping clause assembly = STORE(imm, rs2, rs1, width, aq, rl)
<-> "s" ^ size_mnemonic(width) ^ maybe_aq(aq) ^ maybe_rl(rl) ^ spc() ^ reg_name(rs2) ^ sep() ^ hex_bits_signed_12(imm) ^ opt_spc() ^ "(" ^ opt_spc() ^ reg_name(rs1) ^ opt_spc() ^ ")"

/* ****************************************************************** */
union clause ast = ADDIW : (bits(12), regidx, regidx)
Expand Down
8 changes: 8 additions & 0 deletions model/riscv_mem.sail
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@
function is_aligned_addr forall 'n. (addr : xlenbits, width : int('n)) -> bool =
unsigned(addr) % width == 0

function check_misaligned(vaddr : xlenbits, width : word_width) -> bool = {
if plat_enable_misaligned_access() then {
false
} else {
not(is_aligned_addr(vaddr, size_bytes(width)))
}
}

function read_kind_of_flags (aq : bool, rl : bool, res : bool) -> option(read_kind) =
match (aq, rl, res) {
(false, false, false) => Some(Read_plain),
Expand Down
Loading

0 comments on commit 58824c6

Please sign in to comment.