Add load/store and lr/sc bus fault tests. Fix lr.w not clearing local monitor when HRESP=1 HEXOKAY=1.

This commit is contained in:
Luke Wren 2021-12-11 15:52:34 +00:00
parent f64f44f7af
commit 9460b3cd04
5 changed files with 135 additions and 3 deletions

View File

@ -894,10 +894,13 @@ always @ (posedge clk or negedge rst_n) begin
`endif
if (d_memop_is_amo) begin
mw_local_exclusive_reserved <= 1'b0;
end else if (xm_memop == MEMOP_SC_W) begin
end else if (xm_memop == MEMOP_SC_W && bus_dph_ready_d) begin
mw_local_exclusive_reserved <= 1'b0;
end else if (xm_memop == MEMOP_LR_W) begin
mw_local_exclusive_reserved <= bus_dph_exokay_d;
end else if (xm_memop == MEMOP_LR_W && bus_dph_ready_d) begin
// In theory, the bus should never report HEXOKAY when HRESP is asserted.
// Still might happen (e.g. if HEXOKAY is tied high), so mask HEXOKAY with
// HREADY to be sure a failed lr.w clears the monitor.
mw_local_exclusive_reserved <= bus_dph_exokay_d && !bus_dph_err_d;
end
end
end

View File

@ -0,0 +1,40 @@
#include "tb_cxxrtl_io.h"
#include "hazard3_csr.h"
// Check load/stores which generate a bus fault generate an exception, and
// report the correct mcause and mepc.
int main() {
// Word-aligned address which generates an access fault. Constrained to a
// particular register, because the instructions appear in the test log to
// confirm mepc value.
register void *bad_addr asm ("a5") = (void*)0xcdef1234u;
asm volatile (
"sw zero, (%0)\n"
"sh zero, (%0)\n"
"sb zero, (%0)\n"
"lw zero, (%0)\n"
"lh zero, (%0)\n"
"lhu zero, (%0)\n"
"lb zero, (%0)\n"
"lbu zero, (%0)\n"
: : "r" (bad_addr)
);
tb_puts("Done.\n");
return 0;
}
void __attribute__((interrupt)) handle_exception() {
tb_printf("-> exception, mcause = %u\n", read_csr(mcause));
write_csr(mcause, 0);
uint32_t mepc = read_csr(mepc);
if (*(uint16_t*)mepc & 0x3 == 0x3) {
tb_printf("exception instr: %04x%04x\n", *(uint16_t*)(mepc + 2), *(uint16_t*)mepc);
write_csr(mepc, mepc + 4);
}
else {
tb_printf("exception instr: %04x\n", *(uint16_t*)(mepc + 2), *(uint16_t*)mepc);
write_csr(mepc, mepc + 2);
}
}

View File

@ -0,0 +1,17 @@
-> exception, mcause = 7
exception instr: 0007a023
-> exception, mcause = 7
exception instr: 00079023
-> exception, mcause = 7
exception instr: 00078023
-> exception, mcause = 5
exception instr: 0007a003
-> exception, mcause = 5
exception instr: 00079003
-> exception, mcause = 5
exception instr: 0007d003
-> exception, mcause = 5
exception instr: 00078003
-> exception, mcause = 5
exception instr: 0007c003
Done.

View File

@ -0,0 +1,62 @@
#include "tb_cxxrtl_io.h"
#include "hazard3_csr.h"
// Check lr/sc which generate a bus fault generate an exception, and
// report the correct mcause and mepc.
// Calling convention abuse to get stable register allocation without cursed register keyword
uint32_t __attribute__((naked)) do_lr_sc(uint32_t initial_sc, uint32_t *dst, const uint32_t *src) {
asm volatile (
// a5 used as a dumpster
"lr.w a5, (a2)\n"
// a0 not written if sc.w suppressed -> return value == initial_sc
"sc.w a0, a0, (a1)\n"
"ret"
);
}
int main() {
uint32_t scratch_word;
uint32_t sc_result;
uint32_t *bad_addr = (uint32_t*)0xcdef1234u;
uint32_t *good_addr = &scratch_word;
// Only the lr.w should except, because failed lr.w clears the local monitor,
// suppressing the sc.w.
tb_puts("Failed load, suppressed store\n");
sc_result = do_lr_sc(123, bad_addr, bad_addr);
// Failing sc.w must write 0 to the success register.
tb_printf("sc.w result: %u\n", sc_result);
// This time the sc.w should fault, after the successful lr.w.
tb_puts("Good load, failed store\n");
sc_result = do_lr_sc(123, bad_addr, good_addr);
// Excepted sc.w must not write the success register.
tb_printf("sc.w result: %u\n", sc_result);
tb_puts("Repeated failed store\n");
// Repeat just the sc.w. This should now be suppressed, because the prior
// faulting sc.w should clear the monitor bit.
asm volatile (
"sc.w %0, zero, (%1)\n"
: "+r" (sc_result) : "r" (bad_addr)
);
// Failing sc.w must write 0 to result register.
tb_printf("sc.w result: %u\n", sc_result);
return 0;
}
void __attribute__((interrupt)) handle_exception() {
tb_printf("-> exception, mcause = %u\n", read_csr(mcause));
write_csr(mcause, 0);
uint32_t mepc = read_csr(mepc);
if (*(uint16_t*)mepc & 0x3 == 0x3) {
tb_printf("exception instr: %04x%04x\n", *(uint16_t*)(mepc + 2), *(uint16_t*)mepc);
write_csr(mepc, mepc + 4);
}
else {
tb_printf("exception instr: %04x\n", *(uint16_t*)(mepc + 2), *(uint16_t*)mepc);
write_csr(mepc, mepc + 2);
}
}

View File

@ -0,0 +1,10 @@
Failed load, suppressed store
-> exception, mcause = 5
exception instr: 100627af
sc.w result: 0
Good load, failed store
-> exception, mcause = 7
exception instr: 18a5a52f
sc.w result: 123
Repeated failed store
sc.w result: 0