code wiki / _hdl_build / nx_nxc_run.nx
nx_nxc_run.nx source
↩ module page · 71 lines · 5051 B
1// nx_nxc_run.nx -- GENERIC runner: reads a NishiLang program compiled to RV64 ELF by the self-hosted nxc
2// (knowledge/hw/nxc_in.elf), bridges Linux-ELF -> bare-metal (sp shim + patch `li a7,93; ecall` -> jal to a UART-emit(a0)
3// + SiFive-finisher epilogue via sovereign nx_rv64_asm), runs it on the QEMU-validated golden sim (prints NXCSIM: <hex>),
4// and writes the bare-metal image to knowledge/hw/nxc_bare.bin for the qemu-system-riscv64 liar-killer. Drives the
5// diverse-program validation of the sovereign NishiLang source->RISC-V toolchain. expect_exit: 0
6import "nx_syscalls.nx"
7import "nishi_hdl_primitives.nx"
8import "rv64im_min_decoder.nx"
9import "rv64im_min_alu.nx"
10import "rv64im_min_regfile.nx"
11import "rv64im_min_csr.nx"
12import "rv64im_min_clint.nx"
13import "rv64im_min_uart.nx"
14import "rv64im_min_virtio.nx"
15import "rv64im_min_mmu.nx"
16import "rv64im_min_sim.nx"
17import "nx_rv64_asm.nx"
18const DMEM_MAGIC_4096: i64 = 4096
19const DMEM_MAGIC_200000000: i64 = 200000000
20
21func 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 }
22func g_h2(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 } sys_write(1,b,2); return 0 }
23
24const DMEM_BASE: i64 = 0x80000000
25const DMEM_SIZE: i64 = 262144
26const ELF_HDR: i64 = 120
27
28func 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 }
29func 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) }
30func enc_jal_x0(imm: i64) -> i64 { 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; return (b20<<31)|(b10_1<<21)|(b11<<20)|(b19_12<<12)|0x6F }
31
32func main() -> i64 {
33 let lenbox: *i64=sys_mmap(16) as *i64
34 let elf: *u8=sys_read_file("knowledge/hw/nxc_in.elf" as *u8, lenbox)
35 if (elf as i64)==0 { g_puts("NXCSIM: NOFILE\n" as *u8); sys_exit(1); return 1 }
36 let elfsz: i64=lenbox[0]; let codebytes: i64=elfsz-ELF_HDR
37 // find the codegen exit (`li a7,93; ecall`) in the code -- it may not be at a fixed offset for all programs. scan.
38 var exit_off: i64=0-1; var s: i64=0
39 while s+4<=codebytes { if get32(elf, ELF_HDR+s)==0x05d00893 { if get32(elf, ELF_HDR+s+4)==0x00000073 { exit_off=s; s=codebytes } } s=s+4 }
40 if exit_off<0 { g_puts("NXCSIM: NOEXIT\n" as *u8); sys_exit(1); return 1 }
41
42 let out: *u8=sys_mmap(0x20000)
43 put32(out, 0, 0x80020137); put32(out, 4, 0x02011113); put32(out, 8, 0x02015113) // sp = 0x80020000
44 let SHIM: i64=12
45 var i: i64=0; while i<codebytes { out[SHIM+i]=elf[ELF_HDR+i]; i=i+1 }
46 // patch the `li a7,93` (at exit_off) -> jal to the epilogue. The epilogue is appended AFTER the code, but
47 // .rodata global data (e.g. a `static`) can make codebytes non-4-aligned -> the epilogue would land at a
48 // misaligned address AND the jal offset (codebytes-exit_off) would be ODD (jal imm is even-only) -> the jal
49 // traps / lands mid-instruction and the program never reaches the UART-emit. Round the epilogue up to a
50 // 4-byte boundary so both the target and the jal offset are aligned. (No-global programs already 4-aligned.)
51 let epi_off: i64=((codebytes+3)/4)*4
52 put32(out, SHIM+exit_off, enc_jal_x0(epi_off-exit_off))
53 let epi: *u8=sys_mmap(DMEM_MAGIC_4096)
54 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)
55 var j: i64=0; while j<en { out[SHIM+epi_off+j]=epi[j]; j=j+1 }
56 let outsz: i64=SHIM+epi_off+en
57
58 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
59 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
60 let mem: *u8=sys_mmap(DMEM_SIZE); let tx_buf: *u8=sys_mmap(256)
61 let rf: *NxRv64imRegfile=sys_mmap(64) as *NxRv64imRegfile; let csr: *NxRv64imCsrFile=sys_mmap(64) as *NxRv64imCsrFile
62 let clint: *NxClint=sys_mmap(64) as *NxClint; let uart: *NxUart=sys_mmap(64) as *NxUart; let sim: *NxRv64imSim=sys_mmap(128) as *NxRv64imSim
63 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)
64 nx_rv64im_sim_init(sim, rf, csr, clint, uart, DMEM_BASE, mem, DMEM_SIZE, 0)
65 i=0; while i<outsz { mem[i]=out[i]; i=i+1 }
66 nx_rv64im_sim_run(sim, DMEM_MAGIC_200000000)
67 let n: i64=nx_uart_tx_count(uart)
68 g_puts("NXCSIM: " as *u8); i=0; while i<n { g_h2(tx_buf[i] as i64); i=i+1 } g_puts("\n" as *u8)
69 let fd: i64=sys_openat_wr("knowledge/hw/nxc_bare.bin" as *u8, 420); if fd>=0 { sys_write(fd, out, outsz); sys_close(fd) }
70 sys_exit(0); return 0
71}