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:
parent
f64f44f7af
commit
9460b3cd04
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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.
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue