code wiki / _hdl_build / nx_rv64_atomic_gate.nx

nx_rv64_atomic_gate.nx source

↩ module page · 363 lines · 19764 B

1// nx_rv64_atomic_gate.nx -- LN34 REFEREE (lang.plan): the IR atomic family on the rv64 lane, from the byte up. 2// 3// ONE FAMILY, THREE LOWERINGS: __atomic_load/store/cas/faa_i64 + __atomic_fence (nx_atom.nx wraps them) lower 4// to OP_ATOMIC_* in the IR; x86-64 lowers those to LOCK instructions (pre-existing), the wasm lane to the 0xFE 5// family (nx_wasm_threads_gate), and -- proven HERE -- the rv64 backend (nx_riscv.nx) to RV64A: amoadd.d.aqrl, 6// an lr.d/sc.d.aqrl compare-and-swap loop, and the ISA manual's sequentially-consistent fence mappings. 7// FOUR LEGS, each composed IN-PROCESS so nothing is trusted by name: 8// A. the sovereign text assembler (nx_rv64_asm) encodes every new mnemonic to the word an independent 9// field-layout encoder predicts (funct5<<27 | aq<<26 | rl<<25 | rs2<<20 | rs1<<15 | funct3<<12 | rd<<7 | 0x2F; 10// fence = pred<<24 | succ<<20 | 0x0F), refuses a malformed fence, and now admits `.L<name>:` labels while 11// still skipping `.globl`-style directives; 12// B. the behavioural sim (rv64im_min_sim, the same instance the A-extension conformance gate drives) executes 13// the assembled program with the hand-computed register and memory results; 14// C. the compiler backend, driven exactly as nx_nxc drives it (lex -> parse -> opt -> regalloc -> emit_function), 15// lowers a fixture carrying every atomic op to those mnemonics and leaves no `# unhandled op`, while a plain 16// fixture carries none of them (the neutrality neg-control); 17// D. END TO END: the backend's emitted `tick` function is assembled by leg A's assembler and RUN on leg B's sim 18// with a0/a1 as arguments and ra pointing at a self-loop sentinel, and the sim's a0 equals the tick count. 19// nx_rv64_atomic_gate 20// license_tier: ORIGINAL No hw writes (Rule 26). 21import "nx_syscalls.nx" 22import "nx_gate_verdict.nx" 23import "nishi_hdl_primitives.nx" 24import "rv64im_min_decoder.nx" 25import "rv64im_min_alu.nx" 26import "rv64im_min_regfile.nx" 27import "rv64im_min_csr.nx" 28import "rv64im_min_clint.nx" 29import "rv64im_min_uart.nx" 30import "rv64im_min_virtio.nx" 31import "rv64im_min_sim.nx" 32import "nx_rv64_asm.nx" 33import "nx_types.nx" 34import "nx_lex_kinds.nx" 35import "nx_outbuf.nx" 36import "nx_ir.nx" 37import "nx_tokenizer.nx" 38import "nx_parse.nx" 39import "nx_opt.nx" 40import "nx_regalloc.nx" 41import "nx_riscv.nx" 42 43const GA_MEMB: i64 = 0x80000000 44const GA_MEMS: i64 = 65536 45const GA_CODE_CAP: i64 = 16384 // assembled words for one fixture function 46const GA_SCR_OFF: i64 = 0x2000 // the atomic cell, inside the sim memory 47const GA_SENTINEL_OFF: i64 = 0x3000 // `j .` the end-to-end run returns into 48const GA_STACK_OFF: i64 = 0xF000 // sp for the end-to-end run (grows down, well above code and cell) 49const GA_STEP_BUDGET: i64 = 200000 // instruction budget for the end-to-end run (a loop that never returns) 50const GA_TICKS: i64 = 1000 51const GA_TOKS: i64 = 262144 52const GA_ASM_CAP: i64 = 1048576 53const GA_FN_STRIDE: i64 = 176 // sizeof(Function), the stride nx_nxc walks with 54const GA_LOCS_STRIDE: i64 = 24 55const GA_FRAME_ALIGN: i64 = 16 // the two frame parameters nx_nxc passes to emit_function 56const GA_RA_SLOT: i64 = 8 57// ABI register numbers the backend's lowering uses 58const GA_X_RA: i64 = 1 59const GA_X_SP: i64 = 2 60const GA_X_A0: i64 = 10 61const GA_X_A1: i64 = 11 62const GA_X_A7: i64 = 17 63const GA_X_T4: i64 = 29 64const GA_X_T5: i64 = 30 65const GA_X_T6: i64 = 31 66// field layout of the RV64A / FENCE words -- the INDEPENDENT encoder leg A is measured against 67const GA_OP_AMO: i64 = 0x2F 68const GA_OP_FENCE: i64 = 0x0F 69const GA_F5_ADD: i64 = 0 70const GA_F5_SWAP: i64 = 1 71const GA_F5_LR: i64 = 2 72const GA_F5_SC: i64 = 3 73const GA_F3_D: i64 = 3 74const GA_F3_W: i64 = 2 75const GA_SH_F5: i64 = 27 76const GA_SH_AQ: i64 = 26 77const GA_SH_RL: i64 = 25 78const GA_SH_RS2: i64 = 20 79const GA_SH_RS1: i64 = 15 80const GA_SH_F3: i64 = 12 81const GA_SH_RD: i64 = 7 82const GA_SH_PRED: i64 = 24 83const GA_SH_SUCC: i64 = 20 84const GA_IORW: i64 = 15 85const GA_RW: i64 = 3 86const GA_R: i64 = 2 87const GA_W: i64 = 1 88// hand-computed values for leg B (see the program text below) 89const GA_CELL_INIT: i64 = 5 90const GA_ADDEND: i64 = 3 91const GA_EXP_T6_AFTER_ADD: i64 = 5 92const GA_EXP_A7_AFTER_LR: i64 = 8 93const GA_EXP_CELL_FINAL: i64 = 8 94const GA_EXP_SC_FAIL: i64 = 1 95const GA_EXP_CAS: i64 = 1009 96const GA_JAL_SELF: i64 = 0x6F // jal x0, 0 -- a self-loop the end-to-end run returns into 97 98const GA_FX_ATOMIC: *u8 = "const MO: i64 = 5\nfunc tick(p: i64, n: i64) -> i64 {\n let c: *i64 = p as *i64\n var i: i64 = 0\n while i < n { __atomic_faa_i64(c, 1, MO); i = i + 1 }\n return __atomic_load_i64(c, MO)\n}\nfunc probe_cas(p: i64) -> i64 {\n let c: *i64 = p as *i64\n let s: i64 = __atomic_store_i64(c, 7, MO)\n let a: i64 = __atomic_cas_i64(c, 7, 9, MO)\n let b: i64 = __atomic_cas_i64(c, 7, 11, MO)\n let f: i64 = __atomic_fence(MO)\n let v: i64 = __atomic_load_i64(c, MO)\n return a * 1000 + b * 100 + v + s + f\n}\n" 99const GA_FX_PLAIN: *u8 = "func plain(a: i64, b: i64) -> i64 { return a + b }\n" 100const GA_PROG_B: *u8 = "amoadd.d.aqrl t6, t5, (t4)\nlr.d.aqrl a7, (t4)\nsc.d.aqrl t5, t6, (t4)\nfence rw, rw\namoswap.d.aqrl t6, a7, (t4)\nsc.d.aqrl t5, t6, (t4)\n" 101const GA_PROG_B_N: i64 = 6 102const GA_PROG_LABELS: *u8 = ".globl top\ntop:\n.Lloop_bb0:\n addi t5, t5, -1\n bne t5, zero, .Lloop_bb0\n ret\n" 103const GA_PROG_LABELS_N: i64 = 3 104const GA_PROG_BADFENCE: *u8 = "fence rw\n" 105 106func ga_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 107func ga_has(buf: *u8, n: i64, needle: *u8) -> i64 { 108 let m: i64 = ga_slen(needle) 109 if m == 0 { return 0 } 110 if (buf as i64) == 0 { return 0 } 111 var i: i64 = 0 112 while i + m <= n { 113 var k: i64 = 0 114 var ok: i64 = 1 115 while k < m { if buf[i + k] != needle[k] { ok = 0; k = m } k = k + 1 } 116 if ok == 1 { return 1 } 117 i = i + 1 118 } 119 return 0 120} 121func ga_word(buf: *u8, off: i64) -> i64 { 122 return (buf[off] as i64) | ((buf[off + 1] as i64) << 8) | ((buf[off + 2] as i64) << 16) | ((buf[off + 3] as i64) << 24) 123} 124func ga_wr64(mem: *u8, off: i64, v: i64) -> i64 { var i: i64 = 0; while i < 8 { mem[off + i] = ((v >> (i * 8)) & 0xff) as u8; i = i + 1 } return 0 } 125func ga_rd64(mem: *u8, off: i64) -> i64 { var v: i64 = 0; var i: i64 = 0; while i < 8 { v = v | ((mem[off + i] as i64) << (i * 8)); i = i + 1 } return v } 126// the independent encoders 127func ga_amo(f5: i64, aq: i64, rl: i64, rs2: i64, rs1: i64, f3: i64, rd: i64) -> i64 { 128 return (f5 << GA_SH_F5) | (aq << GA_SH_AQ) | (rl << GA_SH_RL) | (rs2 << GA_SH_RS2) | (rs1 << GA_SH_RS1) | (f3 << GA_SH_F3) | (rd << GA_SH_RD) | GA_OP_AMO 129} 130func ga_fence(pred: i64, succ: i64) -> i64 { return (pred << GA_SH_PRED) | (succ << GA_SH_SUCC) | GA_OP_FENCE } 131 132// a fresh sim over a fresh memory: code at offset 0, sp/args/ra preset by the caller through the regfile 133struct GaSim { 134 sim: *NxRv64imSim, 135 rf: *NxRv64imRegfile, 136 mem: *u8, 137} 138func ga_sim_new() -> *GaSim { 139 let g: *GaSim = sys_mmap(64) as *GaSim 140 let rf_st: *i64 = sys_mmap(8 * NX_RV64IM_RF_N_REGS) as *i64 141 let csr_st: *i64 = sys_mmap(8 * NX_CSR_SLOT_N) as *i64 142 let clint_st: *i64 = sys_mmap(8 * NX_CLINT_SLOT_N) as *i64 143 let uart_st: *i64 = sys_mmap(8 * NX_UART_SLOT_N) as *i64 144 let tx: *u8 = sys_mmap(64) 145 let mem: *u8 = sys_mmap(GA_MEMS) 146 let rf: *NxRv64imRegfile = sys_mmap(64) as *NxRv64imRegfile 147 let csr: *NxRv64imCsrFile = sys_mmap(64) as *NxRv64imCsrFile 148 let clint: *NxClint = sys_mmap(64) as *NxClint 149 let uart: *NxUart = sys_mmap(64) as *NxUart 150 let sim: *NxRv64imSim = sys_mmap(256) as *NxRv64imSim 151 nx_rv64im_rf_init(rf, rf_st); nx_rv64im_csr_init(csr, csr_st, 0); nx_clint_init(clint, clint_st); nx_uart_init(uart, uart_st, tx, 64) 152 nx_rv64im_sim_init(sim, rf, csr, clint, uart, GA_MEMB, mem, GA_MEMS, 0) 153 g.sim = sim 154 g.rf = rf 155 g.mem = mem 156 return g 157} 158 159// the compiler front half, exactly as nx_nxc drives it, into one asm text buffer 160func ga_compile(src: *u8, o: *OutBuf) -> i64 { 161 let toks: *Tok = lex_source(src, GA_TOKS) 162 if toks == (0 as *Tok) { return 0 - 1 } 163 let m: *Module = parse_module(toks, 0 as *Module) 164 if m == (0 as *Module) { return 0 - 2 } 165 var fi: i64 = 0 166 while fi < m.n_functions { 167 let fn_base: i64 = m.functions as i64 168 let f: *Function = (fn_base + fi * GA_FN_STRIDE) as *Function 169 let fn_name: *u8 = f.name_start as *u8 170 opt_run(f) 171 let locs: *ValueLoc = sys_mmap(f.n_values * GA_LOCS_STRIDE + 64) as *ValueLoc 172 let cs: *i64 = sys_mmap(16) as *i64 173 let cs_fpr: *i64 = sys_mmap(16) as *i64 174 let sb: *i64 = sys_mmap(16) as *i64 175 *cs = 0; *cs_fpr = 0; *sb = 0 176 regalloc_function(f, locs, cs, cs_fpr, sb) 177 emit_function(f, locs, o, fn_name, GA_FRAME_ALIGN, GA_RA_SLOT, *cs, *cs_fpr) 178 fi = fi + 1 179 } 180 return m.n_functions 181} 182// find the byte offset of `<name>:` at line start in an asm text (the function's entry), or -1 183func ga_label_off(txt: *u8, n: i64, name: *u8) -> i64 { 184 let m: i64 = ga_slen(name) 185 var i: i64 = 0 186 while i + m + 1 <= n { 187 var at_line: i64 = 0 188 if i == 0 { at_line = 1 } else { if txt[i - 1] == (10 as u8) { at_line = 1 } } 189 if at_line == 1 { 190 var k: i64 = 0 191 var ok: i64 = 1 192 while k < m { if txt[i + k] != name[k] { ok = 0; k = m } k = k + 1 } 193 if ok == 1 { if txt[i + m] == (58 as u8) { return i } } 194 } 195 i = i + 1 196 } 197 return 0 - 1 198} 199// the code address of a label inside an assembled text: count encodable lines before it (the assembler's own 200// rule -- comment and directive lines take no space, label lines take no space, everything else is 4 bytes) 201func ga_label_addr(txt: *u8, n: i64, name: *u8) -> i64 { 202 let target: i64 = ga_label_off(txt, n, name) 203 if target < 0 { return 0 - 1 } 204 var pos: i64 = 0 205 var abs: i64 = 0 206 while pos < target { 207 // line extent [pos, le) 208 var le: i64 = pos 209 var scanning: i64 = 1 210 while scanning == 1 { if le >= n { scanning = 0 } else { if txt[le] == (10 as u8) { scanning = 0 } else { le = le + 1 } } } 211 // first non-blank byte 212 var fp: i64 = pos 213 var skipping: i64 = 1 214 while skipping == 1 { if fp >= le { skipping = 0 } else { if ra_isws(txt[fp] as i64) == 1 { fp = fp + 1 } else { skipping = 0 } } } 215 if fp < le { 216 let c0: i64 = txt[fp] as i64 217 var counts: i64 = 1 218 if c0 == 35 { counts = 0 } 219 if ra_is_directive(txt, fp, le) == 1 { counts = 0 } 220 // a label line (last non-blank byte ':') takes no space 221 var q: i64 = le - 1 222 var trimming: i64 = 1 223 while trimming == 1 { if q < fp { trimming = 0 } else { if ra_isws(txt[q] as i64) == 1 { q = q - 1 } else { trimming = 0 } } } 224 if q >= fp { if (txt[q] as i64) == 58 { counts = 0 } } 225 if counts == 1 { abs = abs + 4 } 226 } 227 pos = le + 1 228 } 229 return abs 230} 231func ga_puti(v: i64) -> i64 { 232 let b: *u8 = sys_mmap(32) 233 var x: i64 = v 234 var neg: i64 = 0 235 if x < 0 { neg = 1; x = 0 - x } 236 var i: i64 = 31 237 b[i] = 0 as u8 238 if x == 0 { i = i - 1; b[i] = 48 as u8 } 239 while x > 0 { i = i - 1; b[i] = (48 + x - (x / 10) * 10) as u8; x = x / 10 } 240 if neg == 1 { i = i - 1; b[i] = 45 as u8 } 241 sys_write(1, ((b as i64) + i) as *u8, 31 - i) 242 return 0 243} 244 245func main(argc: i64, argv: *i64) -> i64 { 246 let ctr: *i64 = gv_ctr() 247 gv_head("nx_rv64_atomic_gate -- LN34: the IR atomic family lowered to RV64A, assembled sovereign, executed on the sovereign sim" as *u8) 248 249 // ---- A. assembler vs the independent encoder ------------------------------------------------ 250 let code: *u8 = sys_mmap(GA_CODE_CAP) 251 let nb: i64 = rvasm_assemble_str(GA_PROG_B, code, GA_CODE_CAP) 252 gv_check("asm-encodes-the-six-atomic-lines-to-six-words" as *u8, nb == GA_PROG_B_N * 4, ctr) 253 gv_check("asm-amoadd-d-aqrl-t6-t5-t4-matches-the-field-layout" as *u8, ga_word(code, 0) == ga_amo(GA_F5_ADD, 1, 1, GA_X_T5, GA_X_T4, GA_F3_D, GA_X_T6), ctr) 254 gv_check("asm-lr-d-aqrl-a7-t4-matches-with-rs2-zero" as *u8, ga_word(code, 4) == ga_amo(GA_F5_LR, 1, 1, 0, GA_X_T4, GA_F3_D, GA_X_A7), ctr) 255 gv_check("asm-sc-d-aqrl-t5-t6-t4-matches" as *u8, ga_word(code, 8) == ga_amo(GA_F5_SC, 1, 1, GA_X_T6, GA_X_T4, GA_F3_D, GA_X_T5), ctr) 256 gv_check("asm-fence-rw-rw-matches-pred-succ-layout" as *u8, ga_word(code, 12) == ga_fence(GA_RW, GA_RW), ctr) 257 gv_check("asm-amoswap-d-aqrl-t6-a7-t4-matches" as *u8, ga_word(code, 16) == ga_amo(GA_F5_SWAP, 1, 1, GA_X_A7, GA_X_T4, GA_F3_D, GA_X_T6), ctr) 258 let code2: *u8 = sys_mmap(GA_CODE_CAP) 259 let nb2: i64 = rvasm_assemble_str("fence\nfence r, rw\nfence rw, w\namoadd.w t6, t5, (t4)\n" as *u8, code2, GA_CODE_CAP) 260 gv_check("asm-bare-fence-is-iorw-iorw-and-the-relaxed-forms-encode" as *u8, (nb2 == 16) * (ga_word(code2, 0) == ga_fence(GA_IORW, GA_IORW)) * (ga_word(code2, 4) == ga_fence(GA_R, GA_RW)) * (ga_word(code2, 8) == ga_fence(GA_RW, GA_W)) * (ga_word(code2, 12) == ga_amo(GA_F5_ADD, 0, 0, GA_X_T5, GA_X_T4, GA_F3_W, GA_X_T6)), ctr) 261 let code3: *u8 = sys_mmap(GA_CODE_CAP) 262 let nb3: i64 = rvasm_assemble_str(GA_PROG_BADFENCE, code3, GA_CODE_CAP) 263 gv_check("neg-control-asm-refuses-a-fence-with-one-set" as *u8, nb3 < 0, ctr) 264 let nb4: i64 = rvasm_assemble_str(GA_PROG_LABELS, code3, GA_CODE_CAP) 265 gv_check("asm-admits-dot-L-labels-and-skips-dot-directives" as *u8, nb4 == GA_PROG_LABELS_N * 4, ctr) 266 267 // ---- B. the sim executes the assembled program with the hand-computed results ---------------- 268 let g: *GaSim = ga_sim_new() 269 var i: i64 = 0 270 while i < nb { g.mem[i] = code[i]; i = i + 1 } 271 ga_wr64(g.mem, GA_SCR_OFF, GA_CELL_INIT) 272 nx_rv64im_rf_write(g.rf, GA_X_T4, GA_MEMB + GA_SCR_OFF) 273 nx_rv64im_rf_write(g.rf, GA_X_T5, GA_ADDEND) 274 var st: i64 = 0 275 while st < GA_PROG_B_N { nx_rv64im_sim_step(g.sim); st = st + 1 } 276 let t6: i64 = nx_rv64im_rf_read(g.rf, GA_X_T6) 277 let a7: i64 = nx_rv64im_rf_read(g.rf, GA_X_A7) 278 let t5: i64 = nx_rv64im_rf_read(g.rf, GA_X_T5) 279 let cell: i64 = ga_rd64(g.mem, GA_SCR_OFF) 280 gv_puts(" sim: t6=" as *u8); ga_puti(t6); gv_puts(" a7=" as *u8); ga_puti(a7); gv_puts(" t5=" as *u8); ga_puti(t5); gv_puts(" cell=" as *u8); ga_puti(cell); gv_puts("\n" as *u8) 281 gv_check("sim-lr-reads-the-amoadd-result-8" as *u8, a7 == GA_EXP_A7_AFTER_LR, ctr) 282 gv_check("sim-amoswap-old-value-proves-the-sc-wrote-5" as *u8, t6 == GA_EXP_T6_AFTER_ADD, ctr) 283 gv_check("sim-cell-ends-at-8-after-the-swap" as *u8, cell == GA_EXP_CELL_FINAL, ctr) 284 gv_check("neg-control-sim-sc-without-a-reservation-fails-1" as *u8, t5 == GA_EXP_SC_FAIL, ctr) 285 286 // ---- C. the backend lowers every atomic op ------------------------------------------------------ 287 let o: *OutBuf = out_new(GA_ASM_CAP) 288 let nf: i64 = ga_compile(GA_FX_ATOMIC, o) 289 gv_check("backend-compiles-the-atomic-fixture-two-functions" as *u8, nf == 2, ctr) 290 gv_check("backend-faa-lowers-to-amoadd-d-aqrl" as *u8, ga_has(o.buf, o.pos, "amoadd.d.aqrl t6, t5, (t4)" as *u8), ctr) 291 gv_check("backend-cas-lowers-to-an-lr-sc-aqrl-loop" as *u8, ga_has(o.buf, o.pos, "lr.d.aqrl t6, (t4)" as *u8) * ga_has(o.buf, o.pos, "sc.d.aqrl t6, a7, (t4)" as *u8) * ga_has(o.buf, o.pos, "_cas" as *u8), ctr) 292 gv_check("backend-load-and-store-carry-the-seq-cst-fences" as *u8, ga_has(o.buf, o.pos, "fence r, rw" as *u8) * ga_has(o.buf, o.pos, "fence rw, w" as *u8), ctr) 293 gv_check("backend-fence-lowers-to-fence-rw-rw" as *u8, ga_has(o.buf, o.pos, "fence rw, rw" as *u8), ctr) 294 gv_check("backend-leaves-no-unhandled-op" as *u8, 1 - ga_has(o.buf, o.pos, "# unhandled op" as *u8), ctr) 295 let op: *OutBuf = out_new(GA_ASM_CAP) 296 let npf: i64 = ga_compile(GA_FX_PLAIN, op) 297 gv_check("neg-control-plain-fixture-carries-no-atomic-or-fence" as *u8, (npf == 1) * (1 - ga_has(op.buf, op.pos, "amo" as *u8)) * (1 - ga_has(op.buf, op.pos, "lr.d" as *u8)) * (1 - ga_has(op.buf, op.pos, "fence" as *u8)), ctr) 298 299 // ---- D. end to end: assemble the backend's output and run it on the sim ------------------------ 300 let ocode: *u8 = sys_mmap(GA_CODE_CAP) 301 o.buf[o.pos] = 0 as u8 302 let onb: i64 = rvasm_assemble(o.buf, o.pos, ocode, GA_CODE_CAP) 303 if onb < 0 { 304 gv_puts(" ASSEMBLE FAILED rc=" as *u8); ga_puti(onb); gv_puts(" on the backend text below:\n" as *u8) 305 sys_write(1, o.buf, o.pos) 306 } 307 gv_check("e2e-backend-text-assembles-on-the-sovereign-assembler" as *u8, onb > 0, ctr) 308 let tick_at: i64 = ga_label_addr(o.buf, o.pos, "tick" as *u8) 309 let cas_at: i64 = ga_label_addr(o.buf, o.pos, "probe_cas" as *u8) 310 gv_check("e2e-both-entry-labels-resolve-to-code-addresses" as *u8, (tick_at >= 0) * (cas_at >= 0), ctr) 311 // run tick(cell, GA_TICKS) 312 let g2: *GaSim = ga_sim_new() 313 i = 0 314 while i < onb { g2.mem[i] = ocode[i]; i = i + 1 } 315 let jself: *u8 = sys_mmap(GA_CODE_CAP) 316 let jn: i64 = rvasm_assemble_str("sent:\n j sent\n" as *u8, jself, GA_CODE_CAP) 317 var jsent: i64 = 0 318 if jn == 4 { if ga_word(jself, 0) == GA_JAL_SELF { jsent = 1; ga_wr64(g2.mem, GA_SENTINEL_OFF, ga_word(jself, 0)) } } 319 gv_check("e2e-sentinel-self-jump-assembles-to-jal-zero-0" as *u8, jsent == 1, ctr) 320 ga_wr64(g2.mem, GA_SCR_OFF, 0) 321 nx_rv64im_rf_write(g2.rf, GA_X_SP, GA_MEMB + GA_STACK_OFF) 322 nx_rv64im_rf_write(g2.rf, GA_X_RA, GA_MEMB + GA_SENTINEL_OFF) 323 nx_rv64im_rf_write(g2.rf, GA_X_A0, GA_MEMB + GA_SCR_OFF) 324 nx_rv64im_rf_write(g2.rf, GA_X_A1, GA_TICKS) 325 g2.sim.pc = GA_MEMB + tick_at 326 var steps: i64 = 0 327 var running: i64 = 1 328 while running == 1 { 329 nx_rv64im_sim_step(g2.sim) 330 steps = steps + 1 331 if g2.sim.pc == GA_MEMB + GA_SENTINEL_OFF { running = 0 } 332 if steps >= GA_STEP_BUDGET { running = 0 } 333 if g2.sim.halted == 1 { running = 0 } 334 } 335 let ret: i64 = nx_rv64im_rf_read(g2.rf, GA_X_A0) 336 let cell2: i64 = ga_rd64(g2.mem, GA_SCR_OFF) 337 gv_puts(" e2e tick: steps=" as *u8); ga_puti(steps); gv_puts(" a0=" as *u8); ga_puti(ret); gv_puts(" cell=" as *u8); ga_puti(cell2); gv_puts(" returned=" as *u8); ga_puti(1 - running); gv_puts("\n" as *u8) 338 gv_check("e2e-tick-returns-through-ra-within-budget" as *u8, (steps < GA_STEP_BUDGET) * (g2.sim.pc == GA_MEMB + GA_SENTINEL_OFF), ctr) 339 gv_check("e2e-tick-1000-amoadd-reads-back-1000-in-a0-and-in-memory" as *u8, (ret == GA_TICKS) * (cell2 == GA_TICKS), ctr) 340 // run probe_cas(cell) 341 let g3: *GaSim = ga_sim_new() 342 i = 0 343 while i < onb { g3.mem[i] = ocode[i]; i = i + 1 } 344 ga_wr64(g3.mem, GA_SENTINEL_OFF, ga_word(jself, 0)) 345 nx_rv64im_rf_write(g3.rf, GA_X_SP, GA_MEMB + GA_STACK_OFF) 346 nx_rv64im_rf_write(g3.rf, GA_X_RA, GA_MEMB + GA_SENTINEL_OFF) 347 nx_rv64im_rf_write(g3.rf, GA_X_A0, GA_MEMB + GA_SCR_OFF) 348 g3.sim.pc = GA_MEMB + cas_at 349 steps = 0 350 running = 1 351 while running == 1 { 352 nx_rv64im_sim_step(g3.sim) 353 steps = steps + 1 354 if g3.sim.pc == GA_MEMB + GA_SENTINEL_OFF { running = 0 } 355 if steps >= GA_STEP_BUDGET { running = 0 } 356 if g3.sim.halted == 1 { running = 0 } 357 } 358 let ret3: i64 = nx_rv64im_rf_read(g3.rf, GA_X_A0) 359 gv_puts(" e2e probe_cas: steps=" as *u8); ga_puti(steps); gv_puts(" a0=" as *u8); ga_puti(ret3); gv_puts("\n" as *u8) 360 gv_check("e2e-cas-swaps-once-then-refuses-reads-1009-on-the-sim" as *u8, (g3.sim.pc == GA_MEMB + GA_SENTINEL_OFF) * (ret3 == GA_EXP_CAS), ctr) 361 362 return gv_verdict("rv64_atomic_gate" as *u8, ctr, "assembler, sim, backend and the end-to-end run of the backend's own output; the NAS toolchain canary is the remaining door" as *u8) 363}