code wiki / _hdl_build / nx_isa_spec.nx

nx_isa_spec.nx source

↩ module page · 61 lines · 3712 B

1// nx_isa_spec.nx -- SPEC-DRIVEN instruction encoder (multi-arch). The capability 2// that removes the last hand-written piece of the machine-code generator: instead 3// of a bespoke bit-twiddling function per instruction, there is ONE generic 4// assembler per FORMAT (the bit layout), and each instruction is pure DATA -- 5// {format, opcode/base, funct3, funct7} lifted straight from the ISA manual. The 6// team authors a new instruction by adding a data row, not by writing encoder code. 7// 8// Covers RV64 (R / I / I-shift / fixed) and AArch64 (MOVZ / ADD-SUB-shifted-reg / 9// fixed). Proven by EXECUTION (the spec-assembled program runs on the sovereign 10// emulators). Refs: RISC-V Unprivileged ISA, ARMv8-A ARM (encoding tables). 11 12import "nx_syscalls_x86_64.nx" 13 14// ---- formats (the only bit-layout logic; shared by every instruction) ---- 15const FMT_RV_R: i64 = 0 // funct7|rs2|rs1|funct3|rd|opcode 16const FMT_RV_I: i64 = 1 // imm[11:0]|rs1|funct3|rd|opcode 17const FMT_RV_ISHIFT: i64 = 2 // funct6|shamt|rs1|funct3|rd|opcode (RV64 6-bit shamt) 18const FMT_A64_MOVZ: i64 = 3 // base | imm16<<5 | Rd 19const FMT_A64_ADDSUB:i64 = 4 // base | Rm<<16 | sh<<10 | Rn<<5 | Rd 20const FMT_FIXED: i64 = 5 // the whole word is the base (ecall/svc) 21 22// the GENERIC assembler: place operands per the format. (rd, a, b, imm) are the 23// uniform operand slots; each format reads the ones it needs. 24func isa_assemble(fmt: i64, base: i64, f3: i64, f7: i64, rd: i64, a: i64, b: i64, imm: i64) -> i64 { 25 if fmt == FMT_RV_R { return (f7 << 25) | (b << 20) | (a << 15) | (f3 << 12) | (rd << 7) | base } 26 if fmt == FMT_RV_I { return ((imm & 0xfff) << 20) | (a << 15) | (f3 << 12) | (rd << 7) | base } 27 if fmt == FMT_RV_ISHIFT { return (f7 << 26) | ((imm & 0x3f) << 20) | (a << 15) | (f3 << 12) | (rd << 7) | base } 28 if fmt == FMT_A64_MOVZ { return base | ((imm & 0xffff) << 5) | rd } 29 if fmt == FMT_A64_ADDSUB { return base | (b << 16) | ((imm & 0x3f) << 10) | (a << 5) | rd } 30 return base 31} 32 33// ---- the instruction SPEC TABLE (pure data -- add a row to add an instruction) ---- 34const I_RV_ADDI: i64 = 0 35const I_RV_SLLI: i64 = 1 36const I_RV_ADD: i64 = 2 37const I_RV_SUB: i64 = 3 38const I_RV_XOR: i64 = 4 39const I_RV_OR: i64 = 5 40const I_RV_ECALL: i64 = 6 41const I_A64_MOVZ: i64 = 7 42const I_A64_ADD: i64 = 8 43const I_A64_SUB: i64 = 9 44const I_A64_SVC: i64 = 10 45 46// emit instruction `inst` with operands -> the encoded word. The body IS the spec 47// table: each row is {format, base, funct3, funct7} -- no bit math, manual values. 48func isa_emit(inst: i64, rd: i64, a: i64, b: i64, imm: i64) -> i64 { 49 if inst == I_RV_ADDI { return isa_assemble(FMT_RV_I, 0x13, 0, 0, rd, a, b, imm) } 50 if inst == I_RV_SLLI { return isa_assemble(FMT_RV_ISHIFT, 0x13, 1, 0, rd, a, b, imm) } 51 if inst == I_RV_ADD { return isa_assemble(FMT_RV_R, 0x33, 0, 0, rd, a, b, imm) } 52 if inst == I_RV_SUB { return isa_assemble(FMT_RV_R, 0x33, 0, 0x20, rd, a, b, imm) } 53 if inst == I_RV_XOR { return isa_assemble(FMT_RV_R, 0x33, 4, 0, rd, a, b, imm) } 54 if inst == I_RV_OR { return isa_assemble(FMT_RV_R, 0x33, 6, 0, rd, a, b, imm) } 55 if inst == I_RV_ECALL { return isa_assemble(FMT_FIXED, 0x73, 0, 0, rd, a, b, imm) } 56 if inst == I_A64_MOVZ { return isa_assemble(FMT_A64_MOVZ, 0xD2800000, 0, 0, rd, a, b, imm) } 57 if inst == I_A64_ADD { return isa_assemble(FMT_A64_ADDSUB, 0x8B000000, 0, 0, rd, a, b, imm) } 58 if inst == I_A64_SUB { return isa_assemble(FMT_A64_ADDSUB, 0xCB000000, 0, 0, rd, a, b, imm) } 59 if inst == I_A64_SVC { return isa_assemble(FMT_FIXED, 0xD4000001, 0, 0, rd, a, b, imm) } 60 return 0 61}