code wiki / (root) / nx_rvc_run_suite.nx

nx_rvc_run_suite.nx source

↩ module page · 443 lines · 21437 B

1// nx_rvc_run_suite.nx -- nishios NO3: run the published RISC-V architectural test suite on the 2// SOVEREIGN rv64im_min_sim and report the pass count PER TEST GROUP against the repo's Spike-generated 3// reference signatures. Watch symbol: rvc_run_suite. 4// 5// THE SUBJECT is our sim (rv64im_min_sim via nx_bootcap's ONE machine composition). The corpus is 6// third-party (riscv-non-isa/riscv-arch-test 2.7.4): the test .S sources are assembled by an 7// ORACLE/BUILD-TOOL gcc on the laptop (never on the sovereign chain) and the .reference_output 8// signatures are the repo's own Spike-generated vectors. So this gate compares OUR execution against 9// a published third-party reference -- exactly what "architectural compliance" means. Corpus is 10// pinned (riscv_arch_test.pin, pipe-row grammar, sha256 per file) so the population is fixed + citable. 11// 12// COMPLETION MARKER: RVMODEL_HALT writes 1 to the `tohost` symbol (a RAM word) then spins. We poll 13// that word; when nonzero the test has signalled done. Then we dump [begin_signature, end_signature) 14// as little-endian 32-bit words and compare, word for word, against the reference. 15// 16// VERDICTS (never one percentage; per group, partition printed and summed): 17// PASS completed (tohost set) AND every signature word matches 18// FAIL completed but a signature word differs (the sim executed something wrong) 19// UNSUPPORTED did not complete: an illegal/unimplemented instruction halted it, or it never wrote 20// tohost within the step budget (a hang) -- reported apart from FAIL because it is NOT 21// "the sim computed a wrong value", it is "the sim cannot run this test". 22// 23// A memory big enough for the whole image is DERIVED from the pinned mem_end (the linker's _end), never 24// guessed: the branch tests (jal/beq/...) place their signature ~586 KB above mem_base, so the fixed 25// 64 KB boot RAM cannot hold them -- nx_bootcap.bootcap_machine_mem takes the size (one composition). 26// license_tier: ORIGINAL (the debugger/assembler/references are third-party oracles; nothing of them ships) 27 28import "nx_syscalls.nx" 29import "nx_bootcap.nx" 30 31const RVC_MEM_BASE: i64 = 0x80000000 32const RVC_PAGE: i64 = 4096 33const RVC_TX_CAP: i64 = 4096 // UART tx scratch; arch-test emits no console output, this is unused headroom 34// A run bound, NAMED for its one purpose (guard a non-halting test). The largest pinned image is 35// jal-01 (~586 KB, ~146K instruction words); each arch-test case is straight-line or a bounded loop, 36// so 8,000,000 steps is >50x the largest instruction count. A test that has not written tohost by 37// then is looping -> reported UNSUPPORTED (STEPS-EXCEEDED), never PASS. Announced, never silent. 38const RVC_MAX_STEPS: i64 = 8000000 39const RVC_V_PASS: i64 = 0 40const RVC_V_FAIL: i64 = 1 41const RVC_V_UNSUP: i64 = 2 42// res[] slots 43const RVC_R_VERDICT: i64 = 0 44const RVC_R_MISMATCH:i64 = 1 // first differing word index, or -1 45const RVC_R_STEPS: i64 = 2 46const RVC_R_HALTCODE:i64 = 3 47const RVC_R_DONE: i64 = 4 // 1 if tohost was set 48const RVC_R_NWORDS: i64 = 5 49const RVC_R_GOT: i64 = 6 // first differing word: what the sim produced 50const RVC_R_EXP: i64 = 7 // first differing word: what the reference holds 51const RVC_R_N: i64 = 8 52 53func rvc_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 54func rvc_num(v: i64) -> i64 { 55 let t: *u8 = sys_mmap(32) 56 var m: i64 = v 57 var neg: i64 = 0 58 if m < 0 { neg = 1; m = 0 - m } 59 var k: i64 = 0 60 if m == 0 { t[0] = 48 as u8; k = 1 } 61 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 62 let o: *u8 = sys_mmap(34) 63 var p: i64 = 0 64 if neg == 1 { o[0] = 45 as u8; p = 1 } 65 var i: i64 = 0 66 while i < k { o[p] = t[k-1-i]; p = p + 1; i = i + 1 } 67 sys_write(1, o, p) 68 return 0 69} 70 71// print a 32-bit value as 8 lowercase hex digits (the reference file's own format) 72func rvc_hex32(v: i64) -> i64 { 73 let b: *u8 = sys_mmap(8) 74 var i: i64 = 0 75 while i < 8 { 76 let nib: i64 = (v >> ((7 - i) * 4)) & 15 77 if nib < 10 { b[i] = (48 + nib) as u8 } else { b[i] = (87 + nib) as u8 } 78 i = i + 1 79 } 80 sys_write(1, b, 8) 81 return 0 82} 83 84// round v up to a multiple of m 85func rvc_roundup(v: i64, m: i64) -> i64 { return ((v + m - 1) / m) * m } 86 87// parse hex from buf starting at pos, skipping an optional 0x; stop at first non-hex. returns value; writes end pos to endp[0] 88func rvc_parse_hex(buf: *u8, pos: i64, endp: *i64) -> i64 { 89 var p: i64 = pos 90 if buf[p] == (48 as u8) { if buf[p+1] == (120 as u8) { p = p + 2 } } // "0x" 91 var v: i64 = 0 92 var any: i64 = 0 93 while 1 == 1 { 94 let c: i64 = buf[p] as i64 95 var d: i64 = -1 96 if c >= 48 { if c <= 57 { d = c - 48 } } // 0-9 97 if c >= 97 { if c <= 102 { d = c - 87 } } // a-f 98 if c >= 65 { if c <= 70 { d = c - 55 } } // A-F 99 if d < 0 { endp[0] = p; return v } 100 v = (v * 16) + d 101 p = p + 1 102 any = any + 1 103 } 104 endp[0] = p 105 return v 106} 107 108// find the next '|' at or after pos, return its index (or the line-end); caller advances past it 109func rvc_next_bar(buf: *u8, pos: i64) -> i64 { 110 var p: i64 = pos 111 while buf[p] != (124 as u8) { if buf[p] == (10 as u8) { return p } if buf[p] == (0 as u8) { return p } p = p + 1 } 112 return p 113} 114 115// copy a NUL-terminated string s into dst at off, return new off 116func rvc_cat(dst: *u8, off: i64, s: *u8) -> i64 { 117 var i: i64 = 0; var p: i64 = off 118 while s[i] != (0 as u8) { dst[p] = s[i]; p = p + 1; i = i + 1 } 119 return p 120} 121// copy a field from buf[a..b) into dst at off, return new off 122func rvc_cat_field(dst: *u8, off: i64, buf: *u8, a: i64, b: i64) -> i64 { 123 var i: i64 = a; var p: i64 = off 124 while i < b { dst[p] = buf[i]; p = p + 1; i = i + 1 } 125 return p 126} 127 128// THE CORE: run one test image already in memory, compare its signature to ref_words[0..nref). 129// img/ilen = the flat binary loaded at RVC_MEM_BASE; entry/begin/end/tohost are ABSOLUTE guest addrs; 130// mem_need = highest address used (linker _end) so the RAM is sized to hold it. Fills res[RVC_R_*]. 131func rvc_run_img(img: *u8, ilen: i64, entry: i64, begin: i64, endsig: i64, tohost: i64, mem_need: i64, 132 ref_words: *i64, nref: i64, res: *i64) -> i64 { 133 res[RVC_R_VERDICT] = RVC_V_UNSUP 134 res[RVC_R_MISMATCH] = -1 135 res[RVC_R_STEPS] = 0 136 res[RVC_R_HALTCODE] = 0 137 res[RVC_R_DONE] = 0 138 let need: i64 = mem_need - RVC_MEM_BASE 139 var msz: i64 = rvc_roundup(need, RVC_PAGE) 140 if msz < rvc_roundup(ilen, RVC_PAGE) { msz = rvc_roundup(ilen, RVC_PAGE) } 141 let tx: *u8 = sys_mmap(RVC_TX_CAP) 142 let sim: *NxRv64imSim = bootcap_machine_mem(img, ilen, tx, RVC_TX_CAP, msz) 143 sim.pc = entry 144 var steps: i64 = 0 145 var done: i64 = 0 146 while steps < RVC_MAX_STEPS { 147 if sim.halted == 1 { break } 148 if nx_rv64im_sim_load64(sim, tohost) != 0 { done = 1; break } 149 nx_rv64im_sim_step(sim) 150 steps = steps + 1 151 } 152 res[RVC_R_STEPS] = steps 153 res[RVC_R_HALTCODE] = sim.halt_code 154 res[RVC_R_DONE] = done 155 let nwords: i64 = (endsig - begin) / 4 156 res[RVC_R_NWORDS] = nwords 157 if done == 0 { 158 // did not signal completion: illegal/unimplemented instruction, or step budget exhausted 159 res[RVC_R_VERDICT] = RVC_V_UNSUP 160 return 0 161 } 162 // signature-size disagreement is itself a FAIL (the sim wrote a different-length region is impossible, 163 // but a bad pin would be caught here) 164 var mism: i64 = -1 165 var i: i64 = 0 166 let n: i64 = nwords 167 res[RVC_R_GOT] = 0 168 res[RVC_R_EXP] = 0 169 while i < n { 170 let got: i64 = nx_rv64im_sim_load32(sim, begin + (i * 4)) & 0xffffffff 171 var exp: i64 = 0 172 if i < nref { exp = ref_words[i] & 0xffffffff } 173 if got != exp { mism = i; res[RVC_R_GOT] = got; res[RVC_R_EXP] = exp; i = n } else { i = i + 1 } 174 } 175 if nwords != nref { if mism < 0 { mism = nwords } } 176 res[RVC_R_MISMATCH] = mism 177 if mism < 0 { res[RVC_R_VERDICT] = RVC_V_PASS } else { res[RVC_R_VERDICT] = RVC_V_FAIL } 178 return 0 179} 180 181// parse a reference file (8-hex-per-line words) into ref_words; returns count 182func rvc_parse_ref(buf: *u8, len: i64, ref_words: *i64, cap: i64) -> i64 { 183 var p: i64 = 0 184 var n: i64 = 0 185 let endp: *i64 = sys_mmap(8) as *i64 186 while p < len { 187 if buf[p] == (10 as u8) { p = p + 1 } 188 else { 189 // parse 8 hex digits (a word); the arch-test dumps fixed 8-char words 190 let v: i64 = rvc_parse_hex(buf, p, endp) 191 if n < cap { ref_words[n] = v; n = n + 1 } 192 p = endp[0] 193 while p < len { if buf[p] == (10 as u8) { p = p + 1; break } p = p + 1 } 194 } 195 } 196 return n 197} 198 199// run one test named by pin fields; root = corpus dir. builds "<root>/<group>/<test>.bin" and ".ref". 200func rvc_run_one(root: *u8, group: *u8, test: *u8, entry: i64, begin: i64, endsig: i64, tohost: i64, 201 mem_need: i64, res: *i64) -> i64 { 202 let binp: *u8 = sys_mmap(512) 203 var o: i64 = rvc_cat(binp, 0, root) 204 o = rvc_cat(binp, o, "/\x00" as *u8) 205 o = rvc_cat(binp, o, group) 206 o = rvc_cat(binp, o, "/\x00" as *u8) 207 o = rvc_cat(binp, o, test) 208 let refp: *u8 = sys_mmap(512) 209 var r: i64 = rvc_cat(refp, 0, binp) // copy path-so-far 210 o = rvc_cat(binp, o, ".bin\x00" as *u8) 211 r = rvc_cat(refp, r, ".ref\x00" as *u8) 212 let lenp: *i64 = sys_mmap(8) as *i64 213 let img: *u8 = sys_read_file(binp, lenp) 214 let ilen: i64 = lenp[0] 215 if (img as i64) == 0 { res[RVC_R_VERDICT] = RVC_V_UNSUP; res[RVC_R_DONE] = 0; return -1 } 216 let rlenp: *i64 = sys_mmap(8) as *i64 217 let rbuf: *u8 = sys_read_file(refp, rlenp) 218 let rlen: i64 = rlenp[0] 219 if (rbuf as i64) == 0 { res[RVC_R_VERDICT] = RVC_V_UNSUP; res[RVC_R_DONE] = 0; return -1 } 220 let refcap: i64 = (rlen / 2) + 16 221 let ref_words: *i64 = sys_mmap(8 * refcap) as *i64 222 let nref: i64 = rvc_parse_ref(rbuf, rlen, ref_words, refcap) 223 rvc_run_img(img, ilen, entry, begin, endsig, tohost, mem_need, ref_words, nref, res) 224 return 0 225} 226 227// group tally: per distinct group, pass/fail/unsup/total. We iterate the pin twice: this simple model 228// prints per-test as it goes and accumulates into a small fixed set of group counters keyed by name. 229const RVC_MAXGROUPS: i64 = 16 230const RVC_GNAME_CAP: i64 = 32 231 232// suite driver: read the pin, run every row, print per-test + per-group + grand partition. Returns 233// number of tests run (0 => empty corpus => the caller treats it as RED). 234func rvc_run_suite(pinpath: *u8, root: *u8) -> i64 { 235 let lenp: *i64 = sys_mmap(8) as *i64 236 let pin: *u8 = sys_read_file(pinpath, lenp) 237 let plen: i64 = lenp[0] 238 if (pin as i64) == 0 { rvc_puts("RVC-SUITE pin-unreadable path="); rvc_puts(pinpath); rvc_puts("\n" as *u8); return 0 } 239 // group counters 240 let gnames: *u8 = sys_mmap(RVC_MAXGROUPS * RVC_GNAME_CAP) 241 let gpass: *i64 = sys_mmap(8 * RVC_MAXGROUPS) as *i64 242 let gfail: *i64 = sys_mmap(8 * RVC_MAXGROUPS) as *i64 243 let gunsup: *i64 = sys_mmap(8 * RVC_MAXGROUPS) as *i64 244 let gtot: *i64 = sys_mmap(8 * RVC_MAXGROUPS) as *i64 245 var ngroups: i64 = 0 246 let res: *i64 = sys_mmap(8 * RVC_R_N) as *i64 247 let gbuf: *u8 = sys_mmap(RVC_GNAME_CAP) 248 let tbuf: *u8 = sys_mmap(RVC_GNAME_CAP * 4) 249 var run: i64 = 0 250 var p: i64 = 0 251 let endp: *i64 = sys_mmap(8) as *i64 252 while p < plen { 253 // skip comment / blank lines: a row starts with "row|" 254 if pin[p] == (114 as u8) { // 'r' 255 if pin[p+1] == (111 as u8) { if pin[p+2] == (119 as u8) { if pin[p+3] == (124 as u8) { 256 // parse: row|group|test|entry|begin|end|tohost|mem_end|binsha|refsha 257 var f: i64 = p + 4 258 var b: i64 = rvc_next_bar(pin, f) 259 // group 260 var gi: i64 = 0 261 var k: i64 = f 262 while k < b { gbuf[gi] = pin[k]; gi = gi + 1; k = k + 1 } 263 gbuf[gi] = 0 as u8 264 f = b + 1 265 b = rvc_next_bar(pin, f) 266 // test 267 var ti: i64 = 0 268 k = f 269 while k < b { tbuf[ti] = pin[k]; ti = ti + 1; k = k + 1 } 270 tbuf[ti] = 0 as u8 271 f = b + 1 272 let entry: i64 = rvc_parse_hex(pin, f, endp); f = endp[0]; f = rvc_next_bar(pin, f) + 1 273 let begin: i64 = rvc_parse_hex(pin, f, endp); f = endp[0]; f = rvc_next_bar(pin, f) + 1 274 let endsig: i64 = rvc_parse_hex(pin, f, endp); f = endp[0]; f = rvc_next_bar(pin, f) + 1 275 let tohost: i64 = rvc_parse_hex(pin, f, endp); f = endp[0]; f = rvc_next_bar(pin, f) + 1 276 let memend: i64 = rvc_parse_hex(pin, f, endp); f = endp[0] 277 rvc_run_one(root, gbuf, tbuf, entry, begin, endsig, tohost, memend, res) 278 run = run + 1 279 // find/insert group 280 var g: i64 = 0 281 var found: i64 = -1 282 while g < ngroups { 283 var same: i64 = 1 284 var c: i64 = 0 285 while c < RVC_GNAME_CAP { 286 let a: i64 = gnames[g*RVC_GNAME_CAP + c] as i64 287 let bb: i64 = gbuf[c] as i64 288 if a != bb { same = 0; c = RVC_GNAME_CAP } else { if a == 0 { c = RVC_GNAME_CAP } else { c = c + 1 } } 289 } 290 if same == 1 { found = g; g = ngroups } else { g = g + 1 } 291 } 292 if found < 0 { 293 found = ngroups 294 var c2: i64 = 0 295 while c2 < RVC_GNAME_CAP { gnames[found*RVC_GNAME_CAP + c2] = gbuf[c2]; c2 = c2 + 1 } 296 gpass[found] = 0; gfail[found] = 0; gunsup[found] = 0; gtot[found] = 0 297 ngroups = ngroups + 1 298 } 299 gtot[found] = gtot[found] + 1 300 let v: i64 = res[RVC_R_VERDICT] 301 if v == RVC_V_PASS { gpass[found] = gpass[found] + 1 } 302 if v == RVC_V_FAIL { gfail[found] = gfail[found] + 1 } 303 if v == RVC_V_UNSUP { gunsup[found] = gunsup[found] + 1 } 304 rvc_puts(" group=" as *u8); rvc_puts(gbuf); rvc_puts(" test=" as *u8); rvc_puts(tbuf) 305 rvc_puts(" verdict=" as *u8) 306 if v == RVC_V_PASS { rvc_puts("PASS" as *u8) } 307 if v == RVC_V_FAIL { rvc_puts("FAIL" as *u8) } 308 if v == RVC_V_UNSUP { rvc_puts("UNSUPPORTED" as *u8) } 309 rvc_puts(" words=" as *u8); rvc_num(res[RVC_R_NWORDS]) 310 if v == RVC_V_FAIL { rvc_puts(" first_mismatch_word=" as *u8); rvc_num(res[RVC_R_MISMATCH]); rvc_puts(" got=" as *u8); rvc_hex32(res[RVC_R_GOT]); rvc_puts(" expected=" as *u8); rvc_hex32(res[RVC_R_EXP]) } 311 if v == RVC_V_UNSUP { rvc_puts(" done=" as *u8); rvc_num(res[RVC_R_DONE]); rvc_puts(" halt_code=" as *u8); rvc_num(res[RVC_R_HALTCODE]); rvc_puts(" steps=" as *u8); rvc_num(res[RVC_R_STEPS]) } 312 rvc_puts("\n" as *u8) 313 }}} 314 } 315 // advance to next line 316 while p < plen { if pin[p] == (10 as u8) { p = p + 1; break } p = p + 1 } 317 } 318 // per-group summary + grand partition 319 rvc_puts("\n" as *u8) 320 var tp: i64 = 0; var tf: i64 = 0; var tu: i64 = 0; var tt: i64 = 0 321 var g2: i64 = 0 322 while g2 < ngroups { 323 rvc_puts("GROUP " as *u8) 324 rvc_puts((gnames + (g2*RVC_GNAME_CAP))) 325 rvc_puts(" pass=" as *u8); rvc_num(gpass[g2]) 326 rvc_puts(" fail=" as *u8); rvc_num(gfail[g2]) 327 rvc_puts(" unsupported=" as *u8); rvc_num(gunsup[g2]) 328 rvc_puts(" total=" as *u8); rvc_num(gtot[g2]) 329 rvc_puts(" (pass+fail+unsup=" as *u8); rvc_num(gpass[g2]+gfail[g2]+gunsup[g2]); rvc_puts(")\n" as *u8) 330 tp = tp + gpass[g2]; tf = tf + gfail[g2]; tu = tu + gunsup[g2]; tt = tt + gtot[g2] 331 g2 = g2 + 1 332 } 333 rvc_puts("SUITE pass=" as *u8); rvc_num(tp) 334 rvc_puts(" fail=" as *u8); rvc_num(tf) 335 rvc_puts(" unsupported=" as *u8); rvc_num(tu) 336 rvc_puts(" total=" as *u8); rvc_num(tt) 337 rvc_puts(" partition_ok=" as *u8) 338 if (tp + tf + tu) == tt { rvc_puts("1" as *u8) } else { rvc_puts("0" as *u8) } 339 rvc_puts(" groups=" as *u8); rvc_num(ngroups) 340 rvc_puts("\n" as *u8) 341 return run 342} 343 344// ---- SELFTEST: a self-contained synthetic RV64 program proving the runner's mechanism WITHOUT the 345// third-party corpus, so the gate is non-vacuous on the NAS (no gcc there). The program stores two 346// known words to a signature region, writes 1 to tohost, then loops. Hand-encoded RV64 (verified by 347// running it: if an encoding is wrong the good case does not PASS). 348func rvc_st_wr32(buf: *u8, off: i64, w: i64) -> i64 { 349 buf[off] = (w & 0xff) as u8 350 buf[off+1] = ((w >> 8) & 0xff) as u8 351 buf[off+2] = ((w >> 16) & 0xff) as u8 352 buf[off+3] = ((w >> 24) & 0xff) as u8 353 return 0 354} 355 356// build the synthetic image into buf (>= 0x210 bytes); returns image length. first_illegal!=0 replaces 357// the first instruction with an illegal word to exercise the UNSUPPORTED path. 358func rvc_st_build(buf: *u8, first_illegal: i64) -> i64 { 359 var i: i64 = 0 360 while i < 0x210 { buf[i] = 0 as u8; i = i + 1 } 361 // code at 0x00 (guest 0x80000000) 362 rvc_st_wr32(buf, 0, 0x00000317) // auipc x6, 0 -> x6 = pc = 0x80000000 363 rvc_st_wr32(buf, 4, 0x01100393) // addi x7, x0, 0x11 364 rvc_st_wr32(buf, 8, 0x10732023) // sw x7, 0x100(x6) -> sig[0] = 0x11 365 rvc_st_wr32(buf, 12, 0x02200393) // addi x7, x0, 0x22 366 rvc_st_wr32(buf, 16, 0x10732223) // sw x7, 0x104(x6) -> sig[1] = 0x22 367 rvc_st_wr32(buf, 20, 0x00100393) // addi x7, x0, 1 368 rvc_st_wr32(buf, 24, 0x20732023) // sw x7, 0x200(x6) -> tohost = 1 369 rvc_st_wr32(buf, 28, 0x0000006f) // jal x0, 0 -> loop forever 370 if first_illegal != 0 { rvc_st_wr32(buf, 0, 0xffffffff) } // an illegal encoding halts the sim 371 return 0x210 372} 373 374func rvc_selftest() -> i64 { 375 let img: *u8 = sys_mmap(0x1000) 376 let res: *i64 = sys_mmap(8 * RVC_R_N) as *i64 377 let entry: i64 = 0x80000000 378 let begin: i64 = 0x80000100 379 let endsig: i64 = 0x80000108 // 2 words 380 let tohost: i64 = 0x80000200 381 let memend: i64 = 0x80001000 382 // good reference 383 let refg: *i64 = sys_mmap(16) as *i64 384 refg[0] = 0x11; refg[1] = 0x22 385 // corrupt reference (second word wrong) 386 let refc: *i64 = sys_mmap(16) as *i64 387 refc[0] = 0x11; refc[1] = 0x99 388 389 rvc_st_build(img, 0) 390 rvc_run_img(img, 0x210, entry, begin, endsig, tohost, memend, refg, 2, res) 391 let good_v: i64 = res[RVC_R_VERDICT] 392 let good_done: i64 = res[RVC_R_DONE] 393 394 rvc_st_build(img, 0) 395 rvc_run_img(img, 0x210, entry, begin, endsig, tohost, memend, refc, 2, res) 396 let corrupt_v: i64 = res[RVC_R_VERDICT] 397 let corrupt_mism: i64 = res[RVC_R_MISMATCH] 398 399 rvc_st_build(img, 1) 400 rvc_run_img(img, 0x210, entry, begin, endsig, tohost, memend, refg, 2, res) 401 let illegal_v: i64 = res[RVC_R_VERDICT] 402 let illegal_done: i64 = res[RVC_R_DONE] 403 404 // empty suite -> a nonexistent pin returns 0 runs (RED at the caller) 405 let empty_run: i64 = rvc_run_suite("/tmp/nx_rvc_gate/nonexistent.pin\x00" as *u8, "/tmp/nx_rvc_gate\x00" as *u8) 406 407 rvc_puts("RVC-SELFTEST good_verdict=" as *u8); rvc_num(good_v); rvc_puts(" good_done=" as *u8); rvc_num(good_done); rvc_puts("\n" as *u8) 408 rvc_puts("RVC-SELFTEST corrupt_verdict=" as *u8); rvc_num(corrupt_v); rvc_puts(" corrupt_mismatch_word=" as *u8); rvc_num(corrupt_mism); rvc_puts("\n" as *u8) 409 rvc_puts("RVC-SELFTEST illegal_verdict=" as *u8); rvc_num(illegal_v); rvc_puts(" illegal_done=" as *u8); rvc_num(illegal_done); rvc_puts("\n" as *u8) 410 rvc_puts("RVC-SELFTEST empty_suite_runs=" as *u8); rvc_num(empty_run); rvc_puts("\n" as *u8) 411 // internal aggregate verdict: good PASS(0), corrupt FAIL(1), illegal UNSUP(2), empty 0 runs 412 var ok: i64 = 1 413 if good_v != RVC_V_PASS { ok = 0 } 414 if good_done != 1 { ok = 0 } 415 if corrupt_v != RVC_V_FAIL { ok = 0 } 416 if illegal_v != RVC_V_UNSUP { ok = 0 } 417 if empty_run != 0 { ok = 0 } 418 rvc_puts("RVC-SELFTEST verdict=" as *u8) 419 if ok == 1 { rvc_puts("GREEN\n" as *u8) } else { rvc_puts("RED\n" as *u8) } 420 if ok == 1 { return 0 } 421 return 1 422} 423 424// rvc_run_suite is the watch symbol; this predicate names it for absence probes and keeps a live ref. 425func rvc_run_suite_live() -> i64 { return RVC_V_PASS } 426 427func main(argc: i64, argv: *i64) -> i64 { 428 if argc >= 2 { 429 let a: *u8 = argv[1] as *u8 430 // "selftest" (a[1]='e') vs "suite" (a[1]='u'): dispatch on the second char 431 if a[0] == (115 as u8) { if a[1] == (101 as u8) { return rvc_selftest() } } 432 } 433 // suite mode: nx_rvc_run_suite suite <pin> <root> (or default pin+root) 434 var pin: *u8 = "bench/riscv-arch-test/riscv_arch_test.pin\x00" as *u8 435 var root: *u8 = "bench/riscv-arch-test\x00" as *u8 436 if argc >= 3 { pin = argv[2] as *u8 } 437 if argc >= 4 { root = argv[3] as *u8 } 438 rvc_puts("=== nx_rvc_run_suite -- RISC-V architectural compliance on rv64im_min_sim (pin=" as *u8) 439 rvc_puts(pin); rvc_puts(")\n\n" as *u8) 440 let run: i64 = rvc_run_suite(pin, root) 441 if run == 0 { rvc_puts("SUITE empty-corpus verdict=RED\n" as *u8); return 1 } 442 return 0 443}