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}