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}