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
 | 
					`endif
 | 
				
			||||||
		if (d_memop_is_amo) begin
 | 
							if (d_memop_is_amo) begin
 | 
				
			||||||
			mw_local_exclusive_reserved <= 1'b0;
 | 
								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;
 | 
								mw_local_exclusive_reserved <= 1'b0;
 | 
				
			||||||
		end else if (xm_memop == MEMOP_LR_W) begin
 | 
							end else if (xm_memop == MEMOP_LR_W && bus_dph_ready_d) begin
 | 
				
			||||||
			mw_local_exclusive_reserved <= bus_dph_exokay_d;
 | 
								// 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
 | 
						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