code wiki / _hdl_build / nx_nxc_baremetal.nx

nx_nxc_baremetal.nx source

↩ module page · 95 lines · 7545 B

1// nx_nxc_baremetal.nx -- THE FULL PRODUCT LOOP: NishiLang SOURCE -> self-hosted nxc -> RV64 ELF -> QEMU-validated 2// emulator. Reads a NishiLang program compiled to a RISC-V Linux ELF by the SOVEREIGN self-hosted nxc (source -> lex -> 3// parse -> IR -> opt -> regalloc -> riscv -> ELF), extracts its .text, and bridges the Linux-ELF -> bare-metal gap: 4// prepend an sp shim (Linux gives argc/argv on the stack; bare-metal doesn't), patch the codegen's `li a7,93; ecall` 5// exit to a `jal` to a UART-emit(a0) + SiFive-finisher epilogue (assembled by the sovereign nx_rv64_asm). Runs the 6// result on the QEMU-validated golden sim (rv64im_min_sim) + writes a flat bin for qemu-system-riscv64 (the liar-killer). 7// sum10.nx returns 55 = 0x37. So the WHOLE sovereign source->RISC-V toolchain is validated against QEMU. expect_exit: 0 8import "nx_syscalls.nx" 9import "nishi_hdl_primitives.nx" 10import "rv64im_min_decoder.nx" 11import "rv64im_min_alu.nx" 12import "rv64im_min_regfile.nx" 13import "rv64im_min_csr.nx" 14import "rv64im_min_clint.nx" 15import "rv64im_min_uart.nx" 16import "rv64im_min_virtio.nx" 17import "rv64im_min_mmu.nx" 18import "rv64im_min_sim.nx" 19import "nx_rv64_asm.nx" 20const DMEM_MAGIC_4096: i64 = 4096 21const DMEM_MAGIC_100000000: i64 = 100000000 22 23func g_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 24func g_pn(v: i64) -> i64 { let b: *u8=sys_mmap(28); var x: i64=v; if x==0{b[0]=48;sys_write(1,b,1);return 0} var d: i64=0; var y: i64=x; while y>0{d=d+1;y=y/10} var i: i64=d-1; y=x; while i>=0{b[i]=(48+(y%10)) as u8;y=y/10;i=i-1} sys_write(1,b,d); return 0 } 25func g_hx(v: i64) -> i64 { let b: *u8=sys_mmap(4); let n0: i64=(v>>4)&15; let n1: i64=v&15; if n0<10 { b[0]=(48+n0) as u8 } else { b[0]=(87+n0) as u8 } if n1<10 { b[1]=(48+n1) as u8 } else { b[1]=(87+n1) as u8 } b[2]=32 as u8; sys_write(1,b,3); return 0 } 26func ck(name: *u8, c: i64) -> i64 { if c==1 { g_puts(" PASS " as *u8) } else { g_puts(" FAIL " as *u8) } g_puts(name); g_puts("\n" as *u8); return c } 27 28const DMEM_BASE: i64 = 0x80000000 29const DMEM_SIZE: i64 = 262144 30const ELF_HDR: i64 = 120 // nxc ELF: 64B ELF header + 56B program header; code (entry) starts here 31 32func put32(m: *u8, o: i64, w: i64) -> i64 { m[o]=(w&0xff) as u8; m[o+1]=((w>>8)&0xff) as u8; m[o+2]=((w>>16)&0xff) as u8; m[o+3]=((w>>24)&0xff) as u8; return 0 } 33func get32(m: *u8, o: i64) -> i64 { return (m[o] as i64)|((m[o+1] as i64)<<8)|((m[o+2] as i64)<<16)|((m[o+3] as i64)<<24) } 34// encode `jal x0, imm` (J-type, rd=0). imm is a signed byte offset (even). 35func enc_jal_x0(imm: i64) -> i64 { 36 let b20: i64=(imm>>20)&1; let b10_1: i64=(imm>>1)&0x3FF; let b11: i64=(imm>>11)&1; let b19_12: i64=(imm>>12)&0xFF 37 return (b20<<31)|(b10_1<<21)|(b11<<20)|(b19_12<<12)|0x6F 38} 39 40func main() -> i64 { 41 g_puts("nx_nxc_baremetal (NishiLang SOURCE -> self-hosted nxc -> RV64 ELF -> QEMU-validated emulator; sum10 -> 55=0x37)\n" as *u8) 42 var pass: i64=0; var total: i64=0 43 let lenbox: *i64=sys_mmap(16) as *i64 44 let elf: *u8=sys_read_file("knowledge/hw/nxc_sum10.elf" as *u8, lenbox) 45 if (elf as i64)==0 { g_puts(" NO nxc_sum10.elf -- compile with nxc first\n" as *u8); sys_exit(1); return 1 } 46 let elfsz: i64=lenbox[0]; let codebytes: i64=elfsz-ELF_HDR 47 g_puts(" nxc-compiled ELF: "); g_pn(elfsz); g_puts(" bytes -> "); g_pn(codebytes/4); g_puts(" code words\n" as *u8) 48 // sanity: word 3 of the code is `li a7,93` (0x05d00893) and word 4 is ecall (0x00000073) -- the codegen exit convention. 49 var t1: i64=0; if get32(elf, ELF_HDR+12)==0x05d00893 { if get32(elf, ELF_HDR+16)==0x00000073 { t1=1 } } 50 pass=pass+ck("T1: the nxc ELF has the codegen exit convention (li a7,93; ecall) -- result is in a0" as *u8, t1); total=total+1 51 52 // build the bare-metal image: [sp shim (3 words)][patched nxc code][epilogue]. 53 let out: *u8=sys_mmap(0x20000) 54 // shim: sp = 0x80020000 (lui sp,0x80020; slli sp,sp,32; srli sp,sp,32) -- a valid downward stack in guest RAM. 55 put32(out, 0, 0x80020137); put32(out, 4, 0x02011113); put32(out, 8, 0x02015113) 56 let SHIM: i64=12 57 // copy the nxc code after the shim 58 var i: i64=0; while i<codebytes { out[SHIM+i]=elf[ELF_HDR+i]; i=i+1 } 59 // patch code word 3 (li a7,93) -> jal x0 to the epilogue (which sits right after the code). offset = codebytes-12. 60 put32(out, SHIM+12, enc_jal_x0(codebytes-12)) 61 // epilogue: emit a0's low byte to UART + sentinel + SiFive-finisher halt (sovereign nx_rv64_asm). 62 let epi: *u8=sys_mmap(DMEM_MAGIC_4096) 63 let en: i64=rvasm_assemble_str(" lui t0, 0x10000\n sb a0, 0(t0)\n li t1, 0x2a\n sb t1, 0(t0)\n lui t2, 0x100\n lui t3, 0x5\n addi t3, t3, 0x555\n sw t3, 0(t2)\nspin:\n j spin\n" as *u8, epi, DMEM_MAGIC_4096) 64 var j: i64=0; while j<en { out[SHIM+codebytes+j]=epi[j]; j=j+1 } 65 let outsz: i64=SHIM+codebytes+en 66 67 // run on the QEMU-validated golden sim 68 let rf_storage: *i64=sys_mmap(8*NX_RV64IM_RF_N_REGS) as *i64; let csr_storage: *i64=sys_mmap(8*NX_CSR_SLOT_N) as *i64 69 let clint_storage: *i64=sys_mmap(8*NX_CLINT_SLOT_N) as *i64; let uart_storage: *i64=sys_mmap(8*NX_UART_SLOT_N) as *i64 70 let mem: *u8=sys_mmap(DMEM_SIZE); let tx_buf: *u8=sys_mmap(256) 71 let rf: *NxRv64imRegfile=sys_mmap(64) as *NxRv64imRegfile; let csr: *NxRv64imCsrFile=sys_mmap(64) as *NxRv64imCsrFile 72 let clint: *NxClint=sys_mmap(64) as *NxClint; let uart: *NxUart=sys_mmap(64) as *NxUart; let sim: *NxRv64imSim=sys_mmap(128) as *NxRv64imSim 73 nx_rv64im_rf_init(rf, rf_storage); nx_rv64im_csr_init(csr, csr_storage, 0); nx_clint_init(clint, clint_storage); nx_uart_init(uart, uart_storage, tx_buf, 256) 74 nx_rv64im_sim_init(sim, rf, csr, clint, uart, DMEM_BASE, mem, DMEM_SIZE, 0) 75 i=0; while i<outsz { mem[i]=out[i]; i=i+1 } 76 nx_rv64im_sim_run(sim, DMEM_MAGIC_100000000) 77 let n: i64=nx_uart_tx_count(uart) 78 g_puts(" golden sim UART ("); g_pn(n); g_puts(" bytes): "); i=0; while i<n { g_hx(tx_buf[i] as i64); i=i+1 } g_puts("\n" as *u8) 79 g_puts(" expected (== QEMU) : 37 2a (NishiLang: sum 1..10 = 55 = 0x37, sentinel)\n" as *u8) 80 var t2: i64=0; if n==2 { if tx_buf[0]==(0x37 as u8) { if tx_buf[1]==(0x2a as u8) { t2=1 } } } 81 pass=pass+ck("T2: the SELF-HOSTED-nxc-compiled NishiLang source runs on the QEMU-validated emulator -> 37 2a (sum 55)" as *u8, t2); total=total+1 82 83 let fd: i64=sys_openat_wr("knowledge/hw/nxc_sum10_bare.bin" as *u8, 420) 84 var t3: i64=0; if fd>=0 { let wn: i64=sys_write(fd, out, outsz); sys_close(fd); if wn==outsz { t3=1 } } 85 pass=pass+ck("T3: bare-metal image written for the qemu-system-riscv64 liar-killer" as *u8, t3); total=total+1 86 87 var okall: i64=0; if pass==total { okall=1 } 88 g_puts("---- nx_nxc_baremetal: passed "); g_pn(pass); g_puts(" / "); g_pn(total); g_puts(" ----\n" as *u8) 89 if okall==1 { 90 let logf: i64=sys_openat_append("knowledge/status/nxc_bare.log" as *u8, 420) 91 if logf>=0 { let z: i64=sys_write(logf,"NXCBARE GREEN: the FULL sovereign NishiLang SOURCE -> self-hosted nxc -> RV64 ELF -> QEMU-validated emulator loop -- a NishiLang while-loop program (sum 1..10) compiled by nxc runs on rv64im_min_sim == 37 2a (55). the product loop is source-level, not just IR.\n" as *u8,280); sys_close(logf) } 92 g_puts("verdict=GREEN (NishiLang SOURCE compiled by the self-hosted nxc runs on the QEMU-validated emulator == expected; run nxc_sum10_bare.bin on qemu-system to liar-kill. THE PRODUCT LOOP, source-level.)\n" as *u8); sys_exit(0); return 0 93 } 94 g_puts("verdict=RED\n" as *u8); sys_exit(1); return 1 95}