diff options
41 files changed, 3499 insertions, 0 deletions
diff --git a/.DS_Store b/.DS_Store Binary files differnew file mode 100644 index 0000000..ed9e52b --- /dev/null +++ b/.DS_Store diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bc3feba --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# Build outputs +*.o +*.a +*.so +*.elf +*.hex + +# Generated sources +/gen/*/output/ +/gen/*/build/ + +# Toolchain builds +/tools/*/build/ + +# Simulator +/sim/build/ + +# Autotools +Makefile +Makefile.in +configure +config.status +config.log +autom4te.cache/ + +# IDE +.cate/ +.vscode/ +.idea/ +*.swp diff --git a/README.md b/README.md new file mode 100644 index 0000000..e064336 --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +# Fyntv — Flexible Yet Novel Transform Vector + +``` +fyntv/ +├── doc/ — ISA specification, programmer's manual, ABI docs +├── isa/ — Formal ISA definition: encoding, opcodes, extensions +├── gen/ — Code generators (C programs that generate source code) +│ ├── tablegen/ — Instruction table generation (like LLVM TableGen) +│ ├── decoder/ — Generate decoder sources from ISA definitions +│ ├── encoder/ — Generate encoder/assembler sources +│ ├── disassembler/ — Generate disassembler sources +│ ├── opcodes/ — Generate opcode header files +│ ├── abi/ — Generate ABI-related sources +│ └── docs/ — Generate documentation from machine-readable ISA defs +├── asm/ — Assembler +├── disasm/ — Disassembler +├── tools/ — Full binutils-style toolchain +│ ├── gas/ — Assembler (GNU style) +│ ├── ld/ — Linker +│ ├── objdump/ — Object file dumper +│ ├── objcopy/ — Object file copy/strip +│ ├── readelf/ — ELF reader +│ ├── nm/ — Symbol listing +│ ├── ar/ — Archive utility +│ ├── size/ — Section size reporter +│ ├── strings/ — String extractor +│ ├── addr2line/ — Address-to-line converter +│ └── cxxfilt/ — C++ name demangler +├── sim/ — Instruction-set simulator / emulator +├── linker/ — Linker scripts & runtime +├── runtime/ — C runtime (crt0, startup code) +├── tests/ — Test suites +│ ├── asm/ — Assembly-level tests +│ ├── sim/ — Simulator tests +│ ├── regression/— Regression test suite +│ └── benchmarks/— Benchmark programs +├── examples/ — Example programs +│ ├── asm/ — Assembly examples +│ ├── c/ — C language examples +│ └── benchmarks/— Dhrystone, Coremark, etc. +├── scripts/ — Build scripts, CI, release tooling +└── docker/ — Docker build environments +``` diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..a09bcb7 --- /dev/null +++ b/configure.ac @@ -0,0 +1,2 @@ +# Autoconf input — generates configure script +# Checks for: C compiler, Python, make, flex, bison diff --git a/doc/.DS_Store b/doc/.DS_Store Binary files differnew file mode 100644 index 0000000..f670f3f --- /dev/null +++ b/doc/.DS_Store diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 0000000..ba08550 --- /dev/null +++ b/doc/README.md @@ -0,0 +1,12 @@ +# Documentation + +| Directory | Contents | +|----------------|-----------------------------------------------| +| `spec/` | Formal ISA specification reference manual | +| `manual/` | Programmer's guide — assembly, toolchain use | +| `abi/` | Application Binary Interface specification | +| `internals/` | Microarchitecture, pipeline, implementor notes| +| `examples/` | Annotated code examples in asm/C | + +The `gen/docs/` generator produces source files for these docs from the +machine-readable ISA definitions. diff --git a/doc/spec/isa_reference.html b/doc/spec/isa_reference.html new file mode 100644 index 0000000..b2a8353 --- /dev/null +++ b/doc/spec/isa_reference.html @@ -0,0 +1,106 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset="UTF-8"> +<title>Fyntv ISA Reference Manual</title> +<link rel="stylesheet" href="style.css"> +</head> +<body> +<article> +<header> +<h1>Fyntv Instruction Set Architecture</h1> +<p class="subtitle">Reference Manual</p> +<p><strong>Version:</strong> 0.0.0.1 | <strong>Date:</strong> June 2026</p> +</header> +<section id="registers"> +<h2>Registers</h2> +<table><thead><tr><th>Register</th><th>ABI Name</th><th>Description</th><th>Saved</th></tr></thead><tbody> +<tr><td>x0</td><td>zero</td><td>Always-zero register — reads return 0, writes are discarded</td><td>No</td></tr> +<tr><td>x1</td><td>ra</td><td>Return address link register</td><td>No</td></tr> +<tr><td>x2</td><td>sp</td><td>Stack pointer</td><td>Yes</td></tr> +<tr><td>x3</td><td>gp</td><td>Global pointer</td><td>Yes</td></tr> +<tr><td>x4</td><td>tp</td><td>Thread pointer</td><td>Yes</td></tr> +<tr><td>x5</td><td>t0</td><td>Temporary register 0</td><td>No</td></tr> +<tr><td>x6</td><td>t1</td><td>Temporary register 1</td><td>No</td></tr> +<tr><td>x7</td><td>t2</td><td>Temporary register 2</td><td>No</td></tr> +<tr><td>x8</td><td>s0</td><td>Saved register 0 / frame pointer</td><td>Yes</td></tr> +<tr><td>x9</td><td>s1</td><td>Saved register 1</td><td>Yes</td></tr> +<tr><td>x10</td><td>a0</td><td>Function argument 0 / return value 0</td><td>No</td></tr> +<tr><td>x11</td><td>a1</td><td>Function argument 1 / return value 1</td><td>No</td></tr> +<tr><td>x12</td><td>a2</td><td>Function argument 2</td><td>No</td></tr> +<tr><td>x13</td><td>a3</td><td>Function argument 3</td><td>No</td></tr> +<tr><td>x14</td><td>a4</td><td>Function argument 4</td><td>No</td></tr> +<tr><td>x15</td><td>a5</td><td>Function argument 5</td><td>No</td></tr> +<tr><td>x16</td><td>a6</td><td>Function argument 6</td><td>No</td></tr> +<tr><td>x17</td><td>a7</td><td>Function argument 7</td><td>No</td></tr> +<tr><td>x18</td><td>s2</td><td>Saved register 2</td><td>Yes</td></tr> +<tr><td>x19</td><td>s3</td><td>Saved register 3</td><td>Yes</td></tr> +<tr><td>x20</td><td>s4</td><td>Saved register 4</td><td>Yes</td></tr> +<tr><td>x21</td><td>s5</td><td>Saved register 5</td><td>Yes</td></tr> +<tr><td>x22</td><td>s6</td><td>Saved register 6</td><td>Yes</td></tr> +<tr><td>x23</td><td>s7</td><td>Saved register 7</td><td>Yes</td></tr> +<tr><td>x24</td><td>s8</td><td>Saved register 8</td><td>Yes</td></tr> +<tr><td>x25</td><td>s9</td><td>Saved register 9</td><td>Yes</td></tr> +<tr><td>x26</td><td>s10</td><td>Saved register 10</td><td>Yes</td></tr> +<tr><td>x27</td><td>s11</td><td>Saved register 11</td><td>Yes</td></tr> +<tr><td>x28</td><td>t3</td><td>Temporary register 3</td><td>No</td></tr> +<tr><td>x29</td><td>t4</td><td>Temporary register 4</td><td>No</td></tr> +<tr><td>x30</td><td>t5</td><td>Temporary register 5</td><td>No</td></tr> +<tr><td>x31</td><td>t6</td><td>Temporary register 6</td><td>No</td></tr> +</tbody></table> +</section> +<section id="instructions"> +<h2>Instruction Set</h2> +<table><thead><tr><th>Mnemonic</th><th>Format</th><th>Opcode</th><th>Operands</th><th>Description</th></tr></thead><tbody> +<tr><td><code>ADD</code></td><td>R</td><td><code>0x33</code></td><td><code>rd, rs1, rs2</code></td><td>Add registers</td></tr> +<tr><td><code>SUB</code></td><td>R</td><td><code>0x33</code></td><td><code>rd, rs1, rs2</code></td><td>Subtract registers</td></tr> +<tr><td><code>ADDI</code></td><td>I</td><td><code>0x13</code></td><td><code>rd, rs1, imm12</code></td><td>Add sign-extended 12-bit immediate to register rs1</td></tr> +<tr><td><code>SLT</code></td><td>R</td><td><code>0x33</code></td><td><code>rd, rs1, rs2</code></td><td>Set if rs1 is less than rs2 (signed)</td></tr> +<tr><td><code>SLTU</code></td><td>R</td><td><code>0x33</code></td><td><code>rd, rs1, rs2</code></td><td>Set if rs1 is less than rs2 (unsigned)</td></tr> +<tr><td><code>SLTI</code></td><td>I</td><td><code>0x13</code></td><td><code>rd, rs1, imm12</code></td><td>Set if rs1 is less than immediate (signed)</td></tr> +<tr><td><code>SLTIU</code></td><td>I</td><td><code>0x13</code></td><td><code>rd, rs1, imm12</code></td><td>Set if rs1 is less than immediate (unsigned)</td></tr> +<tr><td><code>LUI</code></td><td>U</td><td><code>0x37</code></td><td><code>rd, imm20</code></td><td>Load upper immediate — places 20-bit immediate in upper 20 bits of rd</td></tr> +<tr><td><code>AUIPC</code></td><td>U</td><td><code>0x17</code></td><td><code>rd, imm20</code></td><td>Add upper immediate to PC — forms PC-relative address</td></tr> +<tr><td><code>AND</code></td><td>R</td><td><code>0x33</code></td><td><code>rd, rs1, rs2</code></td><td>Bitwise AND</td></tr> +<tr><td><code>OR</code></td><td>R</td><td><code>0x33</code></td><td><code>rd, rs1, rs2</code></td><td>Bitwise OR</td></tr> +<tr><td><code>XOR</code></td><td>R</td><td><code>0x33</code></td><td><code>rd, rs1, rs2</code></td><td>Bitwise XOR</td></tr> +<tr><td><code>ANDI</code></td><td>I</td><td><code>0x13</code></td><td><code>rd, rs1, imm12</code></td><td>Bitwise AND with immediate</td></tr> +<tr><td><code>ORI</code></td><td>I</td><td><code>0x13</code></td><td><code>rd, rs1, imm12</code></td><td>Bitwise OR with immediate</td></tr> +<tr><td><code>XORI</code></td><td>I</td><td><code>0x13</code></td><td><code>rd, rs1, imm12</code></td><td>Bitwise XOR with immediate</td></tr> +<tr><td><code>SLL</code></td><td>R</td><td><code>0x33</code></td><td><code>rd, rs1, rs2</code></td><td>Logical left shift by lower 5 bits of rs2</td></tr> +<tr><td><code>SRL</code></td><td>R</td><td><code>0x33</code></td><td><code>rd, rs1, rs2</code></td><td>Logical right shift by lower 5 bits of rs2</td></tr> +<tr><td><code>SRA</code></td><td>R</td><td><code>0x33</code></td><td><code>rd, rs1, rs2</code></td><td>Arithmetic right shift by lower 5 bits of rs2</td></tr> +<tr><td><code>SLLI</code></td><td>I</td><td><code>0x13</code></td><td><code>rd, rs1, shamt5</code></td><td>Logical left shift by immediate shift amount</td></tr> +<tr><td><code>SRLI</code></td><td>I</td><td><code>0x13</code></td><td><code>rd, rs1, shamt5</code></td><td>Logical right shift by immediate shift amount</td></tr> +<tr><td><code>SRAI</code></td><td>I</td><td><code>0x13</code></td><td><code>rd, rs1, shamt5</code></td><td>Arithmetic right shift by immediate shift amount</td></tr> +<tr><td><code>LB</code></td><td>I</td><td><code>0x03</code></td><td><code>rd, offset(rs1)</code></td><td>Load byte (sign-extended)</td></tr> +<tr><td><code>LH</code></td><td>I</td><td><code>0x03</code></td><td><code>rd, offset(rs1)</code></td><td>Load halfword (sign-extended)</td></tr> +<tr><td><code>LW</code></td><td>I</td><td><code>0x03</code></td><td><code>rd, offset(rs1)</code></td><td>Load word</td></tr> +<tr><td><code>LBU</code></td><td>I</td><td><code>0x03</code></td><td><code>rd, offset(rs1)</code></td><td>Load byte (zero-extended)</td></tr> +<tr><td><code>LHU</code></td><td>I</td><td><code>0x03</code></td><td><code>rd, offset(rs1)</code></td><td>Load halfword (zero-extended)</td></tr> +<tr><td><code>SB</code></td><td>S</td><td><code>0x23</code></td><td><code>rs2, offset(rs1)</code></td><td>Store byte</td></tr> +<tr><td><code>SH</code></td><td>S</td><td><code>0x23</code></td><td><code>rs2, offset(rs1)</code></td><td>Store halfword</td></tr> +<tr><td><code>SW</code></td><td>S</td><td><code>0x23</code></td><td><code>rs2, offset(rs1)</code></td><td>Store word</td></tr> +<tr><td><code>BEQ</code></td><td>B</td><td><code>0x63</code></td><td><code>rs1, rs2, label</code></td><td>Branch equal</td></tr> +<tr><td><code>BNE</code></td><td>B</td><td><code>0x63</code></td><td><code>rs1, rs2, label</code></td><td>Branch not equal</td></tr> +<tr><td><code>BLT</code></td><td>B</td><td><code>0x63</code></td><td><code>rs1, rs2, label</code></td><td>Branch less than (signed)</td></tr> +<tr><td><code>BGE</code></td><td>B</td><td><code>0x63</code></td><td><code>rs1, rs2, label</code></td><td>Branch greater than or equal (signed)</td></tr> +<tr><td><code>BLTU</code></td><td>B</td><td><code>0x63</code></td><td><code>rs1, rs2, label</code></td><td>Branch less than (unsigned)</td></tr> +<tr><td><code>BGEU</code></td><td>B</td><td><code>0x63</code></td><td><code>rs1, rs2, label</code></td><td>Branch greater than or equal (unsigned)</td></tr> +<tr><td><code>JAL</code></td><td>J</td><td><code>0x6F</code></td><td><code>rd, label</code></td><td>Jump and link — jump to PC+offset, save return address to rd</td></tr> +<tr><td><code>JALR</code></td><td>I</td><td><code>0x67</code></td><td><code>rd, rs1, offset</code></td><td>Jump and link register — jump to rs1+offset, save return address</td></tr> +<tr><td><code>FENCE</code></td><td>I</td><td><code>0x0F</code></td><td><code>pred, succ</code></td><td>Memory ordering fence</td></tr> +<tr><td><code>FENCEI</code></td><td>I</td><td><code>0x0F</code></td><td><code>—</code></td><td>Instruction fence — synchronizes instruction and data streams</td></tr> +<tr><td><code>ECALL</code></td><td>I</td><td><code>0x73</code></td><td><code>—</code></td><td>Environment call — raises a system call exception</td></tr> +<tr><td><code>EBREAK</code></td><td>I</td><td><code>0x73</code></td><td><code>—</code></td><td>Breakpoint — raises a breakpoint exception</td></tr> +<tr><td><code>CSRRW</code></td><td>I</td><td><code>0x73</code></td><td><code>rd, csr, rs1</code></td><td>Atomic read/write CSR — writes rs1 to CSR, returns old value in rd</td></tr> +<tr><td><code>CSRRS</code></td><td>I</td><td><code>0x73</code></td><td><code>rd, csr, rs1</code></td><td>Atomic read and set bits in CSR</td></tr> +<tr><td><code>CSRRC</code></td><td>I</td><td><code>0x73</code></td><td><code>rd, csr, rs1</code></td><td>Atomic read and clear bits in CSR</td></tr> +<tr><td><code>CSRRWI</code></td><td>I</td><td><code>0x73</code></td><td><code>rd, csr, uimm5</code></td><td>Atomic read/write CSR with immediate</td></tr> +<tr><td><code>CSRRSI</code></td><td>I</td><td><code>0x73</code></td><td><code>rd, csr, uimm5</code></td><td>Atomic read and set bits in CSR with immediate</td></tr> +<tr><td><code>CSRRCI</code></td><td>I</td><td><code>0x73</code></td><td><code>rd, csr, uimm5</code></td><td>Atomic read and clear bits in CSR with immediate</td></tr> +</tbody></table> +</section> +</article> +</body> +</html> diff --git a/doc/spec/isa_reference.md b/doc/spec/isa_reference.md new file mode 100644 index 0000000..ab7b1da --- /dev/null +++ b/doc/spec/isa_reference.md @@ -0,0 +1,899 @@ +--- +title: "Fyntv Instruction Set Architecture" +subtitle: "Reference Manual" +version: "0.0.0.1" +date: "June 2026" +status: "Draft" +geometry: margin=1in +toc: true +numbersections: true +--- + +\newpage + +# Revision History + +| Version | Date | Description | +|---------|------|-------------| +| 0.0.0.1 | June 2026 | Initial Fyntv release | + +\newpage + +\tableofcontents + +\newpage + +# Introduction + +This document defines the **Fyntv Instruction Set Architecture (ISA)** version 0.0.0.1. + +## Scope + +This specification covers: + +- Instruction formats and encoding +- Register file and calling convention +- All base and extension instructions +- Control and status registers (CSRs) +- Exception and trap handling + +## ISA Overview + +The Fyntv ISA is a load-store architecture with: + +- 32 general-purpose registers (x0–x31) +- Fixed-width 32-bit instructions +- Three operand formats: register (R-type), immediate (I-type), and store (S-type) +- Branch (B-type) and upper-immediate (U-type) variants +- A separate jump-and-link (J-type) format + +\newpage + +# Registers + +## General-Purpose Registers + +The architecture provides 32 general-purpose registers (x0–x31), each 32 bits wide. Register x0 is hardwired to the constant zero. + +| Register | ABI Name | Description | Callee-Saved | +|----------|----------|-------------|--------------| +| x0 | zero | Always-zero register — reads return 0, writes are discarded | — | +| x1 | ra | Return address link register | No | +| x2 | sp | Stack pointer | Yes | +| x3 | gp | Global pointer | Yes | +| x4 | tp | Thread pointer | Yes | +| x5 | t0 | Temporary register 0 | No | +| x6 | t1 | Temporary register 1 | No | +| x7 | t2 | Temporary register 2 | No | +| x8 | s0 | Saved register 0 / frame pointer | Yes | +| x9 | s1 | Saved register 1 | Yes | +| x10 | a0 | Function argument 0 / return value 0 | No | +| x11 | a1 | Function argument 1 / return value 1 | No | +| x12 | a2 | Function argument 2 | No | +| x13 | a3 | Function argument 3 | No | +| x14 | a4 | Function argument 4 | No | +| x15 | a5 | Function argument 5 | No | +| x16 | a6 | Function argument 6 | No | +| x17 | a7 | Function argument 7 | No | +| x18 | s2 | Saved register 2 | Yes | +| x19 | s3 | Saved register 3 | Yes | +| x20 | s4 | Saved register 4 | Yes | +| x21 | s5 | Saved register 5 | Yes | +| x22 | s6 | Saved register 6 | Yes | +| x23 | s7 | Saved register 7 | Yes | +| x24 | s8 | Saved register 8 | Yes | +| x25 | s9 | Saved register 9 | Yes | +| x26 | s10 | Saved register 10 | Yes | +| x27 | s11 | Saved register 11 | Yes | +| x28 | t3 | Temporary register 3 | No | +| x29 | t4 | Temporary register 4 | No | +| x30 | t5 | Temporary register 5 | No | +| x31 | t6 | Temporary register 6 | No | + +### ABI Calling Convention + +| Register | ABI Name | Caller-Saved | Argument | +|----------|----------|-------------|----------| +| x0 | zero | No | No | +| x1 | ra | No | No | +| x2 | sp | No | No | +| x3 | gp | No | No | +| x4 | tp | No | No | +| x5 | t0 | Yes | No | +| x6 | t1 | Yes | No | +| x7 | t2 | Yes | No | +| x8 | s0 | No | No | +| x9 | s1 | No | No | +| x10 | a0 | Yes | Yes | +| x11 | a1 | Yes | Yes | +| x12 | a2 | Yes | Yes | +| x13 | a3 | Yes | Yes | +| x14 | a4 | Yes | Yes | +| x15 | a5 | Yes | Yes | +| x16 | a6 | Yes | Yes | +| x17 | a7 | Yes | Yes | +| x18 | s2 | No | No | +| x19 | s3 | No | No | +| x20 | s4 | No | No | +| x21 | s5 | No | No | +| x22 | s6 | No | No | +| x23 | s7 | No | No | +| x24 | s8 | No | No | +| x25 | s9 | No | No | +| x26 | s10 | No | No | +| x27 | s11 | No | No | +| x28 | t3 | Yes | No | +| x29 | t4 | Yes | No | +| x30 | t5 | Yes | No | +| x31 | t6 | Yes | No | + +\newpage + +# Instruction Formats + +Instructions are encoded in one of several fixed formats. All formats share the same opcode placement (bits 6:0) to allow efficient decoding. + +### R-Type Format + +Bit width: **32 bits** + +``` + 31:25 |24:20|19:15|14:12|11:7| 6:0 +funct7 | rs2 | rs1 |funct3| rd |opcode + 7 | 5 | 5 | 3 | 5 | 7 +``` + +### I-Type Format + +Bit width: **32 bits** + +``` + 31:20 |19:15|14:12|11:7| 6:0 + imm | rs1 |funct3| rd |opcode + 12 | 5 | 3 | 5 | 7 +``` + +### S-Type Format + +Bit width: **32 bits** + +``` + 31:25 |24:20|19:15|14:12|11:7| 6:0 +imm_hi | rs2 | rs1 |funct3|imm_lo|opcode + 7 | 5 | 5 | 3 | 5 | 7 +``` + +### B-Type Format + +Bit width: **32 bits** + +``` +31:31|30:25 |24:20|19:15|14:12|11:7| 6:0 +imm_12|imm_10_5| rs2 | rs1 |funct3|imm_4_1|opcode +1| 6 | 5 | 5 | 3 | 5 | 7 +``` + +### U-Type Format + +Bit width: **32 bits** + +``` + 31:12 |11:7| 6:0 + imm | rd |opcode + 20 | 5 | 7 +``` + +### J-Type Format + +Bit width: **32 bits** + +``` +31:31| 30:21 |20:20| 19:12 |11:7| 6:0 +imm_20| imm_10_1 |imm_11|imm_19_12| rd |opcode +1| 10 |1| 8 | 5 | 7 +``` + +\newpage + +# Instruction Set + +## Branch Operations + +| Mnemonic | Format | Opcode | Funct3 | Funct7 | Operands | Description | +|----------|--------|--------|--------|--------|----------|-------------| +| `BNE` | B | `0x63` | 0x1 | — | rs1, rs2, label | Branch not equal | +| `BEQ` | B | `0x63` | 0x0 | — | rs1, rs2, label | Branch equal | +| `BLT` | B | `0x63` | 0x4 | — | rs1, rs2, label | Branch less than (signed) | +| `BGE` | B | `0x63` | 0x5 | — | rs1, rs2, label | Branch greater than or equal (signed) | +| `BLTU` | B | `0x63` | 0x6 | — | rs1, rs2, label | Branch less than (unsigned) | +| `BGEU` | B | `0x63` | 0x7 | — | rs1, rs2, label | Branch greater than or equal (unsigned) | +## Integer Operations + +| Mnemonic | Format | Opcode | Funct3 | Funct7 | Operands | Description | +|----------|--------|--------|--------|--------|----------|-------------| +| `AUIPC` | U | `0x17` | — | — | rd, imm20 | Add upper immediate to PC — forms PC-relative address | +| `SLT` | R | `0x33` | 0x2 | 0x00 | rd, rs1, rs2 | Set if rs1 is less than rs2 (signed) | +| `SLTU` | R | `0x33` | 0x3 | 0x00 | rd, rs1, rs2 | Set if rs1 is less than rs2 (unsigned) | +| `SLTI` | I | `0x13` | 0x2 | — | rd, rs1, imm12 | Set if rs1 is less than immediate (signed) | +| `SLTIU` | I | `0x13` | 0x3 | — | rd, rs1, imm12 | Set if rs1 is less than immediate (unsigned) | +| `LUI` | U | `0x37` | — | — | rd, imm20 | Load upper immediate — places 20-bit immediate in upper 20 bits of rd | +| `ADD` | R | `0x33` | 0x0 | 0x00 | rd, rs1, rs2 | Add registers | +| `SUB` | R | `0x33` | 0x0 | 0x20 | rd, rs1, rs2 | Subtract registers | +| `ADDI` | I | `0x13` | 0x0 | — | rd, rs1, imm12 | Add sign-extended 12-bit immediate to register rs1 | +## Jump Operations + +| Mnemonic | Format | Opcode | Funct3 | Funct7 | Operands | Description | +|----------|--------|--------|--------|--------|----------|-------------| +| `JAL` | J | `0x6F` | — | — | rd, label | Jump and link — jump to PC+offset, save return address to rd | +| `JALR` | I | `0x67` | 0x0 | — | rd, rs1, offset | Jump and link register — jump to rs1+offset, save return address | +## Logical Operations + +| Mnemonic | Format | Opcode | Funct3 | Funct7 | Operands | Description | +|----------|--------|--------|--------|--------|----------|-------------| +| `XOR` | R | `0x33` | 0x4 | 0x00 | rd, rs1, rs2 | Bitwise XOR | +| `OR` | R | `0x33` | 0x6 | 0x00 | rd, rs1, rs2 | Bitwise OR | +| `AND` | R | `0x33` | 0x7 | 0x00 | rd, rs1, rs2 | Bitwise AND | +| `ORI` | I | `0x13` | 0x6 | — | rd, rs1, imm12 | Bitwise OR with immediate | +| `XORI` | I | `0x13` | 0x4 | — | rd, rs1, imm12 | Bitwise XOR with immediate | +| `ANDI` | I | `0x13` | 0x7 | — | rd, rs1, imm12 | Bitwise AND with immediate | +## Memory Operations + +| Mnemonic | Format | Opcode | Funct3 | Funct7 | Operands | Description | +|----------|--------|--------|--------|--------|----------|-------------| +| `SW` | S | `0x23` | 0x2 | — | rs2, offset(rs1) | Store word | +| `LB` | I | `0x03` | 0x0 | — | rd, offset(rs1) | Load byte (sign-extended) | +| `LH` | I | `0x03` | 0x1 | — | rd, offset(rs1) | Load halfword (sign-extended) | +| `LW` | I | `0x03` | 0x2 | — | rd, offset(rs1) | Load word | +| `LBU` | I | `0x03` | 0x4 | — | rd, offset(rs1) | Load byte (zero-extended) | +| `LHU` | I | `0x03` | 0x5 | — | rd, offset(rs1) | Load halfword (zero-extended) | +| `SB` | S | `0x23` | 0x0 | — | rs2, offset(rs1) | Store byte | +| `SH` | S | `0x23` | 0x1 | — | rs2, offset(rs1) | Store halfword | +## Shift Operations + +| Mnemonic | Format | Opcode | Funct3 | Funct7 | Operands | Description | +|----------|--------|--------|--------|--------|----------|-------------| +| `SRAI` | I | `0x13` | 0x5 | — | rd, rs1, shamt5 | Arithmetic right shift by immediate shift amount | +| `SLLI` | I | `0x13` | 0x1 | — | rd, rs1, shamt5 | Logical left shift by immediate shift amount | +| `SRA` | R | `0x33` | 0x5 | 0x20 | rd, rs1, rs2 | Arithmetic right shift by lower 5 bits of rs2 | +| `SRL` | R | `0x33` | 0x5 | 0x00 | rd, rs1, rs2 | Logical right shift by lower 5 bits of rs2 | +| `SLL` | R | `0x33` | 0x1 | 0x00 | rd, rs1, rs2 | Logical left shift by lower 5 bits of rs2 | +| `SRLI` | I | `0x13` | 0x5 | — | rd, rs1, shamt5 | Logical right shift by immediate shift amount | +## Synchronization Operations + +| Mnemonic | Format | Opcode | Funct3 | Funct7 | Operands | Description | +|----------|--------|--------|--------|--------|----------|-------------| +| `FENCEI` | I | `0x0F` | 0x1 | — | — | Instruction fence — synchronizes instruction and data streams | +| `FENCE` | I | `0x0F` | 0x0 | — | pred, succ | Memory ordering fence | +## System Operations + +| Mnemonic | Format | Opcode | Funct3 | Funct7 | Operands | Description | +|----------|--------|--------|--------|--------|----------|-------------| +| `CSRRCI` | I | `0x73` | 0x7 | — | rd, csr, uimm5 | Atomic read and clear bits in CSR with immediate | +| `ECALL` | I | `0x73` | 0x0 | — | — | Environment call — raises a system call exception | +| `EBREAK` | I | `0x73` | 0x0 | — | — | Breakpoint — raises a breakpoint exception | +| `CSRRW` | I | `0x73` | 0x1 | — | rd, csr, rs1 | Atomic read/write CSR — writes rs1 to CSR, returns old value in rd | +| `CSRRS` | I | `0x73` | 0x2 | — | rd, csr, rs1 | Atomic read and set bits in CSR | +| `CSRRC` | I | `0x73` | 0x3 | — | rd, csr, rs1 | Atomic read and clear bits in CSR | +| `CSRRWI` | I | `0x73` | 0x5 | — | rd, csr, uimm5 | Atomic read/write CSR with immediate | +| `CSRRSI` | I | `0x73` | 0x6 | — | rd, csr, uimm5 | Atomic read and set bits in CSR with immediate | + +\newpage + +## Instruction Details + +### BNE + +- **Format:** B +- **Opcode:** `0x63` +- **Funct3:** `0x1` +- **Operands:** `rs1, rs2, label` +- **Description:** Branch not equal +- **Operation:** `if (rs1 != rs2) PC += sext(offset)` + +### BEQ + +- **Format:** B +- **Opcode:** `0x63` +- **Funct3:** `0x0` +- **Operands:** `rs1, rs2, label` +- **Description:** Branch equal +- **Operation:** `if (rs1 == rs2) PC += sext(offset)` + +### BLT + +- **Format:** B +- **Opcode:** `0x63` +- **Funct3:** `0x4` +- **Operands:** `rs1, rs2, label` +- **Description:** Branch less than (signed) +- **Operation:** `if (rs1 < rs2) PC += sext(offset)` + +### BGE + +- **Format:** B +- **Opcode:** `0x63` +- **Funct3:** `0x5` +- **Operands:** `rs1, rs2, label` +- **Description:** Branch greater than or equal (signed) +- **Operation:** `if (rs1 >= rs2) PC += sext(offset)` + +### BLTU + +- **Format:** B +- **Opcode:** `0x63` +- **Funct3:** `0x6` +- **Operands:** `rs1, rs2, label` +- **Description:** Branch less than (unsigned) +- **Operation:** `if (rs1 < rs2) PC += sext(offset)` + +### BGEU + +- **Format:** B +- **Opcode:** `0x63` +- **Funct3:** `0x7` +- **Operands:** `rs1, rs2, label` +- **Description:** Branch greater than or equal (unsigned) +- **Operation:** `if (rs1 >= rs2) PC += sext(offset)` + +### AUIPC + +- **Format:** U +- **Opcode:** `0x17` +- **Operands:** `rd, imm20` +- **Description:** Add upper immediate to PC — forms PC-relative address +- **Operation:** `rd = PC + (imm20 << 12)` + +### SLT + +- **Format:** R +- **Opcode:** `0x33` +- **Funct3:** `0x2` +- **Funct7:** `0x00` +- **Operands:** `rd, rs1, rs2` +- **Description:** Set if rs1 is less than rs2 (signed) +- **Operation:** `rd = (rs1 < rs2) ? 1 : 0` + +### SLTU + +- **Format:** R +- **Opcode:** `0x33` +- **Funct3:** `0x3` +- **Funct7:** `0x00` +- **Operands:** `rd, rs1, rs2` +- **Description:** Set if rs1 is less than rs2 (unsigned) +- **Operation:** `rd = (rs1 < rs2) ? 1 : 0` + +### SLTI + +- **Format:** I +- **Opcode:** `0x13` +- **Funct3:** `0x2` +- **Operands:** `rd, rs1, imm12` +- **Description:** Set if rs1 is less than immediate (signed) +- **Operation:** `rd = (rs1 < sext(imm12)) ? 1 : 0` + +### SLTIU + +- **Format:** I +- **Opcode:** `0x13` +- **Funct3:** `0x3` +- **Operands:** `rd, rs1, imm12` +- **Description:** Set if rs1 is less than immediate (unsigned) +- **Operation:** `rd = (rs1 < sext(imm12)) ? 1 : 0` + +### LUI + +- **Format:** U +- **Opcode:** `0x37` +- **Operands:** `rd, imm20` +- **Description:** Load upper immediate — places 20-bit immediate in upper 20 bits of rd +- **Operation:** `rd = imm20 << 12` + +### ADD + +- **Format:** R +- **Opcode:** `0x33` +- **Funct3:** `0x0` +- **Funct7:** `0x00` +- **Operands:** `rd, rs1, rs2` +- **Description:** Add registers +- **Operation:** `rd = rs1 + rs2` + +### SUB + +- **Format:** R +- **Opcode:** `0x33` +- **Funct3:** `0x0` +- **Funct7:** `0x20` +- **Operands:** `rd, rs1, rs2` +- **Description:** Subtract registers +- **Operation:** `rd = rs1 - rs2` + +### ADDI + +- **Format:** I +- **Opcode:** `0x13` +- **Funct3:** `0x0` +- **Operands:** `rd, rs1, imm12` +- **Description:** Add sign-extended 12-bit immediate to register rs1 +- **Operation:** `rd = rs1 + sext(imm12)` + +### JAL + +- **Format:** J +- **Opcode:** `0x6F` +- **Operands:** `rd, label` +- **Description:** Jump and link — jump to PC+offset, save return address to rd +- **Operation:** `rd = PC + 4; PC += sext(offset)` + +### JALR + +- **Format:** I +- **Opcode:** `0x67` +- **Funct3:** `0x0` +- **Operands:** `rd, rs1, offset` +- **Description:** Jump and link register — jump to rs1+offset, save return address +- **Operation:** `rd = PC + 4; PC = (rs1 + sext(offset)) & ~1` + +### XOR + +- **Format:** R +- **Opcode:** `0x33` +- **Funct3:** `0x4` +- **Funct7:** `0x00` +- **Operands:** `rd, rs1, rs2` +- **Description:** Bitwise XOR +- **Operation:** `rd = rs1 ^ rs2` + +### OR + +- **Format:** R +- **Opcode:** `0x33` +- **Funct3:** `0x6` +- **Funct7:** `0x00` +- **Operands:** `rd, rs1, rs2` +- **Description:** Bitwise OR +- **Operation:** `rd = rs1 | rs2` + +### AND + +- **Format:** R +- **Opcode:** `0x33` +- **Funct3:** `0x7` +- **Funct7:** `0x00` +- **Operands:** `rd, rs1, rs2` +- **Description:** Bitwise AND +- **Operation:** `rd = rs1 & rs2` + +### ORI + +- **Format:** I +- **Opcode:** `0x13` +- **Funct3:** `0x6` +- **Operands:** `rd, rs1, imm12` +- **Description:** Bitwise OR with immediate +- **Operation:** `rd = rs1 | sext(imm12)` + +### XORI + +- **Format:** I +- **Opcode:** `0x13` +- **Funct3:** `0x4` +- **Operands:** `rd, rs1, imm12` +- **Description:** Bitwise XOR with immediate +- **Operation:** `rd = rs1 ^ sext(imm12)` + +### ANDI + +- **Format:** I +- **Opcode:** `0x13` +- **Funct3:** `0x7` +- **Operands:** `rd, rs1, imm12` +- **Description:** Bitwise AND with immediate +- **Operation:** `rd = rs1 & sext(imm12)` + +### SW + +- **Format:** S +- **Opcode:** `0x23` +- **Funct3:** `0x2` +- **Operands:** `rs2, offset(rs1)` +- **Description:** Store word +- **Operation:** `MEM[rs1 + offset][31:0] = rs2[31:0]` + +### LB + +- **Format:** I +- **Opcode:** `0x03` +- **Funct3:** `0x0` +- **Operands:** `rd, offset(rs1)` +- **Description:** Load byte (sign-extended) +- **Operation:** `rd = sext(MEM[rs1 + offset][7:0])` + +### LH + +- **Format:** I +- **Opcode:** `0x03` +- **Funct3:** `0x1` +- **Operands:** `rd, offset(rs1)` +- **Description:** Load halfword (sign-extended) +- **Operation:** `rd = sext(MEM[rs1 + offset][15:0])` + +### LW + +- **Format:** I +- **Opcode:** `0x03` +- **Funct3:** `0x2` +- **Operands:** `rd, offset(rs1)` +- **Description:** Load word +- **Operation:** `rd = MEM[rs1 + offset][31:0]` + +### LBU + +- **Format:** I +- **Opcode:** `0x03` +- **Funct3:** `0x4` +- **Operands:** `rd, offset(rs1)` +- **Description:** Load byte (zero-extended) +- **Operation:** `rd = MEM[rs1 + offset][7:0]` + +### LHU + +- **Format:** I +- **Opcode:** `0x03` +- **Funct3:** `0x5` +- **Operands:** `rd, offset(rs1)` +- **Description:** Load halfword (zero-extended) +- **Operation:** `rd = MEM[rs1 + offset][15:0]` + +### SB + +- **Format:** S +- **Opcode:** `0x23` +- **Funct3:** `0x0` +- **Operands:** `rs2, offset(rs1)` +- **Description:** Store byte +- **Operation:** `MEM[rs1 + offset][7:0] = rs2[7:0]` + +### SH + +- **Format:** S +- **Opcode:** `0x23` +- **Funct3:** `0x1` +- **Operands:** `rs2, offset(rs1)` +- **Description:** Store halfword +- **Operation:** `MEM[rs1 + offset][15:0] = rs2[15:0]` + +### SRAI + +- **Format:** I +- **Opcode:** `0x13` +- **Funct3:** `0x5` +- **Operands:** `rd, rs1, shamt5` +- **Description:** Arithmetic right shift by immediate shift amount +- **Operation:** `rd = rs1 >>> shamt` + +### SLLI + +- **Format:** I +- **Opcode:** `0x13` +- **Funct3:** `0x1` +- **Operands:** `rd, rs1, shamt5` +- **Description:** Logical left shift by immediate shift amount +- **Operation:** `rd = rs1 << shamt` + +### SRA + +- **Format:** R +- **Opcode:** `0x33` +- **Funct3:** `0x5` +- **Funct7:** `0x20` +- **Operands:** `rd, rs1, rs2` +- **Description:** Arithmetic right shift by lower 5 bits of rs2 +- **Operation:** `rd = rs1 >>> rs2[4:0]` + +### SRL + +- **Format:** R +- **Opcode:** `0x33` +- **Funct3:** `0x5` +- **Funct7:** `0x00` +- **Operands:** `rd, rs1, rs2` +- **Description:** Logical right shift by lower 5 bits of rs2 +- **Operation:** `rd = rs1 >> rs2[4:0]` + +### SLL + +- **Format:** R +- **Opcode:** `0x33` +- **Funct3:** `0x1` +- **Funct7:** `0x00` +- **Operands:** `rd, rs1, rs2` +- **Description:** Logical left shift by lower 5 bits of rs2 +- **Operation:** `rd = rs1 << rs2[4:0]` + +### SRLI + +- **Format:** I +- **Opcode:** `0x13` +- **Funct3:** `0x5` +- **Operands:** `rd, rs1, shamt5` +- **Description:** Logical right shift by immediate shift amount +- **Operation:** `rd = rs1 >> shamt` + +### FENCEI + +- **Format:** I +- **Opcode:** `0x0F` +- **Funct3:** `0x1` +- **Operands:** `—` +- **Description:** Instruction fence — synchronizes instruction and data streams +- **Operation:** `Flushes instruction cache after data writes` + +### FENCE + +- **Format:** I +- **Opcode:** `0x0F` +- **Funct3:** `0x0` +- **Operands:** `pred, succ` +- **Description:** Memory ordering fence +- **Operation:** `Orders memory accesses as specified by pred and succ fields` + +### CSRRCI + +- **Format:** I +- **Opcode:** `0x73` +- **Funct3:** `0x7` +- **Operands:** `rd, csr, uimm5` +- **Description:** Atomic read and clear bits in CSR with immediate +- **Operation:** `rd = CSR[csr]; CSR[csr] &= ~zimm` + +### ECALL + +- **Format:** I +- **Opcode:** `0x73` +- **Funct3:** `0x0` +- **Operands:** `—` +- **Description:** Environment call — raises a system call exception +- **Operation:** `Traps to the configured exception handler` + +### EBREAK + +- **Format:** I +- **Opcode:** `0x73` +- **Funct3:** `0x0` +- **Operands:** `—` +- **Description:** Breakpoint — raises a breakpoint exception +- **Operation:** `Used by debuggers to halt program execution` + +### CSRRW + +- **Format:** I +- **Opcode:** `0x73` +- **Funct3:** `0x1` +- **Operands:** `rd, csr, rs1` +- **Description:** Atomic read/write CSR — writes rs1 to CSR, returns old value in rd +- **Operation:** `rd = CSR[csr]; CSR[csr] = rs1` + +### CSRRS + +- **Format:** I +- **Opcode:** `0x73` +- **Funct3:** `0x2` +- **Operands:** `rd, csr, rs1` +- **Description:** Atomic read and set bits in CSR +- **Operation:** `rd = CSR[csr]; CSR[csr] |= rs1` + +### CSRRC + +- **Format:** I +- **Opcode:** `0x73` +- **Funct3:** `0x3` +- **Operands:** `rd, csr, rs1` +- **Description:** Atomic read and clear bits in CSR +- **Operation:** `rd = CSR[csr]; CSR[csr] &= ~rs1` + +### CSRRWI + +- **Format:** I +- **Opcode:** `0x73` +- **Funct3:** `0x5` +- **Operands:** `rd, csr, uimm5` +- **Description:** Atomic read/write CSR with immediate +- **Operation:** `rd = CSR[csr]; CSR[csr] = zimm` + +### CSRRSI + +- **Format:** I +- **Opcode:** `0x73` +- **Funct3:** `0x6` +- **Operands:** `rd, csr, uimm5` +- **Description:** Atomic read and set bits in CSR with immediate +- **Operation:** `rd = CSR[csr]; CSR[csr] |= zimm` + +\newpage + +# Opcode Map + +The following table shows the top-level opcode mapping. The opcode occupies bits 6:0 of every instruction. + +| opcode[6:0] | Type | Instructions | +|-------------|------|-------------| +| `0x33` (51) | R | ADD, SUB, SLT, SLTU, AND, OR, XOR, SLL, SRL, SRA (10 inst)s | +| `0x13` (19) | I | ADDI, SLTI, SLTIU, ANDI, ORI, XORI, SLLI, SRLI, SRAI (9 inst)s | +| `0x37` (55) | U | LUI (1 inst) | +| `0x17` (23) | U | AUIPC (1 inst) | +| `0x03` (3) | I | LB, LH, LW, LBU, LHU (5 inst)s | +| `0x23` (35) | S | SB, SH, SW (3 inst)s | +| `0x63` (99) | B | BEQ, BNE, BLT, BGE, BLTU, BGEU (6 inst)s | +| `0x6F` (111) | J | JAL (1 inst) | +| `0x67` (103) | I | JALR (1 inst) | +| `0x0F` (15) | I | FENCE, FENCEI (2 inst)s | +| `0x73` (115) | I | ECALL, EBREAK, CSRRW, CSRRS, CSRRC, CSRRWI, CSRRSI, CSRRCI (8 inst)s | + +### Encoding Matrix + +| opcode \ funct3 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | +|---|---|---|---|---|---|---|---|---| +| `0x33` | ADD | SLL | SLT | SLTU | XOR | SRL | OR | AND | +| `0x13` | ADDI | SLLI | SLTI | SLTIU | XORI | SRLI | ORI | ANDI | +| `0x37` | — | — | — | — | — | — | — | — | +| `0x17` | — | — | — | — | — | — | — | — | +| `0x03` | LB | LH | LW | — | LBU | LHU | — | — | +| `0x23` | SB | SH | SW | — | — | — | — | — | +| `0x63` | BEQ | BNE | — | — | BLT | BGE | BLTU | BGEU | +| `0x6F` | — | — | — | — | — | — | — | — | +| `0x67` | JALR | — | — | — | — | — | — | — | +| `0x0F` | FENCE | FENCEI | — | — | — | — | — | — | +| `0x73` | ECALL | CSRRW | CSRRS | CSRRC | — | CSRRWI | CSRRSI | CSRRCI | + +\newpage + +# Exception and Trap Handling + +## Exception Types + +| Exception Code | Name | Description | +|----------------|------|-------------| +| 0 | Instruction address misaligned | PC not aligned to 4 bytes | +| 1 | Instruction access fault | Failed to fetch instruction | +| 2 | Illegal instruction | Unrecognized opcode | +| 3 | Breakpoint | EBREAK instruction executed | +| 4 | Load address misaligned | Load address not properly aligned | +| 5 | Load access fault | Failed to load from memory | +| 6 | Store address misaligned | Store address not properly aligned | +| 7 | Store access fault | Failed to store to memory | +| 8 | Environment call from U-mode | ECALL in user mode | +| 9 | Environment call from S-mode | ECALL in supervisor mode | +| 11 | Environment call from M-mode | ECALL in machine mode | + +## Trap Vector + +On taking a trap, the implementation: + +1. Sets `mepc` to the PC of the trapping instruction (or next PC for ECALL/EBREAK) +2. Sets `mcause` to the exception code +3. Sets `mtval` to exception-specific information +4. Sets `mstatus.MPP` to the current privilege mode +5. Sets `mstatus.MPIE` to `mstatus.MIE` +6. Clears `mstatus.MIE` +7. Sets PC to `mtvec` + +\newpage + +# Pseudo-Instructions + +| Pseudo-instruction | Expansion | Description | +|-------------------|-----------|-------------| +| `NOP` | `ADDI x0, x0, 0` | No operation | +| `MV rd, rs` | `ADDI rd, rs, 0` | Copy register | +| `NOT rd, rs` | `XORI rd, rs, -1` | Bitwise NOT | +| `NEG rd, rs` | `SUB rd, x0, rs` | Negate register | +| `LI rd, imm` | (multiple) | Load immediate | +| `LA rd, label` | (multiple) | Load address | +| `RET` | `JALR x0, x1, 0` | Return from subroutine | +| `CALL label` | (multiple) | Call subroutine | +| `J label` | `JAL x0, label` | Unconditional jump | +| `JR rs` | `JALR x0, rs, 0` | Jump register | + +\newpage + +# Control and Status Registers + +The following CSRs are accessible via the `CSRRW`, `CSRRS`, `CSRRC`, and their immediate variants. + +| Address | Name | Description | +|---------|------|-------------| +| `0x300` | mstatus | Machine status register — holds global interrupt enable and privilege state | +| `0x301` | misa | Machine ISA register — encodes supported ISA extensions | +| `0x302` | medeleg | Machine exception delegation register | +| `0x303` | mideleg | Machine interrupt delegation register | +| `0x304` | mie | Machine interrupt-enable register | +| `0x305` | mtvec | Machine trap-handler base address | +| `0x306` | mcounteren | Machine counter enable register | +| `0x340` | mscratch | Scratch register for machine-mode trap handlers | +| `0x341` | mepc | Machine exception program counter — holds PC of trapping instruction | +| `0x342` | mcause | Machine trap cause — encodes exception or interrupt cause | +| `0x343` | mtval | Machine trap value — exception-specific information | +| `0x344` | mip | Machine interrupt pending register | +| `0x3A0` | pmpcfg0 | Physical memory protection configuration 0 | +| `0x3B0` | pmpaddr0 | Physical memory protection address 0 | +| `0x3B1` | pmpaddr1 | Physical memory protection address 1 | +| `0x3B2` | pmpaddr2 | Physical memory protection address 2 | +| `0x3B3` | pmpaddr3 | Physical memory protection address 3 | +| `0xB00` | mcycle | Machine cycle counter — counts number of clock cycles | +| `0xB02` | minstret | Machine instructions-retired counter | +| `0xB03` | mhpmcounter3 | Machine hardware performance counter 3 | +| `0xB04` | mhpmcounter4 | Machine hardware performance counter 4 | +| `0xB05` | mhpmcounter5 | Machine hardware performance counter 5 | +| `0x323` | mhpmevent3 | Machine hardware performance event selector 3 | +| `0x324` | mhpmevent4 | Machine hardware performance event selector 4 | +| `0x325` | mhpmevent5 | Machine hardware performance event selector 5 | +| `0xC00` | ucycle | User-mode cycle counter read | +| `0xC02` | uinstret | User-mode instructions-retired read | +| `0xC80` | ucycleh | Upper 32 bits of user-mode cycle counter | +| `0xC82` | uinstreth | Upper 32 bits of user-mode instructions-retired counter | +| `0x100` | sstatus | Supervisor status register | +| `0x104` | sie | Supervisor interrupt-enable register | +| `0x105` | stvec | Supervisor trap-handler base address | +| `0x140` | sscratch | Scratch register for supervisor-mode trap handlers | +| `0x141` | sepc | Supervisor exception program counter | +| `0x142` | scause | Supervisor trap cause register | +| `0x143` | stval | Supervisor trap value register | +| `0x144` | sip | Supervisor interrupt pending register | +| `0x180` | satp | Supervisor address translation and protection — controls page tables | + +\newpage + +# Quick Reference + +## Instruction Encoding Quick Reference + +### R-Type + +``` + 31:27 | 26:25 | 24:20 | 19:15 | 14:12 | 11:7 | 6:0 + funct7 | — | rs2 | rs1 | funct3 | rd | opcode + 7 | 2 | 5 | 5 | 3 | 5 | 7 +``` + +### I-Type + +``` + 31:20 | 19:15 | 14:12 | 11:7 | 6:0 + imm[11:0]| rs1 | funct3 | rd | opcode + 12 | 5 | 3 | 5 | 7 +``` + +### S-Type + +``` + 31:27 | 26:25 | 24:20 | 19:15 | 14:12 | 11:7 | 6:0 + imm[11:5]| — | rs2 | rs1 | funct3 | imm[4:0]| opcode + 7 | 2 | 5 | 5 | 3 | 5 | 7 +``` + +### B-Type + +``` + 31 | 30:27 | 26:25 | 24:20 | 19:15 | 14:12 | 11:8 | 7 | 6:0 + imm[12]| imm[10:5]| — | rs2 | rs1 | funct3 | imm[4:1]| imm[11]| opcode + 1 | 6 | 2 | 5 | 5 | 3 | 4 | 1 | 7 +``` + +### U-Type + +``` + 31:12 | 11:7 | 6:0 + imm[31:12] | rd | opcode + 20 | 5 | 7 +``` + +### J-Type + +``` + 31 | 30:21 | 20 | 19:12 | 11:7 | 6:0 + imm[20]| imm[10:1]| imm[11]| imm[19:12]| rd | opcode + 1 | 10 | 1 | 8 | 5 | 7 +``` + diff --git a/doc/spec/isa_reference.pdf b/doc/spec/isa_reference.pdf Binary files differnew file mode 100644 index 0000000..73f0733 --- /dev/null +++ b/doc/spec/isa_reference.pdf diff --git a/editors/vscode/README.md b/editors/vscode/README.md new file mode 100644 index 0000000..e7db36d --- /dev/null +++ b/editors/vscode/README.md @@ -0,0 +1,34 @@ +# ISA Syntax Highlighting for VS Code + +Highlights `.isa` definition files — block types, keys, hex values, comments, bit ranges, and booleans. + +## Install + +```bash +# Symlink into your VS Code extensions folder +mkdir -p ~/.vscode/extensions +ln -sf "$PWD" ~/.vscode/extensions/myisa-isa-syntax +``` + +Or copy the folder: + +```bash +cp -r "$PWD" ~/.vscode/extensions/myisa-isa-syntax +``` + +Then **reload VS Code** (`Cmd+Shift+P` → "Developer: Reload Window"). + +Open any `.isa` file — syntax highlighting activates automatically. + +## Colors by token + +| Token | Color | +|-------|-------| +| `ARCH`, `FORMAT`, `REGISTER`, `INSTRUCTION`, `CSR` | Keyword purple | +| Block name (e.g. `ADD`, `x0`, `R`) | Entity/type blue-green | +| `END` | Keyword purple | +| Keys (`NAME`, `OPCODE`, `DESC`, etc.) | Property orange/yellow | +| `0x...` hex values | Numeric green | +| `31:25` bit ranges | Numeric green | +| `true`/`false` booleans | Language constant | +| `#` comments | Comment gray | diff --git a/editors/vscode/isa-syntax-1.0.0.vsix b/editors/vscode/isa-syntax-1.0.0.vsix Binary files differnew file mode 100644 index 0000000..9efef29 --- /dev/null +++ b/editors/vscode/isa-syntax-1.0.0.vsix diff --git a/editors/vscode/language-configuration.json b/editors/vscode/language-configuration.json new file mode 100644 index 0000000..e7a0d58 --- /dev/null +++ b/editors/vscode/language-configuration.json @@ -0,0 +1,8 @@ +{ + "comments": { + "lineComment": "#" + }, + "brackets": [], + "autoClosingPairs": [], + "surroundingPairs": [] +} diff --git a/editors/vscode/package.json b/editors/vscode/package.json new file mode 100644 index 0000000..d591994 --- /dev/null +++ b/editors/vscode/package.json @@ -0,0 +1,27 @@ +{ + "name": "isa-syntax", + "displayName": "ISA Definition Syntax Highlighting", + "description": "Syntax highlighting for .isa ISA definition files (used by the MyISA doc generator)", + "version": "1.0.0", + "publisher": "myisa", + "license": "MIT", + "engines": { "vscode": "^1.80.0" }, + "categories": ["Programming Languages"], + "contributes": { + "languages": [{ + "id": "isa", + "aliases": ["ISA Definition", "isa"], + "extensions": [".isa"], + "configuration": "./language-configuration.json", + "icon": { + "light": "./icon.png", + "dark": "./icon.png" + } + }], + "grammars": [{ + "language": "isa", + "scopeName": "source.isa", + "path": "./syntaxes/isa.tmLanguage.json" + }] + } +} diff --git a/editors/vscode/syntaxes/isa.tmLanguage.json b/editors/vscode/syntaxes/isa.tmLanguage.json new file mode 100644 index 0000000..c4de36a --- /dev/null +++ b/editors/vscode/syntaxes/isa.tmLanguage.json @@ -0,0 +1,70 @@ +{ + "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", + "name": "ISA Definition", + "scopeName": "source.isa", + "fileTypes": ["isa"], + "patterns": [ + { "include": "#comments" }, + { "include": "#block-header" }, + { "include": "#end-keyword" }, + { "include": "#keys" }, + { "include": "#hex-numbers" }, + { "include": "#bit-ranges" }, + { "include": "#booleans" }, + { "include": "#numbers" }, + { "include": "#names" } + ], + "repository": { + "comments": { + "patterns": [{ + "match": "#.*$", + "name": "comment.line.hash.isa" + }] + }, + "block-header": { + "patterns": [ + { + "match": "\\b(ARCH|FORMAT|REGISTER|INSTRUCTION|CSR)\\b", + "name": "keyword.control.block-type.isa" + }, + { + "match": "(?<=(ARCH|FORMAT|REGISTER|INSTRUCTION|CSR)\\s+)([A-Za-z_][A-Za-z0-9_]*)", + "name": "entity.name.type.isa" + } + ] + }, + "end-keyword": { + "match": "\\bEND\\b", + "name": "keyword.control.end.isa" + }, + "keys": { + "patterns": [ + { "match": "\\b(NAME|VERSION|DATE|STATUS)\\b", "name": "support.type.property-name.meta.isa" }, + { "match": "\\b(WIDTH|FIELD|FIELDS)\\b", "name": "support.type.property-name.format.isa" }, + { "match": "\\b(ABBR|DESC|PRESERVE|CALLER|ARG|INDEX)\\b", "name": "support.type.property-name.register.isa" }, + { "match": "\\b(FORMAT|OPCODE|FUNCT3|FUNCT7|OPERANDS|NOTE|CATEGORY|IMM)\\b", "name": "support.type.property-name.instruction.isa" }, + { "match": "\\b(NUMBER)\\b", "name": "support.type.property-name.csr.isa" } + ] + }, + "hex-numbers": { + "match": "\\b0[xX][0-9a-fA-F]+\\b", + "name": "constant.numeric.hex.isa" + }, + "bit-ranges": { + "match": "\\b([0-9]+:[0-9]+)\\b", + "name": "constant.numeric.bit-range.isa" + }, + "booleans": { + "match": "\\b(true|false|yes|no)\\b", + "name": "constant.language.boolean.isa" + }, + "numbers": { + "match": "\\b[0-9]+\\b", + "name": "constant.numeric.decimal.isa" + }, + "names": { + "match": "\\b([A-Za-z_][A-Za-z0-9_]*)\\b", + "name": "variable.other.isa" + } + } +} diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..b7b399c --- /dev/null +++ b/examples/README.md @@ -0,0 +1,7 @@ +# Example Programs + +| Directory | Contents | +|--------------|--------------------------------------| +| `asm/` | Hand-written assembly examples | +| `c/` | C programs compiled to Fyntv | +| `benchmarks/`| Standard benchmark ports | diff --git a/gen/README.md b/gen/README.md new file mode 100644 index 0000000..958814c --- /dev/null +++ b/gen/README.md @@ -0,0 +1,14 @@ +# Code Generators (C Programs) + +Each generator reads the ISA definitions (`../isa/`) and produces C source +code for the assembler, disassembler, simulator, and documentation. + +| Generator | Input | Output | +|------------------|-------------------|----------------------------------| +| `tablegen/` | ISA encoding | Instruction decode tables | +| `decoder/` | Encoding tables | Decode switch/case source | +| `encoder/` | Opcode tables | Assembler encode logic | +| `disassembler/` | Opcode tables | Disassembler string formatting | +| `opcodes/` | Opcode YAML | `opcodes.h`, `opcodes.c` | +| `abi/` | ABI spec | Calling convention stubs | +| `docs/` | ISA YAML | RST/Markdown/HTML ISA manual | diff --git a/gen/decoder/main.c b/gen/decoder/main.c new file mode 100644 index 0000000..d9676f6 --- /dev/null +++ b/gen/decoder/main.c @@ -0,0 +1,3 @@ +// decoder — Generate C decoder source from tablegen output +// Reads: tablegen output tables +// Writes: decode.c / decode.h for the simulator and tools diff --git a/gen/disassembler/main.c b/gen/disassembler/main.c new file mode 100644 index 0000000..b3548c7 --- /dev/null +++ b/gen/disassembler/main.c @@ -0,0 +1,3 @@ +// disassembler — Generate disassembler source +// Reads: opcode definitions from ../isa/opcodes/ +// Writes: disasm.c / disasm.h for objdump and the simulator diff --git a/gen/docs/convert.sh b/gen/docs/convert.sh new file mode 100644 index 0000000..047eb63 --- /dev/null +++ b/gen/docs/convert.sh @@ -0,0 +1,55 @@ +#!/bin/sh +# convert.sh — Convert generated Markdown ISA doc to PDF +# Usage: ./convert.sh [input.md] [output.pdf] +# +# Requires: pandoc + xelatex (or wkhtmltopdf) +# Install: brew install pandoc mactex (macOS) +# apt install pandoc texlive (Linux) + +set -e + +INPUT="${1:-../../doc/spec/isa_reference.md}" +OUTPUT="${2:-../../doc/spec/isa_reference.pdf}" +DIR="$(cd "$(dirname "$OUTPUT")" && pwd)" +BASE="$(basename "$OUTPUT")" + +echo "Converting: $INPUT → $OUTPUT" + +if command -v pandoc >/dev/null 2>&1; then + if command -v xelatex >/dev/null 2>&1; then + pandoc "$INPUT" \ + -o "$OUTPUT" \ + --pdf-engine=xelatex \ + -V geometry:margin=1in \ + -V colorlinks=true \ + -V linkcolor=blue \ + -V toccolor=blue \ + -V mainfont="Times New Roman" \ + -V monofont="Courier New" \ + --toc --toc-depth=3 \ + --highlight-style=tango + echo "PDF generated: $OUTPUT" + elif command -v wkhtmltopdf >/dev/null 2>&1; then + DIR="$(dirname "$INPUT")" + pandoc "$INPUT" \ + -o "${DIR}/isa_reference.html" \ + --self-contained \ + --toc --toc-depth=3 + wkhtmltopdf \ + --margin-top 20mm \ + --margin-bottom 20mm \ + --margin-left 15mm \ + --margin-right 15mm \ + --toc \ + "${DIR}/isa_reference.html" "$OUTPUT" + echo "PDF generated: $OUTPUT" + else + echo "Error: No PDF engine found." + echo "Install: brew install pandoc mactex (macOS)" + echo " or: apt install pandoc texlive-latex-base (Linux)" + exit 1 + fi +else + echo "Error: pandoc not found. Install it first." + exit 1 +fi diff --git a/gen/docs/generator.c b/gen/docs/generator.c new file mode 100644 index 0000000..f67e42f --- /dev/null +++ b/gen/docs/generator.c @@ -0,0 +1,481 @@ +#include "generator.h" +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +static void write_title_page(FILE *out, const IsaDb *db) { + fprintf(out, "---\n"); + fprintf(out, "title: \"%s Instruction Set Architecture\"\n", db->arch_name[0] ? db->arch_name : "Fyntv"); + fprintf(out, "subtitle: \"Reference Manual\"\n"); + fprintf(out, "version: \"%s\"\n", db->arch_version[0] ? db->arch_version : "1.0.0"); + fprintf(out, "date: \"%s\"\n", db->arch_date[0] ? db->arch_date : "June 2026"); + fprintf(out, "status: \"%s\"\n", db->arch_status[0] ? db->arch_status : "Draft"); + fprintf(out, "geometry: margin=1in\n"); + fprintf(out, "toc: true\n"); + fprintf(out, "numbersections: true\n"); + fprintf(out, "---\n\n"); +} + +static void write_revision_history(FILE *out, const IsaDb *db) { + fprintf(out, "\\newpage\n\n"); + fprintf(out, "# Revision History\n\n"); + fprintf(out, "| Version | Date | Description |\n"); + fprintf(out, "|---------|------|-------------|\n"); + fprintf(out, "| %s | %s | Initial %s release |\n", + db->arch_version[0] ? db->arch_version : "1.0.0", + db->arch_date[0] ? db->arch_date : "2026-06", + db->arch_name[0] ? db->arch_name : "ISA"); + fprintf(out, "\n\\newpage\n\n"); +} + +static void write_toc_placeholder(FILE *out) { + fprintf(out, "\\tableofcontents\n\n"); + fprintf(out, "\\newpage\n\n"); +} + +static void write_intro(FILE *out, const IsaDb *db) { + fprintf(out, "# Introduction\n\n"); + fprintf(out, "This document defines the **%s Instruction Set Architecture (ISA)** version %s.\n\n", + db->arch_name[0] ? db->arch_name : "Fyntv", + db->arch_version[0] ? db->arch_version : "1.0.0"); + + fprintf(out, "## Scope\n\n"); + fprintf(out, "This specification covers:\n\n"); + fprintf(out, "- Instruction formats and encoding\n"); + fprintf(out, "- Register file and calling convention\n"); + fprintf(out, "- All base and extension instructions\n"); + fprintf(out, "- Control and status registers (CSRs)\n"); + fprintf(out, "- Exception and trap handling\n\n"); + + fprintf(out, "## ISA Overview\n\n"); + fprintf(out, "The %s ISA is a load-store architecture with:\n\n", + db->arch_name[0] ? db->arch_name : "Fyntv"); + fprintf(out, "- 32 general-purpose registers (x0–x31)\n"); + fprintf(out, "- Fixed-width 32-bit instructions\n"); + fprintf(out, "- Three operand formats: register (R-type), immediate (I-type), and store (S-type)\n"); + fprintf(out, "- Branch (B-type) and upper-immediate (U-type) variants\n"); + fprintf(out, "- A separate jump-and-link (J-type) format\n\n"); + fprintf(out, "\\newpage\n\n"); +} + +static void write_registers(FILE *out, const IsaDb *db) { + fprintf(out, "# Registers\n\n"); + fprintf(out, "## General-Purpose Registers\n\n"); + fprintf(out, "The architecture provides 32 general-purpose registers (x0–x31), " + "each 32 bits wide. Register x0 is hardwired to the constant zero.\n\n"); + + fprintf(out, "| Register | ABI Name | Description | Callee-Saved |\n"); + fprintf(out, "|----------|----------|-------------|--------------|\n"); + for (int i = 0; i < db->num_registers; i++) { + const IsaRegister *r = &db->registers[i]; + fprintf(out, "| %s | %s | %s | %s |\n", + r->name, r->abbr[0] ? r->abbr : "—", + r->desc[0] ? r->desc : "—", + r->preserve ? "Yes" : (r->preserve == 0 && r->index != 0 ? "No" : "—")); + } + + fprintf(out, "\n### ABI Calling Convention\n\n"); + fprintf(out, "| Register | ABI Name | Caller-Saved | Argument |\n"); + fprintf(out, "|----------|----------|-------------|----------|\n"); + for (int i = 0; i < db->num_registers; i++) { + const IsaRegister *r = &db->registers[i]; + fprintf(out, "| %s | %s | %s | %s |\n", + r->name, r->abbr[0] ? r->abbr : "—", + r->caller_saved ? "Yes" : "No", + r->arg_reg ? "Yes" : "No"); + } + fprintf(out, "\n\\newpage\n\n"); +} + +static void write_format_diagram(FILE *out, const IsaFormat *fmt) { + fprintf(out, "### %s-Type Format\n\n", fmt->name); + fprintf(out, "Bit width: **%d bits**\n\n", fmt->width); + fprintf(out, "```\n"); + + for (int row = 0; row < 3; row++) { + for (int j = fmt->num_fields - 1; j >= 0; j--) { + int fw = fmt->fields[j].high - fmt->fields[j].low + 1; + if (j < fmt->num_fields - 1) fputc('|', out); + + if (row == 0) { + int rpad = (fw - 5) / 2; + int lpad = fw - 5 - rpad; + if (rpad < 0) rpad = 0; + if (lpad < 0) lpad = 0; + for (int k = 0; k < rpad; k++) fputc(' ', out); + fprintf(out, "%d:%d", fmt->fields[j].high, fmt->fields[j].low); + for (int k = 0; k < lpad; k++) fputc(' ', out); + } else if (row == 1) { + int slen = (int)strlen(fmt->fields[j].name); + int rpad = (fw - slen) / 2; + int lpad = fw - slen - rpad; + if (rpad < 0) rpad = 0; + if (lpad < 0) lpad = 0; + for (int k = 0; k < rpad; k++) fputc(' ', out); + fprintf(out, "%s", fmt->fields[j].name); + for (int k = 0; k < lpad; k++) fputc(' ', out); + } else { + char buf[16]; + snprintf(buf, sizeof(buf), "%d", fw); + int slen = (int)strlen(buf); + int rpad = (fw - slen) / 2; + int lpad = fw - slen - rpad; + if (rpad < 0) rpad = 0; + if (lpad < 0) lpad = 0; + for (int k = 0; k < rpad; k++) fputc(' ', out); + fprintf(out, "%s", buf); + for (int k = 0; k < lpad; k++) fputc(' ', out); + } + } + fprintf(out, "\n"); + } + + fprintf(out, "```\n\n"); +} + +static void write_formats(FILE *out, const IsaDb *db) { + fprintf(out, "# Instruction Formats\n\n"); + fprintf(out, "Instructions are encoded in one of several fixed formats. " + "All formats share the same opcode placement (bits 6:0) to " + "allow efficient decoding.\n\n"); + + for (int i = 0; i < db->num_formats; i++) { + write_format_diagram(out, &db->formats[i]); + } + + fprintf(out, "\\newpage\n\n"); +} + +static int cat_cmp(const void *a, const void *b) { + return strcasecmp(((const IsaInstruction *)a)->category, + ((const IsaInstruction *)b)->category); +} + +static void write_instructions(FILE *out, const IsaDb *db) { + fprintf(out, "# Instruction Set\n\n"); + + if (db->num_instructions == 0) { + fprintf(out, "No instructions defined.\n\n"); + return; + } + + IsaInstruction *sorted = malloc(db->num_instructions * sizeof(IsaInstruction)); + if (!sorted) return; + memcpy(sorted, db->instructions, db->num_instructions * sizeof(IsaInstruction)); + qsort(sorted, db->num_instructions, sizeof(IsaInstruction), cat_cmp); + + char current_cat[MAX_LABEL] = ""; + for (int i = 0; i < db->num_instructions; i++) { + const IsaInstruction *inst = &sorted[i]; + const char *cat = inst->category[0] ? inst->category : "Uncategorized"; + + if (strcasecmp(current_cat, cat) != 0) { + strncpy(current_cat, cat, MAX_LABEL - 1); + fprintf(out, "## %s Operations\n\n", cat); + + fprintf(out, "| Mnemonic | Format | Opcode | Funct3 | Funct7 | Operands | Description |\n"); + fprintf(out, "|----------|--------|--------|--------|--------|----------|-------------|\n"); + } + + char f3buf[16], f7buf[16]; + if (inst->funct3_valid) snprintf(f3buf, sizeof(f3buf), "0x%X", inst->funct3); + else strcpy(f3buf, "—"); + if (inst->funct7_valid) snprintf(f7buf, sizeof(f7buf), "0x%02X", inst->funct7); + else strcpy(f7buf, "—"); + + fprintf(out, "| `%s` | %s | `0x%02X` | %s | %s | %s | %s |\n", + inst->name, + inst->fmt_name, + inst->opcode, + f3buf, f7buf, + inst->operands[0] ? inst->operands : "—", + inst->desc); + } + + fprintf(out, "\n\\newpage\n\n"); + + fprintf(out, "## Instruction Details\n\n"); + strcpy(current_cat, ""); + for (int i = 0; i < db->num_instructions; i++) { + const IsaInstruction *inst = &sorted[i]; + const char *cat = inst->category[0] ? inst->category : "Uncategorized"; + + if (strcasecmp(current_cat, cat) != 0) { + strncpy(current_cat, cat, MAX_LABEL - 1); + } + + fprintf(out, "### %s\n\n", inst->name); + fprintf(out, "- **Format:** %s\n", inst->fmt_name); + fprintf(out, "- **Opcode:** `0x%02X`\n", inst->opcode); + if (inst->funct3_valid) + fprintf(out, "- **Funct3:** `0x%X`\n", inst->funct3); + if (inst->funct7_valid) + fprintf(out, "- **Funct7:** `0x%02X`\n", inst->funct7); + fprintf(out, "- **Operands:** `%s`\n", inst->operands[0] ? inst->operands : "(none)"); + fprintf(out, "- **Description:** %s\n", inst->desc); + if (inst->note[0]) + fprintf(out, "- **Operation:** `%s`\n", inst->note); + fprintf(out, "\n"); + } + + free(sorted); + fprintf(out, "\\newpage\n\n"); +} + +static void write_opcode_map(FILE *out, const IsaDb *db) { + fprintf(out, "# Opcode Map\n\n"); + fprintf(out, "The following table shows the top-level opcode mapping. " + "The opcode occupies bits 6:0 of every instruction.\n\n"); + + fprintf(out, "| opcode[6:0] | Type | Instructions |\n"); + fprintf(out, "|-------------|------|-------------|\n"); + + int seen_ops[128] = {0}; + for (int i = 0; i < db->num_instructions; i++) { + const IsaInstruction *inst = &db->instructions[i]; + int op = inst->opcode & 0x7F; + if (seen_ops[op]) continue; + seen_ops[op] = 1; + + fprintf(out, "| `0x%02X` (%d) | %s | %s", op, op, inst->fmt_name, inst->name); + int count = 1; + for (int j = i + 1; j < db->num_instructions; j++) { + if ((db->instructions[j].opcode & 0x7F) == (unsigned)op) { + fprintf(out, ", %s", db->instructions[j].name); + count++; + } + } + fprintf(out, " (%d inst)%s |\n", count, count > 1 ? "s" : ""); + } + + fprintf(out, "\n### Encoding Matrix\n\n"); + fprintf(out, "| opcode \\ funct3 |"); + for (int f3 = 0; f3 < 8; f3++) fprintf(out, " %X |", f3); + fprintf(out, "\n"); + fprintf(out, "|"); + for (int f3 = 0; f3 < 9; f3++) fprintf(out, "---|"); + fprintf(out, "\n"); + + int seen_ops2[128] = {0}; + for (int i = 0; i < db->num_instructions; i++) { + const IsaInstruction *inst = &db->instructions[i]; + int op = inst->opcode & 0x7F; + if (seen_ops2[op]) continue; + seen_ops2[op] = 1; + + fprintf(out, "| `0x%02X` |", op); + for (int f3 = 0; f3 < 8; f3++) { + int found = 0; + for (int j = 0; j < db->num_instructions; j++) { + if ((db->instructions[j].opcode & 0x7F) == (unsigned)op && + db->instructions[j].funct3 == (unsigned)f3 && + db->instructions[j].funct3_valid) { + fprintf(out, " %s |", db->instructions[j].name); + found = 1; + break; + } + } + if (!found) fprintf(out, " — |"); + } + fprintf(out, "\n"); + } + + fprintf(out, "\n\\newpage\n\n"); +} + +static void write_csrs(FILE *out, const IsaDb *db) { + if (db->num_csrs == 0) return; + + fprintf(out, "# Control and Status Registers\n\n"); + fprintf(out, "The following CSRs are accessible via the `CSRRW`, `CSRRS`, " + "`CSRRC`, and their immediate variants.\n\n"); + + fprintf(out, "| Address | Name | Description |\n"); + fprintf(out, "|---------|------|-------------|\n"); + for (int i = 0; i < db->num_csrs; i++) { + fprintf(out, "| `0x%03X` | %s | %s |\n", + db->csrs[i].number, db->csrs[i].name, db->csrs[i].desc); + } + fprintf(out, "\n\\newpage\n\n"); +} + +static void write_exceptions(FILE *out) { + fprintf(out, "# Exception and Trap Handling\n\n"); + fprintf(out, "## Exception Types\n\n"); + fprintf(out, "| Exception Code | Name | Description |\n"); + fprintf(out, "|----------------|------|-------------|\n"); + fprintf(out, "| 0 | Instruction address misaligned | PC not aligned to 4 bytes |\n"); + fprintf(out, "| 1 | Instruction access fault | Failed to fetch instruction |\n"); + fprintf(out, "| 2 | Illegal instruction | Unrecognized opcode |\n"); + fprintf(out, "| 3 | Breakpoint | EBREAK instruction executed |\n"); + fprintf(out, "| 4 | Load address misaligned | Load address not properly aligned |\n"); + fprintf(out, "| 5 | Load access fault | Failed to load from memory |\n"); + fprintf(out, "| 6 | Store address misaligned | Store address not properly aligned |\n"); + fprintf(out, "| 7 | Store access fault | Failed to store to memory |\n"); + fprintf(out, "| 8 | Environment call from U-mode | ECALL in user mode |\n"); + fprintf(out, "| 9 | Environment call from S-mode | ECALL in supervisor mode |\n"); + fprintf(out, "| 11 | Environment call from M-mode | ECALL in machine mode |\n\n"); + + fprintf(out, "## Trap Vector\n\n"); + fprintf(out, "On taking a trap, the implementation:\n\n"); + fprintf(out, "1. Sets `mepc` to the PC of the trapping instruction (or next PC for ECALL/EBREAK)\n"); + fprintf(out, "2. Sets `mcause` to the exception code\n"); + fprintf(out, "3. Sets `mtval` to exception-specific information\n"); + fprintf(out, "4. Sets `mstatus.MPP` to the current privilege mode\n"); + fprintf(out, "5. Sets `mstatus.MPIE` to `mstatus.MIE`\n"); + fprintf(out, "6. Clears `mstatus.MIE`\n"); + fprintf(out, "7. Sets PC to `mtvec`\n\n"); + + fprintf(out, "\\newpage\n\n"); +} + +static void write_pseudo_instructions(FILE *out) { + fprintf(out, "# Pseudo-Instructions\n\n"); + fprintf(out, "| Pseudo-instruction | Expansion | Description |\n"); + fprintf(out, "|-------------------|-----------|-------------|\n"); + fprintf(out, "| `NOP` | `ADDI x0, x0, 0` | No operation |\n"); + fprintf(out, "| `MV rd, rs` | `ADDI rd, rs, 0` | Copy register |\n"); + fprintf(out, "| `NOT rd, rs` | `XORI rd, rs, -1` | Bitwise NOT |\n"); + fprintf(out, "| `NEG rd, rs` | `SUB rd, x0, rs` | Negate register |\n"); + fprintf(out, "| `LI rd, imm` | (multiple) | Load immediate |\n"); + fprintf(out, "| `LA rd, label` | (multiple) | Load address |\n"); + fprintf(out, "| `RET` | `JALR x0, x1, 0` | Return from subroutine |\n"); + fprintf(out, "| `CALL label` | (multiple) | Call subroutine |\n"); + fprintf(out, "| `J label` | `JAL x0, label` | Unconditional jump |\n"); + fprintf(out, "| `JR rs` | `JALR x0, rs, 0` | Jump register |\n"); + fprintf(out, "\n\\newpage\n\n"); +} + +static void write_quick_reference(FILE *out) { + fprintf(out, "# Quick Reference\n\n"); + fprintf(out, "## Instruction Encoding Quick Reference\n\n"); + + fprintf(out, "### R-Type\n\n"); + fprintf(out, "```\n"); + fprintf(out, " 31:27 | 26:25 | 24:20 | 19:15 | 14:12 | 11:7 | 6:0\n"); + fprintf(out, " funct7 | — | rs2 | rs1 | funct3 | rd | opcode\n"); + fprintf(out, " 7 | 2 | 5 | 5 | 3 | 5 | 7\n"); + fprintf(out, "```\n\n"); + + fprintf(out, "### I-Type\n\n"); + fprintf(out, "```\n"); + fprintf(out, " 31:20 | 19:15 | 14:12 | 11:7 | 6:0\n"); + fprintf(out, " imm[11:0]| rs1 | funct3 | rd | opcode\n"); + fprintf(out, " 12 | 5 | 3 | 5 | 7\n"); + fprintf(out, "```\n\n"); + + fprintf(out, "### S-Type\n\n"); + fprintf(out, "```\n"); + fprintf(out, " 31:27 | 26:25 | 24:20 | 19:15 | 14:12 | 11:7 | 6:0\n"); + fprintf(out, " imm[11:5]| — | rs2 | rs1 | funct3 | imm[4:0]| opcode\n"); + fprintf(out, " 7 | 2 | 5 | 5 | 3 | 5 | 7\n"); + fprintf(out, "```\n\n"); + + fprintf(out, "### B-Type\n\n"); + fprintf(out, "```\n"); + fprintf(out, " 31 | 30:27 | 26:25 | 24:20 | 19:15 | 14:12 | 11:8 | 7 | 6:0\n"); + fprintf(out, " imm[12]| imm[10:5]| — | rs2 | rs1 | funct3 | imm[4:1]| imm[11]| opcode\n"); + fprintf(out, " 1 | 6 | 2 | 5 | 5 | 3 | 4 | 1 | 7\n"); + fprintf(out, "```\n\n"); + + fprintf(out, "### U-Type\n\n"); + fprintf(out, "```\n"); + fprintf(out, " 31:12 | 11:7 | 6:0\n"); + fprintf(out, " imm[31:12] | rd | opcode\n"); + fprintf(out, " 20 | 5 | 7\n"); + fprintf(out, "```\n\n"); + + fprintf(out, "### J-Type\n\n"); + fprintf(out, "```\n"); + fprintf(out, " 31 | 30:21 | 20 | 19:12 | 11:7 | 6:0\n"); + fprintf(out, " imm[20]| imm[10:1]| imm[11]| imm[19:12]| rd | opcode\n"); + fprintf(out, " 1 | 10 | 1 | 8 | 5 | 7\n"); + fprintf(out, "```\n\n"); +} + +int gen_markdown(const IsaDb *db, const char *output_path) { + FILE *out = fopen(output_path, "w"); + if (!out) { + fprintf(stderr, "Error: cannot write to %s\n", output_path); + return -1; + } + + printf("Generating: %s\n", output_path); + printf(" Formats: %d\n", db->num_formats); + printf(" Registers: %d\n", db->num_registers); + printf(" Instructions: %d\n", db->num_instructions); + printf(" CSRs: %d\n", db->num_csrs); + + write_title_page(out, db); + write_revision_history(out, db); + write_toc_placeholder(out); + write_intro(out, db); + write_registers(out, db); + write_formats(out, db); + write_instructions(out, db); + write_opcode_map(out, db); + write_exceptions(out); + write_pseudo_instructions(out); + + if (db->num_csrs > 0) + write_csrs(out, db); + + write_quick_reference(out); + + fclose(out); + printf(" Done: %s\n", output_path); + return 0; +} + +int gen_html(const IsaDb *db, const char *output_path) { + FILE *out = fopen(output_path, "w"); + if (!out) { + fprintf(stderr, "Error: cannot write to %s\n", output_path); + return -1; + } + + fprintf(out, "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n"); + fprintf(out, "<meta charset=\"UTF-8\">\n"); + fprintf(out, "<title>%s ISA Reference Manual</title>\n", + db->arch_name[0] ? db->arch_name : "Fyntv"); + fprintf(out, "<link rel=\"stylesheet\" href=\"style.css\">\n"); + fprintf(out, "</head>\n<body>\n<article>\n"); + + fprintf(out, "<header>\n"); + fprintf(out, "<h1>%s Instruction Set Architecture</h1>\n", + db->arch_name[0] ? db->arch_name : "Fyntv"); + fprintf(out, "<p class=\"subtitle\">Reference Manual</p>\n"); + fprintf(out, "<p><strong>Version:</strong> %s | <strong>Date:</strong> %s</p>\n", + db->arch_version[0] ? db->arch_version : "1.0.0", + db->arch_date[0] ? db->arch_date : "June 2026"); + fprintf(out, "</header>\n"); + + fprintf(out, "<section id=\"registers\">\n"); + fprintf(out, "<h2>Registers</h2>\n"); + fprintf(out, "<table><thead><tr><th>Register</th><th>ABI Name</th><th>Description</th><th>Saved</th></tr></thead><tbody>\n"); + for (int i = 0; i < db->num_registers; i++) { + fprintf(out, "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\n", + db->registers[i].name, + db->registers[i].abbr[0] ? db->registers[i].abbr : "—", + db->registers[i].desc[0] ? db->registers[i].desc : "—", + db->registers[i].preserve ? "Yes" : "No"); + } + fprintf(out, "</tbody></table>\n</section>\n"); + + fprintf(out, "<section id=\"instructions\">\n"); + fprintf(out, "<h2>Instruction Set</h2>\n"); + fprintf(out, "<table><thead><tr><th>Mnemonic</th><th>Format</th><th>Opcode</th><th>Operands</th><th>Description</th></tr></thead><tbody>\n"); + for (int i = 0; i < db->num_instructions; i++) { + fprintf(out, "<tr><td><code>%s</code></td><td>%s</td><td><code>0x%02X</code></td><td><code>%s</code></td><td>%s</td></tr>\n", + db->instructions[i].name, + db->instructions[i].fmt_name, + db->instructions[i].opcode, + db->instructions[i].operands[0] ? db->instructions[i].operands : "—", + db->instructions[i].desc); + } + fprintf(out, "</tbody></table>\n</section>\n"); + + fprintf(out, "</article>\n</body>\n</html>\n"); + fclose(out); + return 0; +} diff --git a/gen/docs/generator.h b/gen/docs/generator.h new file mode 100644 index 0000000..407b47a --- /dev/null +++ b/gen/docs/generator.h @@ -0,0 +1,9 @@ +#ifndef GENERATOR_H +#define GENERATOR_H + +#include "types.h" + +int gen_markdown(const IsaDb *db, const char *output_path); +int gen_html(const IsaDb *db, const char *output_path); + +#endif diff --git a/gen/docs/isa-docgen b/gen/docs/isa-docgen Binary files differnew file mode 100755 index 0000000..c330db7 --- /dev/null +++ b/gen/docs/isa-docgen diff --git a/gen/docs/isa_defs/arch.isa b/gen/docs/isa_defs/arch.isa new file mode 100644 index 0000000..f1added --- /dev/null +++ b/gen/docs/isa_defs/arch.isa @@ -0,0 +1,68 @@ +# Fyntv Architecture Definition +ARCH Fyntv + NAME Fyntv + VERSION 0.0.0.1 + DATE June 2026 + STATUS Draft +END + +# ============================================================================= +# Instruction Formats +# ============================================================================= + +FORMAT R + WIDTH 32 + FIELD opcode 6:0 + FIELD rd 11:7 + FIELD funct3 14:12 + FIELD rs1 19:15 + FIELD rs2 24:20 + FIELD funct7 31:25 +END + +FORMAT I + WIDTH 32 + FIELD opcode 6:0 + FIELD rd 11:7 + FIELD funct3 14:12 + FIELD rs1 19:15 + FIELD imm 31:20 +END + +FORMAT S + WIDTH 32 + FIELD opcode 6:0 + FIELD imm_lo 11:7 + FIELD funct3 14:12 + FIELD rs1 19:15 + FIELD rs2 24:20 + FIELD imm_hi 31:25 +END + +FORMAT B + WIDTH 32 + FIELD opcode 6:0 + FIELD imm_4_1 11:7 + FIELD funct3 14:12 + FIELD rs1 19:15 + FIELD rs2 24:20 + FIELD imm_10_5 30:25 + FIELD imm_12 31:31 +END + +FORMAT U + WIDTH 32 + FIELD opcode 6:0 + FIELD rd 11:7 + FIELD imm 31:12 +END + +FORMAT J + WIDTH 32 + FIELD opcode 6:0 + FIELD rd 11:7 + FIELD imm_19_12 19:12 + FIELD imm_11 20:20 + FIELD imm_10_1 30:21 + FIELD imm_20 31:31 +END diff --git a/gen/docs/isa_defs/csrs.isa b/gen/docs/isa_defs/csrs.isa new file mode 100644 index 0000000..e2098ef --- /dev/null +++ b/gen/docs/isa_defs/csrs.isa @@ -0,0 +1,191 @@ +# Fyntv Control and Status Register Definitions + +CSR mstatus + NUMBER 0x300 + DESC Machine status register — holds global interrupt enable and privilege state +END + +CSR misa + NUMBER 0x301 + DESC Machine ISA register — encodes supported ISA extensions +END + +CSR medeleg + NUMBER 0x302 + DESC Machine exception delegation register +END + +CSR mideleg + NUMBER 0x303 + DESC Machine interrupt delegation register +END + +CSR mie + NUMBER 0x304 + DESC Machine interrupt-enable register +END + +CSR mtvec + NUMBER 0x305 + DESC Machine trap-handler base address +END + +CSR mcounteren + NUMBER 0x306 + DESC Machine counter enable register +END + +CSR mscratch + NUMBER 0x340 + DESC Scratch register for machine-mode trap handlers +END + +CSR mepc + NUMBER 0x341 + DESC Machine exception program counter — holds PC of trapping instruction +END + +CSR mcause + NUMBER 0x342 + DESC Machine trap cause — encodes exception or interrupt cause +END + +CSR mtval + NUMBER 0x343 + DESC Machine trap value — exception-specific information +END + +CSR mip + NUMBER 0x344 + DESC Machine interrupt pending register +END + +CSR pmpcfg0 + NUMBER 0x3A0 + DESC Physical memory protection configuration 0 +END + +CSR pmpaddr0 + NUMBER 0x3B0 + DESC Physical memory protection address 0 +END + +CSR pmpaddr1 + NUMBER 0x3B1 + DESC Physical memory protection address 1 +END + +CSR pmpaddr2 + NUMBER 0x3B2 + DESC Physical memory protection address 2 +END + +CSR pmpaddr3 + NUMBER 0x3B3 + DESC Physical memory protection address 3 +END + +CSR mcycle + NUMBER 0xB00 + DESC Machine cycle counter — counts number of clock cycles +END + +CSR minstret + NUMBER 0xB02 + DESC Machine instructions-retired counter +END + +CSR mhpmcounter3 + NUMBER 0xB03 + DESC Machine hardware performance counter 3 +END + +CSR mhpmcounter4 + NUMBER 0xB04 + DESC Machine hardware performance counter 4 +END + +CSR mhpmcounter5 + NUMBER 0xB05 + DESC Machine hardware performance counter 5 +END + +CSR mhpmevent3 + NUMBER 0x323 + DESC Machine hardware performance event selector 3 +END + +CSR mhpmevent4 + NUMBER 0x324 + DESC Machine hardware performance event selector 4 +END + +CSR mhpmevent5 + NUMBER 0x325 + DESC Machine hardware performance event selector 5 +END + +CSR ucycle + NUMBER 0xC00 + DESC User-mode cycle counter read +END + +CSR uinstret + NUMBER 0xC02 + DESC User-mode instructions-retired read +END + +CSR ucycleh + NUMBER 0xC80 + DESC Upper 32 bits of user-mode cycle counter +END + +CSR uinstreth + NUMBER 0xC82 + DESC Upper 32 bits of user-mode instructions-retired counter +END + +CSR sstatus + NUMBER 0x100 + DESC Supervisor status register +END + +CSR sie + NUMBER 0x104 + DESC Supervisor interrupt-enable register +END + +CSR stvec + NUMBER 0x105 + DESC Supervisor trap-handler base address +END + +CSR sscratch + NUMBER 0x140 + DESC Scratch register for supervisor-mode trap handlers +END + +CSR sepc + NUMBER 0x141 + DESC Supervisor exception program counter +END + +CSR scause + NUMBER 0x142 + DESC Supervisor trap cause register +END + +CSR stval + NUMBER 0x143 + DESC Supervisor trap value register +END + +CSR sip + NUMBER 0x144 + DESC Supervisor interrupt pending register +END + +CSR satp + NUMBER 0x180 + DESC Supervisor address translation and protection — controls page tables +END diff --git a/gen/docs/isa_defs/instructions.isa b/gen/docs/isa_defs/instructions.isa new file mode 100644 index 0000000..c7255d3 --- /dev/null +++ b/gen/docs/isa_defs/instructions.isa @@ -0,0 +1,512 @@ +# Fyntv Core Integer Instructions + +INSTRUCTION ADD + FORMAT R + OPCODE 0x33 + FUNCT3 0x0 + FUNCT7 0x00 + OPERANDS rd, rs1, rs2 + DESC Add registers + NOTE rd = rs1 + rs2 + CATEGORY Integer +END + +INSTRUCTION SUB + FORMAT R + OPCODE 0x33 + FUNCT3 0x0 + FUNCT7 0x20 + OPERANDS rd, rs1, rs2 + DESC Subtract registers + NOTE rd = rs1 - rs2 + CATEGORY Integer +END + +INSTRUCTION ADDI + FORMAT I + OPCODE 0x13 + FUNCT3 0x0 + OPERANDS rd, rs1, imm12 + DESC Add sign-extended 12-bit immediate to register rs1 + NOTE rd = rs1 + sext(imm12) + CATEGORY Integer + IMM true +END + +INSTRUCTION SLT + FORMAT R + OPCODE 0x33 + FUNCT3 0x2 + FUNCT7 0x00 + OPERANDS rd, rs1, rs2 + DESC Set if rs1 is less than rs2 (signed) + NOTE rd = (rs1 < rs2) ? 1 : 0 + CATEGORY Integer +END + +INSTRUCTION SLTU + FORMAT R + OPCODE 0x33 + FUNCT3 0x3 + FUNCT7 0x00 + OPERANDS rd, rs1, rs2 + DESC Set if rs1 is less than rs2 (unsigned) + NOTE rd = (rs1 < rs2) ? 1 : 0 + CATEGORY Integer +END + +INSTRUCTION SLTI + FORMAT I + OPCODE 0x13 + FUNCT3 0x2 + OPERANDS rd, rs1, imm12 + DESC Set if rs1 is less than immediate (signed) + NOTE rd = (rs1 < sext(imm12)) ? 1 : 0 + CATEGORY Integer + IMM true +END + +INSTRUCTION SLTIU + FORMAT I + OPCODE 0x13 + FUNCT3 0x3 + OPERANDS rd, rs1, imm12 + DESC Set if rs1 is less than immediate (unsigned) + NOTE rd = (rs1 < sext(imm12)) ? 1 : 0 + CATEGORY Integer + IMM true +END + +INSTRUCTION LUI + FORMAT U + OPCODE 0x37 + OPERANDS rd, imm20 + DESC Load upper immediate — places 20-bit immediate in upper 20 bits of rd + NOTE rd = imm20 << 12 + CATEGORY Integer +END + +INSTRUCTION AUIPC + FORMAT U + OPCODE 0x17 + OPERANDS rd, imm20 + DESC Add upper immediate to PC — forms PC-relative address + NOTE rd = PC + (imm20 << 12) + CATEGORY Integer +END + +# ============================================================================= +# Logical Instructions +# ============================================================================= + +INSTRUCTION AND + FORMAT R + OPCODE 0x33 + FUNCT3 0x7 + FUNCT7 0x00 + OPERANDS rd, rs1, rs2 + DESC Bitwise AND + NOTE rd = rs1 & rs2 + CATEGORY Logical +END + +INSTRUCTION OR + FORMAT R + OPCODE 0x33 + FUNCT3 0x6 + FUNCT7 0x00 + OPERANDS rd, rs1, rs2 + DESC Bitwise OR + NOTE rd = rs1 | rs2 + CATEGORY Logical +END + +INSTRUCTION XOR + FORMAT R + OPCODE 0x33 + FUNCT3 0x4 + FUNCT7 0x00 + OPERANDS rd, rs1, rs2 + DESC Bitwise XOR + NOTE rd = rs1 ^ rs2 + CATEGORY Logical +END + +INSTRUCTION ANDI + FORMAT I + OPCODE 0x13 + FUNCT3 0x7 + OPERANDS rd, rs1, imm12 + DESC Bitwise AND with immediate + NOTE rd = rs1 & sext(imm12) + CATEGORY Logical + IMM true +END + +INSTRUCTION ORI + FORMAT I + OPCODE 0x13 + FUNCT3 0x6 + OPERANDS rd, rs1, imm12 + DESC Bitwise OR with immediate + NOTE rd = rs1 | sext(imm12) + CATEGORY Logical + IMM true +END + +INSTRUCTION XORI + FORMAT I + OPCODE 0x13 + FUNCT3 0x4 + OPERANDS rd, rs1, imm12 + DESC Bitwise XOR with immediate + NOTE rd = rs1 ^ sext(imm12) + CATEGORY Logical + IMM true +END + +# ============================================================================= +# Shift Instructions +# ============================================================================= + +INSTRUCTION SLL + FORMAT R + OPCODE 0x33 + FUNCT3 0x1 + FUNCT7 0x00 + OPERANDS rd, rs1, rs2 + DESC Logical left shift by lower 5 bits of rs2 + NOTE rd = rs1 << rs2[4:0] + CATEGORY Shift +END + +INSTRUCTION SRL + FORMAT R + OPCODE 0x33 + FUNCT3 0x5 + FUNCT7 0x00 + OPERANDS rd, rs1, rs2 + DESC Logical right shift by lower 5 bits of rs2 + NOTE rd = rs1 >> rs2[4:0] + CATEGORY Shift +END + +INSTRUCTION SRA + FORMAT R + OPCODE 0x33 + FUNCT3 0x5 + FUNCT7 0x20 + OPERANDS rd, rs1, rs2 + DESC Arithmetic right shift by lower 5 bits of rs2 + NOTE rd = rs1 >>> rs2[4:0] + CATEGORY Shift +END + +INSTRUCTION SLLI + FORMAT I + OPCODE 0x13 + FUNCT3 0x1 + OPERANDS rd, rs1, shamt5 + DESC Logical left shift by immediate shift amount + NOTE rd = rs1 << shamt + CATEGORY Shift +END + +INSTRUCTION SRLI + FORMAT I + OPCODE 0x13 + FUNCT3 0x5 + OPERANDS rd, rs1, shamt5 + DESC Logical right shift by immediate shift amount + NOTE rd = rs1 >> shamt + CATEGORY Shift +END + +INSTRUCTION SRAI + FORMAT I + OPCODE 0x13 + FUNCT3 0x5 + OPERANDS rd, rs1, shamt5 + DESC Arithmetic right shift by immediate shift amount + NOTE rd = rs1 >>> shamt + CATEGORY Shift +END + +# ============================================================================= +# Memory Instructions +# ============================================================================= + +INSTRUCTION LB + FORMAT I + OPCODE 0x03 + FUNCT3 0x0 + OPERANDS rd, offset(rs1) + DESC Load byte (sign-extended) + NOTE rd = sext(MEM[rs1 + offset][7:0]) + CATEGORY Memory +END + +INSTRUCTION LH + FORMAT I + OPCODE 0x03 + FUNCT3 0x1 + OPERANDS rd, offset(rs1) + DESC Load halfword (sign-extended) + NOTE rd = sext(MEM[rs1 + offset][15:0]) + CATEGORY Memory +END + +INSTRUCTION LW + FORMAT I + OPCODE 0x03 + FUNCT3 0x2 + OPERANDS rd, offset(rs1) + DESC Load word + NOTE rd = MEM[rs1 + offset][31:0] + CATEGORY Memory +END + +INSTRUCTION LBU + FORMAT I + OPCODE 0x03 + FUNCT3 0x4 + OPERANDS rd, offset(rs1) + DESC Load byte (zero-extended) + NOTE rd = MEM[rs1 + offset][7:0] + CATEGORY Memory +END + +INSTRUCTION LHU + FORMAT I + OPCODE 0x03 + FUNCT3 0x5 + OPERANDS rd, offset(rs1) + DESC Load halfword (zero-extended) + NOTE rd = MEM[rs1 + offset][15:0] + CATEGORY Memory +END + +INSTRUCTION SB + FORMAT S + OPCODE 0x23 + FUNCT3 0x0 + OPERANDS rs2, offset(rs1) + DESC Store byte + NOTE MEM[rs1 + offset][7:0] = rs2[7:0] + CATEGORY Memory +END + +INSTRUCTION SH + FORMAT S + OPCODE 0x23 + FUNCT3 0x1 + OPERANDS rs2, offset(rs1) + DESC Store halfword + NOTE MEM[rs1 + offset][15:0] = rs2[15:0] + CATEGORY Memory +END + +INSTRUCTION SW + FORMAT S + OPCODE 0x23 + FUNCT3 0x2 + OPERANDS rs2, offset(rs1) + DESC Store word + NOTE MEM[rs1 + offset][31:0] = rs2[31:0] + CATEGORY Memory +END + +# ============================================================================= +# Branch Instructions +# ============================================================================= + +INSTRUCTION BEQ + FORMAT B + OPCODE 0x63 + FUNCT3 0x0 + OPERANDS rs1, rs2, label + DESC Branch equal + NOTE if (rs1 == rs2) PC += sext(offset) + CATEGORY Branch +END + +INSTRUCTION BNE + FORMAT B + OPCODE 0x63 + FUNCT3 0x1 + OPERANDS rs1, rs2, label + DESC Branch not equal + NOTE if (rs1 != rs2) PC += sext(offset) + CATEGORY Branch +END + +INSTRUCTION BLT + FORMAT B + OPCODE 0x63 + FUNCT3 0x4 + OPERANDS rs1, rs2, label + DESC Branch less than (signed) + NOTE if (rs1 < rs2) PC += sext(offset) + CATEGORY Branch +END + +INSTRUCTION BGE + FORMAT B + OPCODE 0x63 + FUNCT3 0x5 + OPERANDS rs1, rs2, label + DESC Branch greater than or equal (signed) + NOTE if (rs1 >= rs2) PC += sext(offset) + CATEGORY Branch +END + +INSTRUCTION BLTU + FORMAT B + OPCODE 0x63 + FUNCT3 0x6 + OPERANDS rs1, rs2, label + DESC Branch less than (unsigned) + NOTE if (rs1 < rs2) PC += sext(offset) + CATEGORY Branch +END + +INSTRUCTION BGEU + FORMAT B + OPCODE 0x63 + FUNCT3 0x7 + OPERANDS rs1, rs2, label + DESC Branch greater than or equal (unsigned) + NOTE if (rs1 >= rs2) PC += sext(offset) + CATEGORY Branch +END + +# ============================================================================= +# Jump Instructions +# ============================================================================= + +INSTRUCTION JAL + FORMAT J + OPCODE 0x6F + OPERANDS rd, label + DESC Jump and link — jump to PC+offset, save return address to rd + NOTE rd = PC + 4; PC += sext(offset) + CATEGORY Jump +END + +INSTRUCTION JALR + FORMAT I + OPCODE 0x67 + FUNCT3 0x0 + OPERANDS rd, rs1, offset + DESC Jump and link register — jump to rs1+offset, save return address + NOTE rd = PC + 4; PC = (rs1 + sext(offset)) & ~1 + CATEGORY Jump +END + +# ============================================================================= +# Synchronization Instructions +# ============================================================================= + +INSTRUCTION FENCE + FORMAT I + OPCODE 0x0F + FUNCT3 0x0 + OPERANDS pred, succ + DESC Memory ordering fence + NOTE Orders memory accesses as specified by pred and succ fields + CATEGORY Synchronization +END + +INSTRUCTION FENCEI + FORMAT I + OPCODE 0x0F + FUNCT3 0x1 + OPERANDS — + DESC Instruction fence — synchronizes instruction and data streams + NOTE Flushes instruction cache after data writes + CATEGORY Synchronization +END + +# ============================================================================= +# System Instructions +# ============================================================================= + +INSTRUCTION ECALL + FORMAT I + OPCODE 0x73 + FUNCT3 0x0 + OPERANDS — + DESC Environment call — raises a system call exception + NOTE Traps to the configured exception handler + CATEGORY System +END + +INSTRUCTION EBREAK + FORMAT I + OPCODE 0x73 + FUNCT3 0x0 + OPERANDS — + DESC Breakpoint — raises a breakpoint exception + NOTE Used by debuggers to halt program execution + CATEGORY System +END + +INSTRUCTION CSRRW + FORMAT I + OPCODE 0x73 + FUNCT3 0x1 + OPERANDS rd, csr, rs1 + DESC Atomic read/write CSR — writes rs1 to CSR, returns old value in rd + NOTE rd = CSR[csr]; CSR[csr] = rs1 + CATEGORY System +END + +INSTRUCTION CSRRS + FORMAT I + OPCODE 0x73 + FUNCT3 0x2 + OPERANDS rd, csr, rs1 + DESC Atomic read and set bits in CSR + NOTE rd = CSR[csr]; CSR[csr] |= rs1 + CATEGORY System +END + +INSTRUCTION CSRRC + FORMAT I + OPCODE 0x73 + FUNCT3 0x3 + OPERANDS rd, csr, rs1 + DESC Atomic read and clear bits in CSR + NOTE rd = CSR[csr]; CSR[csr] &= ~rs1 + CATEGORY System +END + +INSTRUCTION CSRRWI + FORMAT I + OPCODE 0x73 + FUNCT3 0x5 + OPERANDS rd, csr, uimm5 + DESC Atomic read/write CSR with immediate + NOTE rd = CSR[csr]; CSR[csr] = zimm + CATEGORY System +END + +INSTRUCTION CSRRSI + FORMAT I + OPCODE 0x73 + FUNCT3 0x6 + OPERANDS rd, csr, uimm5 + DESC Atomic read and set bits in CSR with immediate + NOTE rd = CSR[csr]; CSR[csr] |= zimm + CATEGORY System +END + +INSTRUCTION CSRRCI + FORMAT I + OPCODE 0x73 + FUNCT3 0x7 + OPERANDS rd, csr, uimm5 + DESC Atomic read and clear bits in CSR with immediate + NOTE rd = CSR[csr]; CSR[csr] &= ~zimm + CATEGORY System +END diff --git a/gen/docs/isa_defs/registers.isa b/gen/docs/isa_defs/registers.isa new file mode 100644 index 0000000..baa8124 --- /dev/null +++ b/gen/docs/isa_defs/registers.isa @@ -0,0 +1,289 @@ +# Fyntv Register Definitions + +REGISTER x0 + ABBR zero + DESC Always-zero register — reads return 0, writes are discarded + PRESERVE false + CALLER false + ARG false + INDEX 0 +END + +REGISTER x1 + ABBR ra + DESC Return address link register + PRESERVE false + CALLER false + ARG false + INDEX 1 +END + +REGISTER x2 + ABBR sp + DESC Stack pointer + PRESERVE true + CALLER false + ARG false + INDEX 2 +END + +REGISTER x3 + ABBR gp + DESC Global pointer + PRESERVE true + CALLER false + ARG false + INDEX 3 +END + +REGISTER x4 + ABBR tp + DESC Thread pointer + PRESERVE true + CALLER false + ARG false + INDEX 4 +END + +REGISTER x5 + ABBR t0 + DESC Temporary register 0 + PRESERVE false + CALLER true + ARG false + INDEX 5 +END + +REGISTER x6 + ABBR t1 + DESC Temporary register 1 + PRESERVE false + CALLER true + ARG false + INDEX 6 +END + +REGISTER x7 + ABBR t2 + DESC Temporary register 2 + PRESERVE false + CALLER true + ARG false + INDEX 7 +END + +REGISTER x8 + ABBR s0 + DESC Saved register 0 / frame pointer + PRESERVE true + CALLER false + ARG false + INDEX 8 +END + +REGISTER x9 + ABBR s1 + DESC Saved register 1 + PRESERVE true + CALLER false + ARG false + INDEX 9 +END + +REGISTER x10 + ABBR a0 + DESC Function argument 0 / return value 0 + PRESERVE false + CALLER true + ARG true + INDEX 10 +END + +REGISTER x11 + ABBR a1 + DESC Function argument 1 / return value 1 + PRESERVE false + CALLER true + ARG true + INDEX 11 +END + +REGISTER x12 + ABBR a2 + DESC Function argument 2 + PRESERVE false + CALLER true + ARG true + INDEX 12 +END + +REGISTER x13 + ABBR a3 + DESC Function argument 3 + PRESERVE false + CALLER true + ARG true + INDEX 13 +END + +REGISTER x14 + ABBR a4 + DESC Function argument 4 + PRESERVE false + CALLER true + ARG true + INDEX 14 +END + +REGISTER x15 + ABBR a5 + DESC Function argument 5 + PRESERVE false + CALLER true + ARG true + INDEX 15 +END + +REGISTER x16 + ABBR a6 + DESC Function argument 6 + PRESERVE false + CALLER true + ARG true + INDEX 16 +END + +REGISTER x17 + ABBR a7 + DESC Function argument 7 + PRESERVE false + CALLER true + ARG true + INDEX 17 +END + +REGISTER x18 + ABBR s2 + DESC Saved register 2 + PRESERVE true + CALLER false + ARG false + INDEX 18 +END + +REGISTER x19 + ABBR s3 + DESC Saved register 3 + PRESERVE true + CALLER false + ARG false + INDEX 19 +END + +REGISTER x20 + ABBR s4 + DESC Saved register 4 + PRESERVE true + CALLER false + ARG false + INDEX 20 +END + +REGISTER x21 + ABBR s5 + DESC Saved register 5 + PRESERVE true + CALLER false + ARG false + INDEX 21 +END + +REGISTER x22 + ABBR s6 + DESC Saved register 6 + PRESERVE true + CALLER false + ARG false + INDEX 22 +END + +REGISTER x23 + ABBR s7 + DESC Saved register 7 + PRESERVE true + CALLER false + ARG false + INDEX 23 +END + +REGISTER x24 + ABBR s8 + DESC Saved register 8 + PRESERVE true + CALLER false + ARG false + INDEX 24 +END + +REGISTER x25 + ABBR s9 + DESC Saved register 9 + PRESERVE true + CALLER false + ARG false + INDEX 25 +END + +REGISTER x26 + ABBR s10 + DESC Saved register 10 + PRESERVE true + CALLER false + ARG false + INDEX 26 +END + +REGISTER x27 + ABBR s11 + DESC Saved register 11 + PRESERVE true + CALLER false + ARG false + INDEX 27 +END + +REGISTER x28 + ABBR t3 + DESC Temporary register 3 + PRESERVE false + CALLER true + ARG false + INDEX 28 +END + +REGISTER x29 + ABBR t4 + DESC Temporary register 4 + PRESERVE false + CALLER true + ARG false + INDEX 29 +END + +REGISTER x30 + ABBR t5 + DESC Temporary register 5 + PRESERVE false + CALLER true + ARG false + INDEX 30 +END + +REGISTER x31 + ABBR t6 + DESC Temporary register 6 + PRESERVE false + CALLER true + ARG false + INDEX 31 +END diff --git a/gen/docs/main.c b/gen/docs/main.c new file mode 100644 index 0000000..760253e --- /dev/null +++ b/gen/docs/main.c @@ -0,0 +1,83 @@ +#include "parser.h" +#include "generator.h" +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +static void print_usage(const char *prog) { + fprintf(stderr, "Usage: %s [options] <isa_defs_dir>\n\n", prog); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -o <file> Output Markdown file (default: doc/spec/isa_reference.md)\n"); + fprintf(stderr, " --html Also generate HTML output\n"); + fprintf(stderr, " --help Show this help\n\n"); + fprintf(stderr, "Generates professional ISA reference documentation from .isa definition files.\n"); +} + +int main(int argc, char **argv) { + const char *defs_dir = NULL; + const char *output = "doc/spec/isa_reference.md"; + int gen_html_flag = 0; + + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--help") == 0) { + print_usage(argv[0]); + return 0; + } else if (strcmp(argv[i], "-o") == 0 && i + 1 < argc) { + output = argv[++i]; + } else if (strcmp(argv[i], "--html") == 0) { + gen_html_flag = 1; + } else if (argv[i][0] != '-') { + defs_dir = argv[i]; + } else { + fprintf(stderr, "Unknown option: %s\n", argv[i]); + print_usage(argv[0]); + return 1; + } + } + + if (!defs_dir) { + fprintf(stderr, "Error: no ISA definitions directory specified.\n\n"); + print_usage(argv[0]); + return 1; + } + + IsaDb db; + memset(&db, 0, sizeof(db)); + + printf("ISA Documentation Generator\n"); + printf("Reading definitions from: %s\n", defs_dir); + + if (isa_parse_dir(defs_dir, &db) != 0) { + fprintf(stderr, "Error: failed to parse ISA definitions.\n"); + return 1; + } + + printf("\nParsed:\n"); + printf(" %d format(s)\n", db.num_formats); + printf(" %d register(s)\n", db.num_registers); + printf(" %d instruction(s)\n", db.num_instructions); + printf(" %d CSR(s)\n", db.num_csrs); + + if (db.num_instructions == 0) { + fprintf(stderr, "Warning: no instructions loaded. Output will be sparse.\n"); + } + + if (gen_markdown(&db, output) != 0) { + fprintf(stderr, "Error: failed to generate Markdown.\n"); + return 1; + } + + if (gen_html_flag) { + char html_path[1024]; + snprintf(html_path, sizeof(html_path), "%s.html", output); + const char *ext = strrchr(output, '.'); + if (ext) { + size_t len = ext - output; + snprintf(html_path, sizeof(html_path), "%.*s.html", (int)len, output); + } + gen_html(&db, html_path); + } + + printf("\nDone. Output: %s\n", output); + return 0; +} diff --git a/gen/docs/parser.c b/gen/docs/parser.c new file mode 100644 index 0000000..2852070 --- /dev/null +++ b/gen/docs/parser.c @@ -0,0 +1,256 @@ +#include "parser.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <dirent.h> + +static void str_trim(char *s) { + char *e; + while (isspace((unsigned char)*s)) s++; + if (*s == 0) return; + e = s + strlen(s) - 1; + while (e > s && isspace((unsigned char)*e)) e--; + *(e + 1) = '\0'; +} + +static int str_split(const char *line, char **key, char **val) { + static char buf[MAX_LINE]; + strncpy(buf, line, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; + char *eq = strchr(buf, ' '); + if (!eq) { *key = buf; *val = ""; return 0; } + *eq = '\0'; + *key = buf; + *val = eq + 1; + str_trim(*key); + str_trim(*val); + return 1; +} + +static int str_to_int(const char *s) { + if (strncmp(s, "0x", 2) == 0 || strncmp(s, "0X", 2) == 0) + return (int)strtol(s, NULL, 16); + return atoi(s); +} + +static int str_to_bool(const char *s) { + return strcmp(s, "true") == 0 || strcmp(s, "yes") == 0 || strcmp(s, "1") == 0; +} + +static int parse_block(FILE *f, const char *block_type, const char *block_name, IsaDb *db) { + char line[MAX_LINE]; + + if (strcasecmp(block_type, "FORMAT") == 0) { + if (db->num_formats >= MAX_FMTS) { + fprintf(stderr, "Too many formats (max %d)\n", MAX_FMTS); + return -1; + } + IsaFormat *fmt = &db->formats[db->num_formats]; + strncpy(fmt->name, block_name, MAX_NAME - 1); + fmt->width = 32; + fmt->num_fields = 0; + + while (fgets(line, sizeof(line), f)) { + char *trimmed = line; + while (isspace((unsigned char)*trimmed)) trimmed++; + if (*trimmed == '#' || *trimmed == '\n') continue; + if (strncmp(trimmed, "END", 3) == 0) break; + + char *key, *val; + str_split(trimmed, &key, &val); + + if (strcasecmp(key, "WIDTH") == 0) { + fmt->width = str_to_int(val); + } else if (strcasecmp(key, "FIELD") == 0) { + if (fmt->num_fields >= MAX_FIELDS) { + fprintf(stderr, "Too many fields in format %s\n", fmt->name); + continue; + } + char fname[64]; + int high, low; + if (sscanf(val, "%63s %d:%d", fname, &high, &low) >= 3) { + strncpy(fmt->fields[fmt->num_fields].name, fname, MAX_LABEL - 1); + fmt->fields[fmt->num_fields].high = high; + fmt->fields[fmt->num_fields].low = low; + fmt->num_fields++; + } + } + } + db->num_formats++; + } else if (strcasecmp(block_type, "REGISTER") == 0) { + if (db->num_registers >= MAX_REGS) { + fprintf(stderr, "Too many registers (max %d)\n", MAX_REGS); + return -1; + } + IsaRegister *reg = &db->registers[db->num_registers]; + memset(reg, 0, sizeof(*reg)); + strncpy(reg->name, block_name, MAX_NAME - 1); + reg->preserve = -1; + + while (fgets(line, sizeof(line), f)) { + char *trimmed = line; + while (isspace((unsigned char)*trimmed)) trimmed++; + if (*trimmed == '#' || *trimmed == '\n') continue; + if (strncmp(trimmed, "END", 3) == 0) break; + + char *key, *val; + str_split(trimmed, &key, &val); + + if (strcasecmp(key, "ABBR") == 0) + strncpy(reg->abbr, val, MAX_LABEL - 1); + else if (strcasecmp(key, "DESC") == 0) + strncpy(reg->desc, val, MAX_DESC - 1); + else if (strcasecmp(key, "PRESERVE") == 0) + reg->preserve = str_to_bool(val); + else if (strcasecmp(key, "CALLER") == 0) + reg->caller_saved = str_to_bool(val); + else if (strcasecmp(key, "ARG") == 0) + reg->arg_reg = str_to_bool(val); + else if (strcasecmp(key, "INDEX") == 0) + reg->index = str_to_int(val); + } + if (reg->preserve == -1) reg->preserve = 0; + db->num_registers++; + } else if (strcasecmp(block_type, "INSTRUCTION") == 0) { + if (db->num_instructions >= MAX_INSTS) { + fprintf(stderr, "Too many instructions (max %d)\n", MAX_INSTS); + return -1; + } + IsaInstruction *inst = &db->instructions[db->num_instructions]; + memset(inst, 0, sizeof(*inst)); + strncpy(inst->name, block_name, MAX_NAME - 1); + inst->funct3_valid = 0; + inst->funct7_valid = 0; + + while (fgets(line, sizeof(line), f)) { + char *trimmed = line; + while (isspace((unsigned char)*trimmed)) trimmed++; + if (*trimmed == '#' || *trimmed == '\n') continue; + if (strncmp(trimmed, "END", 3) == 0) break; + + char *key, *val; + str_split(trimmed, &key, &val); + + if (strcasecmp(key, "FORMAT") == 0) + strncpy(inst->fmt_name, val, MAX_NAME - 1); + else if (strcasecmp(key, "OPCODE") == 0) + inst->opcode = (uint32_t)str_to_int(val); + else if (strcasecmp(key, "FUNCT3") == 0) { + inst->funct3 = (uint32_t)str_to_int(val); + inst->funct3_valid = 1; + } else if (strcasecmp(key, "FUNCT7") == 0) { + inst->funct7 = (uint32_t)str_to_int(val); + inst->funct7_valid = 1; + } else if (strcasecmp(key, "OPERANDS") == 0) + strncpy(inst->operands, val, MAX_OPERANDS - 1); + else if (strcasecmp(key, "DESC") == 0) + strncpy(inst->desc, val, MAX_DESC - 1); + else if (strcasecmp(key, "NOTE") == 0) + strncpy(inst->note, val, MAX_NOTE - 1); + else if (strcasecmp(key, "CATEGORY") == 0) + strncpy(inst->category, val, MAX_LABEL - 1); + else if (strcasecmp(key, "IMM") == 0) + inst->has_imm = str_to_bool(val); + } + db->num_instructions++; + } else if (strcasecmp(block_type, "CSR") == 0) { + if (db->num_csrs >= MAX_CSRS) { + fprintf(stderr, "Too many CSRs (max %d)\n", MAX_CSRS); + return -1; + } + IsaCsr *csr = &db->csrs[db->num_csrs]; + memset(csr, 0, sizeof(*csr)); + strncpy(csr->name, block_name, MAX_NAME - 1); + + while (fgets(line, sizeof(line), f)) { + char *trimmed = line; + while (isspace((unsigned char)*trimmed)) trimmed++; + if (*trimmed == '#' || *trimmed == '\n') continue; + if (strncmp(trimmed, "END", 3) == 0) break; + + char *key, *val; + str_split(trimmed, &key, &val); + + if (strcasecmp(key, "NUMBER") == 0) + csr->number = str_to_int(val); + else if (strcasecmp(key, "DESC") == 0) + strncpy(csr->desc, val, MAX_DESC - 1); + } + db->num_csrs++; + } else if (strcasecmp(block_type, "ARCH") == 0) { + while (fgets(line, sizeof(line), f)) { + char *trimmed = line; + while (isspace((unsigned char)*trimmed)) trimmed++; + if (*trimmed == '#' || *trimmed == '\n') continue; + if (strncmp(trimmed, "END", 3) == 0) break; + + char *key, *val; + str_split(trimmed, &key, &val); + + if (strcasecmp(key, "NAME") == 0) + strncpy(db->arch_name, val, MAX_NAME - 1); + else if (strcasecmp(key, "VERSION") == 0) + strncpy(db->arch_version, val, MAX_LABEL - 1); + else if (strcasecmp(key, "DATE") == 0) + strncpy(db->arch_date, val, MAX_LABEL - 1); + else if (strcasecmp(key, "STATUS") == 0) + strncpy(db->arch_status, val, MAX_LABEL - 1); + } + } + + return 0; +} + +int isa_parse_file(const char *path, IsaDb *db) { + FILE *f = fopen(path, "r"); + if (!f) { + fprintf(stderr, "Error: cannot open %s\n", path); + return -1; + } + + char line[MAX_LINE]; + int line_num = 0; + + while (fgets(line, sizeof(line), f)) { + line_num++; + char *trimmed = line; + while (isspace((unsigned char)*trimmed)) trimmed++; + if (*trimmed == '#' || *trimmed == '\n' || *trimmed == '\r') continue; + + char type[MAX_LABEL], name[MAX_NAME]; + if (sscanf(trimmed, "%31s %63s", type, name) >= 2) { + if (parse_block(f, type, name, db) != 0) { + fprintf(stderr, "Error parsing %s in %s line %d\n", type, path, line_num); + fclose(f); + return -1; + } + } + } + + fclose(f); + return 0; +} + +int isa_parse_dir(const char *dir, IsaDb *db) { + DIR *d = opendir(dir); + if (!d) { + fprintf(stderr, "Error: cannot open directory %s\n", dir); + return -1; + } + + struct dirent *entry; + while ((entry = readdir(d)) != NULL) { + if (entry->d_type != DT_REG) continue; + const char *ext = strrchr(entry->d_name, '.'); + if (!ext || strcasecmp(ext, ".isa") != 0) continue; + + char path[MAX_PATH]; + snprintf(path, sizeof(path), "%s/%s", dir, entry->d_name); + printf(" Parsing: %s\n", path); + isa_parse_file(path, db); + } + + closedir(d); + return 0; +} diff --git a/gen/docs/parser.h b/gen/docs/parser.h new file mode 100644 index 0000000..53a27fc --- /dev/null +++ b/gen/docs/parser.h @@ -0,0 +1,9 @@ +#ifndef PARSER_H +#define PARSER_H + +#include "types.h" + +int isa_parse_file(const char *path, IsaDb *db); +int isa_parse_dir(const char *dir, IsaDb *db); + +#endif diff --git a/gen/docs/style.css b/gen/docs/style.css new file mode 100644 index 0000000..305b00b --- /dev/null +++ b/gen/docs/style.css @@ -0,0 +1,139 @@ +/* style.css — Professional styling for ISA reference HTML (used by wkhtmltopdf) */ + +body { + font-family: "Times New Roman", Times, serif; + font-size: 11pt; + line-height: 1.5; + color: #1a1a1a; + max-width: 7in; + margin: 0 auto; + padding: 1in 0.5in; +} + +h1 { + font-size: 24pt; + text-align: center; + margin-top: 2in; + margin-bottom: 0.3in; + page-break-before: always; +} + +h1:first-of-type { + margin-top: 3in; + page-break-before: avoid; +} + +.subtitle { + text-align: center; + font-size: 16pt; + color: #555; + margin-bottom: 0.5in; +} + +h2 { + font-size: 16pt; + border-bottom: 2px solid #333; + padding-bottom: 4pt; + margin-top: 0.5in; +} + +h3 { + font-size: 13pt; + margin-top: 0.3in; +} + +h4 { + font-size: 11pt; + font-style: italic; + margin-top: 0.2in; +} + +table { + width: 100%; + border-collapse: collapse; + margin: 0.2in 0; + font-size: 9.5pt; +} + +th, td { + border: 1px solid #999; + padding: 4pt 6pt; + text-align: left; + vertical-align: top; +} + +th { + background-color: #e8e8e8; + font-weight: bold; +} + +tr:nth-child(even) { + background-color: #f6f6f6; +} + +code { + font-family: "Courier New", Courier, monospace; + font-size: 9.5pt; + background-color: #f0f0f0; + padding: 1pt 3pt; + border-radius: 2pt; +} + +pre { + font-family: "Courier New", Courier, monospace; + font-size: 9pt; + background-color: #f8f8f8; + border: 1px solid #ddd; + padding: 8pt; + overflow-x: auto; + line-height: 1.3; +} + +pre code { + background: none; + padding: 0; +} + +/* Page breaks */ +section, .chapter { + page-break-before: always; +} + +/* Tables of contents */ +#TOC { + page-break-after: always; +} + +#TOC ul { + list-style: none; + padding-left: 0; +} + +#TOC li { + margin: 4pt 0; +} + +/* Header and footer */ +@page { + @top-center { + content: element(header); + font-size: 9pt; + color: #888; + } + @bottom-center { + content: counter(page); + font-size: 9pt; + } +} + +@media print { + body { + padding: 0; + } + table { + page-break-inside: avoid; + } + h2, h3 { + page-break-after: avoid; + } +} diff --git a/gen/docs/types.h b/gen/docs/types.h new file mode 100644 index 0000000..14fb4e0 --- /dev/null +++ b/gen/docs/types.h @@ -0,0 +1,78 @@ +#ifndef TYPES_H +#define TYPES_H + +#include <stdint.h> +#include <stddef.h> + +#define MAX_NAME 64 +#define MAX_LABEL 32 +#define MAX_DESC 512 +#define MAX_NOTE 512 +#define MAX_OPERANDS 128 +#define MAX_FIELDS 8 +#define MAX_INSTS 256 +#define MAX_REGS 64 +#define MAX_FMTS 16 +#define MAX_CSRS 128 +#define MAX_LINE 1024 +#define MAX_TOKENS 16 +#define MAX_PATH 256 + +typedef struct { + char name[MAX_NAME]; + int width; + int num_fields; + struct { + char name[MAX_LABEL]; + int high; + int low; + } fields[MAX_FIELDS]; +} IsaFormat; + +typedef struct { + char name[MAX_NAME]; + char abbr[MAX_LABEL]; + char desc[MAX_DESC]; + int preserve; + int caller_saved; + int arg_reg; + int index; +} IsaRegister; + +typedef struct { + char name[MAX_NAME]; + char fmt_name[MAX_NAME]; + uint32_t opcode; + uint32_t funct3; + uint32_t funct7; + uint32_t funct3_valid; + uint32_t funct7_valid; + char operands[MAX_OPERANDS]; + char desc[MAX_DESC]; + char note[MAX_NOTE]; + char category[MAX_LABEL]; + int has_imm; +} IsaInstruction; + +typedef struct { + char name[MAX_NAME]; + int number; + char desc[MAX_DESC]; +} IsaCsr; + +typedef struct { + IsaFormat formats[MAX_FMTS]; + int num_formats; + IsaRegister registers[MAX_REGS]; + int num_registers; + IsaInstruction instructions[MAX_INSTS]; + int num_instructions; + IsaCsr csrs[MAX_CSRS]; + int num_csrs; + char arch_name[MAX_NAME]; + char arch_version[MAX_LABEL]; + char arch_date[MAX_LABEL]; + char arch_status[MAX_LABEL]; +} IsaDb; + +#endif diff --git a/gen/encoder/main.c b/gen/encoder/main.c new file mode 100644 index 0000000..ac3fed5 --- /dev/null +++ b/gen/encoder/main.c @@ -0,0 +1,3 @@ +// encoder — Generate assembler encoder source +// Reads: opcode definitions from ../isa/opcodes/ +// Writes: encode.c / encode.h mapping mnemonics→instruction words diff --git a/gen/opcodes/main.c b/gen/opcodes/main.c new file mode 100644 index 0000000..f9819fd --- /dev/null +++ b/gen/opcodes/main.c @@ -0,0 +1,3 @@ +// opcodes — Generate opcode header files +// Reads: ../isa/opcodes/*.yaml +// Writes: opcodes.h (enum, bitfield structs, encode/decode macros) diff --git a/gen/tablegen/main.c b/gen/tablegen/main.c new file mode 100644 index 0000000..ef91ae2 --- /dev/null +++ b/gen/tablegen/main.c @@ -0,0 +1,3 @@ +// tablegen — Generate instruction decode tables from ISA encoding definitions +// Reads: ../isa/encoding/*.yaml +// Writes: decoder tables (switch/case, bit-mask dispatch) diff --git a/isa/README.md b/isa/README.md new file mode 100644 index 0000000..bf855eb --- /dev/null +++ b/isa/README.md @@ -0,0 +1,12 @@ +# ISA Definition + +Formal, machine-readable specification of the instruction set. + +## Structure + +- `encoding/` — Instruction encoding bitfield definitions (JSON/YAML tables) +- `opcodes/` — Opcode mnemonics, values, operand types +- `extensions/` — Optional ISA extensions (e.g. float, vector, crypto) +- `privileged/` — Privileged architecture: CSRs, traps, memory management + +This directory is the **source of truth** consumed by the generators in `gen/`. diff --git a/isa/encoding/base.yaml b/isa/encoding/base.yaml new file mode 100644 index 0000000..0b4654a --- /dev/null +++ b/isa/encoding/base.yaml @@ -0,0 +1,3 @@ +# Base instruction encoding format +# Instruction width: 32-bit fixed / 16+32 variable +# Format fields: opcode, rd, funct3, rs1, rs2, funct7, imm diff --git a/isa/opcodes/base.yaml b/isa/opcodes/base.yaml new file mode 100644 index 0000000..289bc46 --- /dev/null +++ b/isa/opcodes/base.yaml @@ -0,0 +1,8 @@ +# Base instruction opcode definitions +# Format: mnemonic, encoding, operands, type, description +# e.g.: +# - mnemonic: ADD +# encoding: 0b0000000_?????_?????_000_?????_0110011 +# operands: [rd, rs1, rs2] +# type: R +# description: Add registers diff --git a/runtime/README.md b/runtime/README.md new file mode 100644 index 0000000..8dad172 --- /dev/null +++ b/runtime/README.md @@ -0,0 +1,6 @@ +# Runtime Support + +- `crt0.S` — C runtime startup (reset handler, .bss clear, call main) +- `crti.S` — C runtime init (constructor init) +- `crtn.S` — C runtime fini (constructor fini) +- `linker.ld` — Default linker script diff --git a/sim/README.md b/sim/README.md new file mode 100644 index 0000000..45fc547 --- /dev/null +++ b/sim/README.md @@ -0,0 +1,8 @@ +# Instruction-Set Simulator + +Functional (and optionally cycle-approximate) simulator for Fyntv. + +- Reads ELF binaries +- Executes instructions via decode-dispatch loop +- Provides GDB remote debugging stub +- Tracks instruction counts, cycle estimates diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..5046211 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,8 @@ +# Test Suites + +| Directory | Contents | +|------------------|-------------------------------------------| +| `asm/` | Instruction-level assembly tests | +| `sim/` | Simulator compliance tests | +| `regression/` | Regression test suite (one test per bug) | +| `benchmarks/` | Performance benchmarks (Dhrystone, etc.) | diff --git a/tools/README.md b/tools/README.md new file mode 100644 index 0000000..a3aae90 --- /dev/null +++ b/tools/README.md @@ -0,0 +1,17 @@ +# Toolchain (binutils-style) + +Standard GNU binutils ports for Fyntv: + +| Tool | Purpose | +|-------------|--------------------------------------| +| `gas/` | Assembler | +| `ld/` | Linker | +| `objdump/` | Disassembler + object dump | +| `objcopy/` | Section copying / stripping | +| `readelf/` | ELF file reader | +| `nm/` | Symbol listing | +| `ar/` | Static library archiver | +| `size/` | Section/symbol size listing | +| `strings/` | Printable string extraction | +| `addr2line/`| Virtual address → source line | +| `cxxfilt/` | C++ ABI name demangling | |
