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}