From 763a5cd364b4798940882235af07fe3e33ddded1 Mon Sep 17 00:00:00 2001
From: Luke Wren <wren6991@gmail.com>
Date: Sat, 11 Dec 2021 17:50:12 +0000
Subject: [PATCH] Add test for readability of all implemented CSRs

---
 test/sim/common/hazard3_csr.h                 |   8 +-
 test/sim/sw_testcases/csr_readable.c          | 155 ++++++++++++++++++
 test/sim/sw_testcases/ebreak.c                |   2 +-
 test/sim/sw_testcases/illegal_instr.c         |   2 +-
 .../sim/sw_testcases/load_misalign_halfword.c |   2 +-
 test/sim/sw_testcases/load_misalign_word.c    |   2 +-
 test/sim/sw_testcases/load_store_fault.c      |   2 +-
 test/sim/sw_testcases/lr_sc_fault.c           |   2 +-
 test/sim/sw_testcases/lr_sc_smoke.c           |   2 +
 .../sw_testcases/store_misalign_halfword.c    |   2 +-
 test/sim/sw_testcases/store_misalign_word.c   |   2 +-
 11 files changed, 169 insertions(+), 12 deletions(-)
 create mode 100644 test/sim/sw_testcases/csr_readable.c

diff --git a/test/sim/common/hazard3_csr.h b/test/sim/common/hazard3_csr.h
index bba805b..3ab5daa 100644
--- a/test/sim/common/hazard3_csr.h
+++ b/test/sim/common/hazard3_csr.h
@@ -5,10 +5,10 @@
 #include "stdint.h"
 #endif
 
-#define hazard3_csr_midcr 0xbc0
-#define hazard3_csr_meie0 0xbe0 // External interrupt enable IRQ0 -> 31
-#define hazard3_csr_meip0 0xfe0 // External interrupt pending IRQ0 -> 31
-#define hazard3_csr_mlei  0xfe4 // Lowest external interrupt (pending & enabled)
+#define hazard3_csr_dmdata0 0xbff // Debug-mode shadow CSR for DM data transfer
+#define hazard3_csr_meie0   0xbe0 // External interrupt enable IRQ0 -> 31
+#define hazard3_csr_meip0   0xfe0 // External interrupt pending IRQ0 -> 31
+#define hazard3_csr_mlei    0xfe4 // Lowest external interrupt (pending & enabled)
 
 #define _read_csr(csrname) ({ \
   uint32_t __csr_tmp_u32; \
diff --git a/test/sim/sw_testcases/csr_readable.c b/test/sim/sw_testcases/csr_readable.c
new file mode 100644
index 0000000..b1f35ad
--- /dev/null
+++ b/test/sim/sw_testcases/csr_readable.c
@@ -0,0 +1,155 @@
+#include "tb_cxxrtl_io.h"
+#include "hazard3_csr.h"
+
+// Check all implemented M-mode CSRs are readable, without exception (haha).
+// Check reading D-mode CSRs generates illegal instruction exceptions in M-mode.
+
+// These are new (priv-1.12) and may not be recognised by the toolchain:
+#define mconfigptr 0xf15
+#define mstatush 0x310
+
+// Exceptions here are: medeleg, mideleg, tdata1, dcsr, dpc, dscratch1,
+// dscratch0, dmdata0 (custom). medeleg/mideleg are just a couple of
+// unimplemented registers sprinkled in for a sanity check, and the remainder
+// are D-mode registers.
+//
+// Note we permit reads but not writes to tselect, to work around a logic
+// error in openocd. Planning to implement triggers at some point, so this
+// oddity will vanish.
+
+/*EXPECTED-OUTPUT***************************************************************
+
+-> exception, mcause = 2
+CSR was 302
+-> exception, mcause = 2
+CSR was 303
+-> exception, mcause = 2
+CSR was 7a1
+-> exception, mcause = 2
+CSR was 7b0
+-> exception, mcause = 2
+CSR was 7b1
+-> exception, mcause = 2
+CSR was 7b2
+-> exception, mcause = 2
+CSR was 7b3
+-> exception, mcause = 2
+CSR was bff
+
+*******************************************************************************/
+
+int main() {
+	(void)read_csr(mvendorid);
+	(void)read_csr(marchid);
+	(void)read_csr(mimpid);
+	(void)read_csr(mhartid);
+	(void)read_csr(mconfigptr);
+	(void)read_csr(misa);
+
+	(void)read_csr(mstatus);
+	(void)read_csr(mstatush);
+	(void)read_csr(medeleg);
+	(void)read_csr(mideleg);
+	(void)read_csr(mie);
+	(void)read_csr(mip);
+	(void)read_csr(mtvec);
+	(void)read_csr(mscratch);
+	(void)read_csr(mepc);
+	(void)read_csr(mcause);
+	(void)read_csr(mtval);
+
+	(void)read_csr(mcycle);
+	(void)read_csr(mcycleh);
+	(void)read_csr(minstret);
+	(void)read_csr(minstreth);
+
+	(void)read_csr(mhpmcounter3);
+	(void)read_csr(mhpmcounter4);
+	(void)read_csr(mhpmcounter5);
+	(void)read_csr(mhpmcounter6);
+	(void)read_csr(mhpmcounter7);
+	(void)read_csr(mhpmcounter8);
+	(void)read_csr(mhpmcounter9);
+	(void)read_csr(mhpmcounter10);
+	(void)read_csr(mhpmcounter11);
+	(void)read_csr(mhpmcounter12);
+	(void)read_csr(mhpmcounter13);
+	(void)read_csr(mhpmcounter14);
+	(void)read_csr(mhpmcounter15);
+	(void)read_csr(mhpmcounter16);
+	(void)read_csr(mhpmcounter17);
+	(void)read_csr(mhpmcounter18);
+	(void)read_csr(mhpmcounter19);
+	(void)read_csr(mhpmcounter20);
+	(void)read_csr(mhpmcounter21);
+	(void)read_csr(mhpmcounter22);
+	(void)read_csr(mhpmcounter23);
+	(void)read_csr(mhpmcounter24);
+	(void)read_csr(mhpmcounter25);
+	(void)read_csr(mhpmcounter26);
+	(void)read_csr(mhpmcounter27);
+	(void)read_csr(mhpmcounter28);
+	(void)read_csr(mhpmcounter29);
+	(void)read_csr(mhpmcounter30);
+	(void)read_csr(mhpmcounter31);
+
+	(void)read_csr(mhpmcounter3h);
+	(void)read_csr(mhpmcounter4h);
+	(void)read_csr(mhpmcounter5h);
+	(void)read_csr(mhpmcounter6h);
+	(void)read_csr(mhpmcounter7h);
+	(void)read_csr(mhpmcounter8h);
+	(void)read_csr(mhpmcounter9h);
+	(void)read_csr(mhpmcounter10h);
+	(void)read_csr(mhpmcounter11h);
+	(void)read_csr(mhpmcounter12h);
+	(void)read_csr(mhpmcounter13h);
+	(void)read_csr(mhpmcounter14h);
+	(void)read_csr(mhpmcounter15h);
+	(void)read_csr(mhpmcounter16h);
+	(void)read_csr(mhpmcounter17h);
+	(void)read_csr(mhpmcounter18h);
+	(void)read_csr(mhpmcounter19h);
+	(void)read_csr(mhpmcounter20h);
+	(void)read_csr(mhpmcounter21h);
+	(void)read_csr(mhpmcounter22h);
+	(void)read_csr(mhpmcounter23h);
+	(void)read_csr(mhpmcounter24h);
+	(void)read_csr(mhpmcounter25h);
+	(void)read_csr(mhpmcounter26h);
+	(void)read_csr(mhpmcounter27h);
+	(void)read_csr(mhpmcounter28h);
+	(void)read_csr(mhpmcounter29h);
+	(void)read_csr(mhpmcounter30h);
+	(void)read_csr(mhpmcounter31h);
+
+	(void)read_csr(mcountinhibit);
+	(void)read_csr(mhpmevent3);
+	(void)read_csr(tselect);
+	(void)read_csr(tdata1);
+	(void)read_csr(dcsr);
+	(void)read_csr(dpc);
+	(void)read_csr(dscratch0);
+	(void)read_csr(dscratch1);
+	(void)read_csr(hazard3_csr_dmdata0);
+	(void)read_csr(hazard3_csr_meie0);
+	(void)read_csr(hazard3_csr_meip0);
+	(void)read_csr(hazard3_csr_mlei);
+
+	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("CSR was %03x\n", *(uint16_t*)(mepc + 2) >> 4);
+		mepc += 4;
+	}
+	else {
+		tb_puts("Exception on 16-bit instruction?!\n");
+		tb_exit(-1);
+	}
+	write_csr(mepc, mepc);
+}
diff --git a/test/sim/sw_testcases/ebreak.c b/test/sim/sw_testcases/ebreak.c
index 41f300e..cdb8c54 100644
--- a/test/sim/sw_testcases/ebreak.c
+++ b/test/sim/sw_testcases/ebreak.c
@@ -51,7 +51,7 @@ int main() {
 void __attribute__((interrupt)) handle_exception() {
 	tb_printf("mcause = %u\n", read_csr(mcause));
 	tb_printf("Offset into test: %u, ", read_csr(mepc) - (uintptr_t)&test);
-	if (*(uint16_t*)read_csr(mepc) & 0x3 == 0x3) {
+	if ((*(uint16_t*)read_csr(mepc) & 0x3) == 0x3) {
 		tb_puts("32-bit ebreak\n");
 		write_csr(mepc, read_csr(mepc) + 4);
 	}
diff --git a/test/sim/sw_testcases/illegal_instr.c b/test/sim/sw_testcases/illegal_instr.c
index a119e3a..f0f92b1 100644
--- a/test/sim/sw_testcases/illegal_instr.c
+++ b/test/sim/sw_testcases/illegal_instr.c
@@ -50,7 +50,7 @@ void __attribute__((interrupt)) handle_exception() {
 	tb_printf("Exception, mcause = %u\n", mcause);
 
 	uint16_t i0 = *(uint16_t*)mepc;
-	if (i0 & 0x3u == 0x3u) {
+	if ((i0 & 0x3u) == 0x3u) {
 		uint16_t i1 = *(uint16_t*)(mepc + 2);
 		tb_printf("32-bit illegal instruction: %04x%04x\n", i1, i0);
 		mepc += 4;
diff --git a/test/sim/sw_testcases/load_misalign_halfword.c b/test/sim/sw_testcases/load_misalign_halfword.c
index 0a7e901..d767043 100644
--- a/test/sim/sw_testcases/load_misalign_halfword.c
+++ b/test/sim/sw_testcases/load_misalign_halfword.c
@@ -58,7 +58,7 @@ int main() {
 void __attribute__((interrupt)) handle_exception() {
 	tb_printf("-> exception, mcause = %u\n", read_csr(mcause));
 	write_csr(mcause, 0);
-	if (*(uint16_t*)read_csr(mepc) & 0x3 == 0x3) {
+	if ((*(uint16_t*)read_csr(mepc) & 0x3) == 0x3) {
 		write_csr(mepc, read_csr(mepc) + 4);
 	}
 	else {
diff --git a/test/sim/sw_testcases/load_misalign_word.c b/test/sim/sw_testcases/load_misalign_word.c
index ee96471..19420ba 100644
--- a/test/sim/sw_testcases/load_misalign_word.c
+++ b/test/sim/sw_testcases/load_misalign_word.c
@@ -37,7 +37,7 @@ int main() {
 void __attribute__((interrupt)) handle_exception() {
 	tb_printf("-> exception, mcause = %u\n", read_csr(mcause));
 	write_csr(mcause, 0);
-	if (*(uint16_t*)read_csr(mepc) & 0x3 == 0x3) {
+	if ((*(uint16_t*)read_csr(mepc) & 0x3) == 0x3) {
 		write_csr(mepc, read_csr(mepc) + 4);
 	}
 	else {
diff --git a/test/sim/sw_testcases/load_store_fault.c b/test/sim/sw_testcases/load_store_fault.c
index dee7238..fd30d60 100644
--- a/test/sim/sw_testcases/load_store_fault.c
+++ b/test/sim/sw_testcases/load_store_fault.c
@@ -51,7 +51,7 @@ 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) {
+	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);
 	}
diff --git a/test/sim/sw_testcases/lr_sc_fault.c b/test/sim/sw_testcases/lr_sc_fault.c
index 2628254..e547fbb 100644
--- a/test/sim/sw_testcases/lr_sc_fault.c
+++ b/test/sim/sw_testcases/lr_sc_fault.c
@@ -68,7 +68,7 @@ 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) {
+	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);
 	}
diff --git a/test/sim/sw_testcases/lr_sc_smoke.c b/test/sim/sw_testcases/lr_sc_smoke.c
index 84ed4a3..b194039 100644
--- a/test/sim/sw_testcases/lr_sc_smoke.c
+++ b/test/sim/sw_testcases/lr_sc_smoke.c
@@ -24,6 +24,8 @@ int main() {
 		"lr.w %0, (%2)\n"
 		"nop\n"
 		"sc.w %1, %3, (%2)\n"
+		// Note the "&": this marks an "earlyclobber" operand, telling GCC it can't
+		// allocate this output to an input register. (particularly, %0 to %2)
 		: "=&r" (load_result), "=r" (success)
 		: "r" (&scratch[0]), "r" (0x5678)
 	);
diff --git a/test/sim/sw_testcases/store_misalign_halfword.c b/test/sim/sw_testcases/store_misalign_halfword.c
index 1f8762d..e06e334 100644
--- a/test/sim/sw_testcases/store_misalign_halfword.c
+++ b/test/sim/sw_testcases/store_misalign_halfword.c
@@ -37,7 +37,7 @@ int main() {
 void __attribute__((interrupt)) handle_exception() {
 	tb_printf("-> exception, mcause = %u\n", read_csr(mcause));
 	write_csr(mcause, 0);
-	if (*(uint16_t*)read_csr(mepc) & 0x3 == 0x3) {
+	if ((*(uint16_t*)read_csr(mepc) & 0x3) == 0x3) {
 		write_csr(mepc, read_csr(mepc) + 4);
 	}
 	else {
diff --git a/test/sim/sw_testcases/store_misalign_word.c b/test/sim/sw_testcases/store_misalign_word.c
index 13b562d..ccdaa1d 100644
--- a/test/sim/sw_testcases/store_misalign_word.c
+++ b/test/sim/sw_testcases/store_misalign_word.c
@@ -40,7 +40,7 @@ int main() {
 void __attribute__((interrupt)) handle_exception() {
 	tb_printf("-> exception, mcause = %u\n", read_csr(mcause));
 	write_csr(mcause, 0);
-	if (*(uint16_t*)read_csr(mepc) & 0x3 == 0x3) {
+	if ((*(uint16_t*)read_csr(mepc) & 0x3) == 0x3) {
 		write_csr(mepc, read_csr(mepc) + 4);
 	}
 	else {