Add test for cm.mvsa01/cm.mva01s tearing on IRQs
This commit is contained in:
		
							parent
							
								
									10a6c2616a
								
							
						
					
					
						commit
						e8b4578b40
					
				|  | @ -0,0 +1,146 @@ | ||||||
|  | #include "tb_cxxrtl_io.h" | ||||||
|  | #include "hazard3_irq.h" | ||||||
|  | #include <stdlib.h> | ||||||
|  | 
 | ||||||
|  | // Test intent: check that IRQs interrupting cm.mvsa01 and cm.mva01s do not
 | ||||||
|  | // observe any intermediate states that are disallowed by the ISA manual.
 | ||||||
|  | // These instructions expand to two movs, and an interrupt must not observe
 | ||||||
|  | // the state between the two movs. The Hazard3 implementation should allow
 | ||||||
|  | // the first expanded mov instruction to be interrupted, but not the second.
 | ||||||
|  | 
 | ||||||
|  | uint64_t __attribute__((naked)) foreground_task(uint32_t iters) { | ||||||
|  | 	asm volatile ( | ||||||
|  | 		// Move the iters argument to a different areg, as a0/a1 are special
 | ||||||
|  | 		// wrt cm.mv* instructions
 | ||||||
|  | 		"mv a4, a0\n" | ||||||
|  | 		"li a5, 0xf00b0000\n" | ||||||
|  | 		// Save s regs which we're about to trash
 | ||||||
|  | 		"addi sp, sp, -16\n" | ||||||
|  | 		"sw s0, 0(sp)\n" | ||||||
|  | 		"sw s1, 4(sp)\n" | ||||||
|  | 		"sw s2, 8(sp)\n" | ||||||
|  | 		"sw s3, 12(sp)\n" | ||||||
|  | 		// Give each s reg a recognisable token in the upper half, containing
 | ||||||
|  | 		// a-reg and s-reg index it will be found in:
 | ||||||
|  | 		"li s0, 0xa0500000\n" | ||||||
|  | 		"li s1, 0xa1510000\n" | ||||||
|  | 		"li s2, 0xa0520000\n" | ||||||
|  | 		"li s3, 0xa1530000\n" | ||||||
|  | 		// Instructions under test: bounce both s-register pairs in and out of
 | ||||||
|  | 		// a01, incrementing them each time. IRQs are disabled when
 | ||||||
|  | 		// incrementing the two a registers, to avoid misdiagnosing this as
 | ||||||
|  | 		// tearing of a cm.mva01s instruction.
 | ||||||
|  | 	"1:\n" | ||||||
|  | 	".rept 2\n" | ||||||
|  | 		".hword 0xac66\n"      // cm.mva01s s0, s1
 | ||||||
|  | 		"csrci mstatus, 0x8\n" // IRQs OFF
 | ||||||
|  | 		"addi a0, a0, 1\n" | ||||||
|  | 		"addi a1, a1, 1\n" | ||||||
|  | 		"csrsi mstatus, 0x8\n" // IRQs ON
 | ||||||
|  | 		".hword 0xac26\n"      // cm.mvsa01 s0, s1
 | ||||||
|  | 		".hword 0xad6e\n"      // cm.mva01s s2, s3
 | ||||||
|  | 		"csrci mstatus, 0x8\n" // IRQs OFF
 | ||||||
|  | 		"addi a0, a0, 1\n" | ||||||
|  | 		"addi a1, a1, 1\n" | ||||||
|  | 		"csrsi mstatus, 0x8\n" // IRQs ON
 | ||||||
|  | 		".hword 0xad2e\n"      // cm.mvsa01 s2, s3
 | ||||||
|  | 	".endr\n" | ||||||
|  | 		"addi a4, a4, -1\n" | ||||||
|  | 		"addi a5, a5, 1\n" | ||||||
|  | 		"bgtz a4, 1b\n" | ||||||
|  | 		// Done. We've been counting up in a5 as we count down in a4, so
 | ||||||
|  | 		// return a5 to confirm we iterated the expected number of times. a1
 | ||||||
|  | 		// is also returned in a1 -- the lower half should be double the
 | ||||||
|  | 		// iteration count.
 | ||||||
|  | 		"csrci mstatus, 0x8\n" // IRQs OFF
 | ||||||
|  | 		"mv a0, a5\n" | ||||||
|  | 		"lw s0, 0(sp)\n" | ||||||
|  | 		"lw s1, 4(sp)\n" | ||||||
|  | 		"lw s2, 8(sp)\n" | ||||||
|  | 		"lw s3, 12(sp)\n" | ||||||
|  | 		"addi sp, sp, 16\n" | ||||||
|  | 		"ret\n" | ||||||
|  | 	); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void isr_machine_timer_c(uint32_t a0, uint32_t a1, uint32_t s0, uint32_t s1, uint32_t s2, uint32_t s3) { | ||||||
|  | 	tb_assert((a0 & 0xffffu) == (a1 & 0xffffu), "Low-half tearing of a0,a1"); | ||||||
|  | 	tb_assert((s0 & 0xffffu) == (s1 & 0xffffu), "Low-half tearing of s0,s1"); | ||||||
|  | 	tb_assert((s2 & 0xffffu) == (s3 & 0xffffu), "Low-half tearing of s2,s3"); | ||||||
|  | 	unsigned int dwell = rand() % 301; | ||||||
|  | 	mm_timer->mtimecmp = mm_timer->mtime + dwell; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Trampoline into the actual ISR: make a0-1 and s0-3 *in the interrupted
 | ||||||
|  | // code* available as arguments.
 | ||||||
|  | void __attribute__((naked)) isr_machine_timer(void) { | ||||||
|  | 	asm volatile ( | ||||||
|  | 		// Save all caller-saved regs
 | ||||||
|  | 		"addi sp, sp, -80\n" | ||||||
|  | 		"sw ra,  0(sp)\n" | ||||||
|  | 		"sw t0,  4(sp)\n" | ||||||
|  | 		"sw t1,  8(sp)\n" | ||||||
|  | 		"sw t2, 12(sp)\n" | ||||||
|  | 		"sw a0, 16(sp)\n" | ||||||
|  | 		"sw a1, 20(sp)\n" | ||||||
|  | 		"sw a2, 24(sp)\n" | ||||||
|  | 		"sw a3, 28(sp)\n" | ||||||
|  | 		"sw a4, 32(sp)\n" | ||||||
|  | 		"sw a5, 36(sp)\n" | ||||||
|  | 		"sw a6, 40(sp)\n" | ||||||
|  | 		"sw a7, 44(sp)\n" | ||||||
|  | 		"sw t3, 48(sp)\n" | ||||||
|  | 		"sw t4, 52(sp)\n" | ||||||
|  | 		"sw t5, 56(sp)\n" | ||||||
|  | 		"sw t6, 60(sp)\n" | ||||||
|  | 		// Pass first 4 s regs as arguments
 | ||||||
|  | 		"mv a2, s0\n" | ||||||
|  | 		"mv a3, s1\n" | ||||||
|  | 		"mv a4, s2\n" | ||||||
|  | 		"mv a5, s3\n" | ||||||
|  | 		// Call actual IRQ, written in C
 | ||||||
|  | 		"jal isr_machine_timer_c\n" | ||||||
|  | 		// Restore caller saves, then return through mepc
 | ||||||
|  | 		"lw ra,  0(sp)\n" | ||||||
|  | 		"lw t0,  4(sp)\n" | ||||||
|  | 		"lw t1,  8(sp)\n" | ||||||
|  | 		"lw t2, 12(sp)\n" | ||||||
|  | 		"lw a0, 16(sp)\n" | ||||||
|  | 		"lw a1, 20(sp)\n" | ||||||
|  | 		"lw a2, 24(sp)\n" | ||||||
|  | 		"lw a3, 28(sp)\n" | ||||||
|  | 		"lw a4, 32(sp)\n" | ||||||
|  | 		"lw a5, 36(sp)\n" | ||||||
|  | 		"lw a6, 40(sp)\n" | ||||||
|  | 		"lw a7, 44(sp)\n" | ||||||
|  | 		"lw t3, 48(sp)\n" | ||||||
|  | 		"lw t4, 52(sp)\n" | ||||||
|  | 		"lw t5, 56(sp)\n" | ||||||
|  | 		"lw t6, 60(sp)\n" | ||||||
|  | 		"addi sp, sp, 80\n" | ||||||
|  | 		"mret\n" | ||||||
|  | 	); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | int main() { | ||||||
|  | 	tb_puts("Starting\n"); | ||||||
|  | 	timer_irq_enable(true); | ||||||
|  | 	// Note we do not globally enable IRQs, the code-under-test enables them
 | ||||||
|  | 	// once it has set up the necessary register tokens
 | ||||||
|  | 	const uint32_t expected_iterations = 1000; | ||||||
|  | 	tb_printf("Running %u iterations\n", expected_iterations); | ||||||
|  | 	uint64_t result_a1a0 = foreground_task(expected_iterations); | ||||||
|  | 	uint32_t result_iters = result_a1a0 & 0xffffffffu; | ||||||
|  | 	uint32_t result_a1ctr = result_a1a0 >> 32; | ||||||
|  | 	tb_printf("Result: %08x %08x\n", result_iters, result_a1ctr); | ||||||
|  | 	tb_assert(result_iters == 0xf00b0000u + expected_iterations, "Wrong number of iterations"); | ||||||
|  | 	tb_assert(result_a1ctr == (0xa1530000 + 2 * expected_iterations), "Wrong a1 counter value"); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*EXPECTED-OUTPUT***************************************************************
 | ||||||
|  | Starting | ||||||
|  | Running 1000 iterations | ||||||
|  | Result: f00b03e8 a15307d0 | ||||||
|  | *******************************************************************************/ | ||||||
		Loading…
	
		Reference in New Issue