diff options
| author | Gabriel Schneider <[email protected]> | 2026-08-25 15:42:03 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-25 16:17:41 -0300 |
| commit | 9ab0cbb93dd7678dcc8e86e164325e7d1b8ea7c6 (patch) | |
| tree | 21c382587835328927c505b3ea3c2b30649baa5f /cpu-docs/espressif-custom-isa | |
| parent | 894524a2e10296d6db4e82e0fe98b1802910ab77 (diff) | |
| download | esp32p4-9ab0cbb93dd7678dcc8e86e164325e7d1b8ea7c6.tar.gz esp32p4-9ab0cbb93dd7678dcc8e86e164325e7d1b8ea7c6.zip | |
Archive the vendor and ISA documents this port was read against
`cpu-docs/` holds the documents behind the reverse-engineering in this repo, and
`cpu-docs/manifests/` records each source URL with a sha256, a byte count and a
page count. The manifests ARE the archive as far as this history is concerned:
121 MB of vendor PDFs are ignored, exactly as `/zig-pkg/` is, because a re-fetch
is one command away and the manifest makes a drifted document detectable.
The small text sources stay tracked instead of ignored, because Espressif
publishes them in no other form - the esptool serial protocol, firmware image
format and boot-mode selection pages, and the P4's custom PIE/SIMD instruction
reference. There is no PDF to re-fetch in their place.
Diffstat (limited to 'cpu-docs/espressif-custom-isa')
| -rw-r--r-- | cpu-docs/espressif-custom-isa/esp32p4-pie-simd_instruction-reference_esp-dl_2026-07-15.md | 907 |
1 files changed, 907 insertions, 0 deletions
diff --git a/cpu-docs/espressif-custom-isa/esp32p4-pie-simd_instruction-reference_esp-dl_2026-07-15.md b/cpu-docs/espressif-custom-isa/esp32p4-pie-simd_instruction-reference_esp-dl_2026-07-15.md new file mode 100644 index 0000000..fa60eb1 --- /dev/null +++ b/cpu-docs/espressif-custom-isa/esp32p4-pie-simd_instruction-reference_esp-dl_2026-07-15.md @@ -0,0 +1,907 @@ +# ESP32-P4 SIMD Instruction Reference + +## Table of Contents +- [Register Architecture](#register-architecture) +- [Data Overflow, Saturation, and Rounding](#data-overflow-saturation-and-rounding) +- [Read Instructions](#read-instructions) +- [Write Instructions](#write-instructions) +- [Data Exchange Instructions](#data-exchange-instructions) +- [Arithmetic Instructions](#arithmetic-instructions) +- [Comparison Instructions](#comparison-instructions) +- [Bitwise Logical Instructions](#bitwise-logical-instructions) +- [Shift Instructions](#shift-instructions) +- [FFT Dedicated Instructions](#fft-dedicated-instructions) + +--- + +## Register Architecture + +### QR (Vector) Registers +- 8 × 128-bit vector registers: `q0`–`q7` +- Also referenced as `qw`, `qx`, `qy`, `qz`, `qu`, `qv` in instruction syntax +- Each QR can be viewed as: + - 16 × 8-bit elements (bytes) + - 8 × 16-bit elements (half-words) + - 4 × 32-bit elements (words) + +### QACC (Quad Accumulator) Registers +- **QACC_H**: 256-bit accumulator (high half) +- **QACC_L**: 256-bit accumulator (low half) +- Together form a 512-bit accumulator for multiply-accumulate operations +- QACC_H/L are segmented based on element width: + - S8/U8 MAC: 16 × 32-bit segments per QACC half + - S16/U16 MAC: 4 × 64-bit segments per QACC half + +### XACC (Cross Accumulator) Register +- 40-bit accumulator for dot-product (sum-of-products) operations +- `XACC[39:24]`: high 16 bits +- `XACC[23:0]`: low 24 bits (sign-extended to 32 when read) + +### SAR (Shift Amount Register) +- 6-bit register (`SAR[5:0]`), controls right-shift amount for multiply/accumulate→QR moves +- Used by VMUL, CMUL, MOV.*.QACC, SRCMB instructions + +### SAR_BYTE Register +- Byte-level shift amount for spliced shift instructions (SRC.Q etc.) +- Set automatically by ESP.LD.128.USAR.* instructions from address LSBs + +### CFG (Configuration Register) +Control/status register accessed via `ESP.MOVX.R.CFG` / `ESP.MOVX.W.CFG`: + +| Field | Bits | Access | Description | +|-----------|-------|--------|-------------| +| `vxsat_en`| 8 | R/W | Enable saturation status tracking | +| `vxrm` | 7:4 | R/W | Rounding mode (see [Rounding Modes](#rounding-modes)) | +| `rm_exc` | 3 | RO | Exception flag for UNNECESSARY rounding mode | +| `vxsat` | 2 | RO | Saturation occurred flag (cleared on CFG read if vxsat_en=1) | +| `mis_ld` | 1 | R/W | Enable hardware misaligned load (0=force-align, 1=HW handle) | +| `mis_st` | 0 | R/W | Enable hardware misaligned store (0=force-align, 1=HW handle) | + +### FFT_BIT_WIDTH Register +- 4-bit register controlling bit-reverse width (3–10 bits) for `ESP.BITREV` + +### PERF (Performance Counter) Register +- 32-bit performance counter, accessed via `ESP.MOVX.R.PERF` / `ESP.MOVX.W.PERF` + +--- + +## Data Overflow, Saturation, and Rounding + +### Data Overflow Handling + +When an operation result exceeds the bit-width of the destination register, two strategies are used: + +1. **Saturation** (clipping): The result is clamped to the representable range. + - Signed N-bit: clamped to `[-2^(N-1), 2^(N-1)-1]` + - Unsigned N-bit: clamped to `[0, 2^N-1]` + - Used by: VADD, VSUB, VSADDS, VSSUBS, VMULAS, VSMULAS, SRCMB, SRS, VCLAMP, VSAT + +2. **Wraparound** (truncation): Only the lower N bits of the result are retained. + - Used by: internal calculation results of most other instructions + +### Saturation Status (vxsat) + +- When `vxsat_en` is set in CFG, the `vxsat` bit records whether any saturation occurred +- `vxsat` is sticky: once set, it stays set until explicitly cleared +- Reading CFG (via `ESP.MOVX.R.CFG`) automatically clears `vxsat` + +### Rounding Modes (vxrm) + +The 4-bit `vxrm` field in CFG controls rounding behavior for right-shift operations: + +| Mode | vxrm | Description | +|-------------|------|-------------| +| FLOOR | 0 | Round towards -∞ | +| CEILING | 1 | Round towards +∞ | +| UP | 2 | Round away from zero | +| DOWN | 3 | Round towards zero (truncation) | +| HALF_UP | 4 | Round to nearest; ties round up (away from zero) | +| HALF_DOWN | 5 | Round to nearest; ties round down (towards zero) | +| HALF_EVEN | 6 | Round to nearest; ties round to even neighbor | +| UNNECESSARY | 7 | Assert no rounding needed; `rm_exc` set if rounding would occur | + +Rounding examples for common values: + +| Input | FLOOR | CEILING | UP | DOWN | HALF_UP | HALF_DOWN | HALF_EVEN | +|--------|-------|---------|----|------|---------|-----------|-----------| +| +5.5 | 5 | 6 | 6 | 5 | 6 | 5 | 6 | +| +2.5 | 2 | 3 | 3 | 2 | 3 | 2 | 2 | +| +1.6 | 1 | 2 | 2 | 1 | 2 | 2 | 2 | +| +1.1 | 1 | 2 | 2 | 1 | 1 | 1 | 1 | +| +1.0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | +| -1.0 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | +| -1.1 | -2 | -1 | -2 | -1 | -1 | -1 | -1 | +| -1.6 | -2 | -1 | -2 | -1 | -2 | -2 | -2 | +| -2.5 | -3 | -2 | -3 | -2 | -3 | -2 | -2 | +| -5.5 | -6 | -5 | -6 | -5 | -6 | -5 | -6 | + +### Data Alignment + +| Format | Bits | Aligned Address LSBs | +|---------|-------|----------------------| +| 1-byte | 8 | xxxx | +| 2-byte | 16 | xxx0 | +| 4-byte | 32 | xx00 | +| 8-byte | 64 | x000 | +| 16-byte | 128 | 0000 | + +**Force alignment mode** (mis_ld=0 / mis_st=0): Low address bits are forced to 0. +**Hardware misaligned mode** (mis_ld=1 / mis_st=1): Hardware splits misaligned access into multiple aligned accesses. + +--- + +## Read Instructions + +Load data from memory into vector registers. Address post-increment variants available. + +### 128-bit Vector Loads + +| Instruction | Description | +|-------------|-------------| +| `ESP.VLD.128.IP qu, rs1, imm` | Load 16 bytes, rs1 += imm (imm: -128 to 112, step 16) | +| `ESP.VLD.128.XP qu, rs1, rs2` | Load 16 bytes, rs1 += rs2 | + +### 64-bit Vector Loads (to high/low half of QR) + +| Instruction | Description | +|-------------|-------------| +| `ESP.VLD.H.64.IP qu, rs1, imm` | Load 8 bytes to QR[127:64], rs1 += imm | +| `ESP.VLD.H.64.XP qu, rs1, rs2` | Load 8 bytes to QR[127:64], rs1 += rs2 | +| `ESP.VLD.L.64.IP qu, rs1, imm` | Load 8 bytes to QR[63:0], rs1 += imm | +| `ESP.VLD.L.64.XP qu, rs1, rs2` | Load 8 bytes to QR[63:0], rs1 += rs2 | + +### Broadcast Loads (scalar to vector) + +| Instruction | Description | +|-------------|-------------| +| `ESP.VLDBC.8.IP qu, rs1, imm` | Load 1 byte, broadcast to 16 bytes, rs1 += imm | +| `ESP.VLDBC.8.XP qu, rs1, rs2` | Load 1 byte, broadcast to 16 bytes, rs1 += rs2 | +| `ESP.VLDBC.16.IP qu, rs1, imm` | Load 2 bytes, broadcast to 8 × 16-bit, rs1 += imm | +| `ESP.VLDBC.16.XP qu, rs1, rs2` | Load 2 bytes, broadcast to 8 × 16-bit, rs1 += rs2 | +| `ESP.VLDBC.32.IP qu, rs1, imm` | Load 4 bytes, broadcast to 4 × 32-bit, rs1 += imm | +| `ESP.VLDBC.32.XP qu, rs1, rs2` | Load 4 bytes, broadcast to 4 × 32-bit, rs1 += rs2 | +| `ESP.VLDHBC.16.INCP qu, qz, rs1` | Load 16 bytes, broadcast each 16-bit element to 32-bit: low halves → `qu`, high halves → `qz`. rs1 += 16 | + +### Unaligned 128-bit Loads + +| Instruction | Description | +|-------------|-------------| +| `ESP.LD.128.USAR.IP qu, rs1, imm` | Load 16 bytes (set SAR_BYTE from addr), rs1 += imm | +| `ESP.LD.128.USAR.XP qu, rs1, rs2` | Load 16 bytes (set SAR_BYTE from addr), rs1 += rs2 | + +### QACC Loads (sign/zero extended) + +| Instruction | Description | +|-------------|-------------| +| `ESP.LDQA.U8.128.IP rs1, imm` | Load 16 bytes, zero-extend each 8-bit to 20-bit to QACC_H/L, rs1 += imm | +| `ESP.LDQA.U8.128.XP rs1, rs2` | Same, rs1 += rs2 | +| `ESP.LDQA.U16.128.IP rs1, imm` | Load 16 bytes, zero-extend each 16-bit to 40-bit to QACC_H/L | +| `ESP.LDQA.U16.128.XP rs1, rs2` | Same, rs1 += rs2 | +| `ESP.LDQA.S8.128.IP rs1, imm` | Load 16 bytes, sign-extend each 8-bit to 20-bit to QACC_H/L | +| `ESP.LDQA.S8.128.XP rs1, rs2` | Same, rs1 += rs2 | +| `ESP.LDQA.S16.128.IP rs1, imm` | Load 16 bytes, sign-extend each 16-bit to 40-bit to QACC_H/L | +| `ESP.LDQA.S16.128.XP rs1, rs2` | Same, rs1 += rs2 | + +### QACC/XACC Direct Loads + +Load data directly into QACC_H, QACC_L, XACC, or UA_STATE from memory. These instructions do NOT use a QR register — the load goes directly into the special register. + +| Instruction | Description | +|-------------|-------------| +| `ESP.LD.QACC.H.H.128.IP rs1, imm` | Load 16 bytes to QACC_H[255:128], rs1 += imm (imm: -2048 to 2032, step 16) | +| `ESP.LD.QACC.H.L.128.IP rs1, imm` | Load 16 bytes to QACC_H[127:0], rs1 += imm | +| `ESP.LD.QACC.L.H.128.IP rs1, imm` | Load 16 bytes to QACC_L[255:128], rs1 += imm | +| `ESP.LD.QACC.L.L.128.IP rs1, imm` | Load 16 bytes to QACC_L[127:0], rs1 += imm | +| `ESP.LD.XACC.IP rs1, imm` | Load 8 bytes to XACC (lower 40 bits), rs1 += imm (imm: -1024 to 1016, step 8) | +| `ESP.LD.UA.STATE.IP rs1, imm` | Load 16 bytes to UA_STATE, rs1 += imm | + +### Indexed/Extended Loads + +| Instruction | Description | +|-------------|-------------| +| `ESP.LDXQ.32 qu, qw, rs1, sel4, sel8` | rs1 += qw[sel8*16+15:sel8*16] << 2 as index, then load 4 bytes into qu[32*sel4+31:32*sel4] | +| `ESP.VLDEXT.U8.IP qu, rs1, imm` | Vector unsigned-extend 8-bit load segments to 16-bit | +| `ESP.VLDEXT.U8.XP qu, rs1, rs2` | Same, rs1 += rs2 | +| `ESP.VLDEXT.U16.IP qu, rs1, imm` | Vector unsigned-extend 16-bit load segments to 32-bit | +| `ESP.VLDEXT.U16.XP qu, rs1, rs2` | Same, rs1 += rs2 | +| `ESP.VLDEXT.S8.IP qu, rs1, imm` | Vector signed-extend 8-bit load segments to 16-bit | +| `ESP.VLDEXT.S8.XP qu, rs1, rs2` | Same, rs1 += rs2 | +| `ESP.VLDEXT.S16.IP qu, rs1, imm` | Vector signed-extend 16-bit load segments to 32-bit | +| `ESP.VLDEXT.S16.XP qu, rs1, rs2` | Same, rs1 += rs2 | + +--- + +## Write Instructions + +Store data from vector registers or accumulators to memory. + +### 128-bit Vector Stores + +| Instruction | Description | +|-------------|-------------| +| `ESP.VST.128.IP qu, rs1, imm` | Store 16 bytes, rs1 += imm (imm: -128 to 112, step 16) | +| `ESP.VST.128.XP qu, rs1, rs2` | Store 16 bytes, rs1 += rs2 | + +### 64-bit Vector Stores + +| Instruction | Description | +|-------------|-------------| +| `ESP.VST.H.64.IP qu, rs1, imm` | Store QR[127:64] (8 bytes), rs1 += imm | +| `ESP.VST.H.64.XP qu, rs1, rs2` | Store QR[127:64], rs1 += rs2 | +| `ESP.VST.L.64.IP qu, rs1, imm` | Store QR[63:0] (8 bytes), rs1 += imm | +| `ESP.VST.L.64.XP qu, rs1, rs2` | Store QR[63:0], rs1 += rs2 | + +### QACC Stores + +Store QACC_H/L data directly to memory. No QR register is involved — the data flows directly from the special register to memory. + +| Instruction | Description | +|-------------|-------------| +| `ESP.ST.QACC.H.H.128.IP rs1, imm` | Store QACC_H[255:128] to memory, rs1 += imm (imm: -2048 to 2032, step 16) | +| `ESP.ST.QACC.H.L.128.IP rs1, imm` | Store QACC_H[127:0] to memory, rs1 += imm | +| `ESP.ST.QACC.L.H.128.IP rs1, imm` | Store QACC_L[255:128] to memory, rs1 += imm | +| `ESP.ST.QACC.L.L.128.IP rs1, imm` | Store QACC_L[127:0] to memory, rs1 += imm | + +### XACC Stores + +Store XACC data directly to memory (sign-extended or zero-extended to 8 bytes). No QR register is involved. + +| Instruction | Description | +|-------------|-------------| +| `ESP.ST.U.XACC.IP rs1, imm` | Zero-extend XACC[39:0] to 64-bit and store, rs1 += imm (imm: -1024 to 1016, step 8) | +| `ESP.ST.S.XACC.IP rs1, imm` | Sign-extend XACC[39:0] to 64-bit and store, rs1 += imm | + +### Other Stores + +| Instruction | Description | +|-------------|-------------| +| `ESP.ST.UA.STATE.IP rs1, imm` | Store UA_STATE to memory, rs1 += imm | +| `ESP.STXQ.32 qu, qw, rs1, sel4, sel8` | Store qu[32*sel4+31:32*sel4] to address rs1 + qw[sel8*16+15:sel8*16] << 2, then rs1 += qw[sel8*16+15:sel8*16] << 2 | + +--- + +## Data Exchange Instructions + +Move data between different register types and perform reordering. + +### AR to QR Element Moves + +| Instruction | Description | +|-------------|-------------| +| `ESP.MOVI.8.A qu, rs1, sel16` | Move QR[sel16*8+7:sel16*8] (1 byte) to AR | +| `ESP.MOVI.16.A qu, rs1, sel8` | Move QR[sel8*16+15:sel8*16] (2 bytes) to AR | +| `ESP.MOVI.32.A qu, rs1, sel4` | Move QR[sel4*32+31:sel4*32] (4 bytes) to AR | +| `ESP.MOVI.8.Q qu, rs1, sel16` | Move AR to QR[sel16*8+7:sel16*8] | +| `ESP.MOVI.16.Q qu, rs1, sel8` | Move AR to QR[sel8*16+15:sel8*16] | +| `ESP.MOVI.32.Q qu, rs1, sel4` | Move AR to QR[sel4*32+31:sel4*32] | + +### Special Register Moves + +| Instruction | Description | +|-------------|-------------| +| `ESP.MOVX.R.CFG rd` | Read CFG to AR. Also auto-clears vxsat bit. | +| `ESP.MOVX.W.CFG rs1` | Write AR to CFG | +| `ESP.MOVX.R.SAR.BYTES rd` | Read SAR_BYTE to AR | +| `ESP.MOVX.W.SAR.BYTES rs1` | Write AR to SAR_BYTE | +| `ESP.MOVX.R.SAR rd` | Read SAR to AR | +| `ESP.MOVX.W.SAR rs1` | Write AR to SAR | +| `ESP.MOVX.R.FFT.BIT.WIDTH rd` | Read FFT_BIT_WIDTH to AR | +| `ESP.MOVX.W.FFT.BIT.WIDTH rs1` | Write AR to FFT_BIT_WIDTH | +| `ESP.MOVX.R.PERF rd, rs1` | Read PERF counter to AR | +| `ESP.MOVX.W.PERF rs1` | Write AR to PERF counter | +| `ESP.MOVX.R.XACC.H rd` | Read XACC[39:24] (high 16 bits) to AR | +| `ESP.MOVX.R.XACC.L rd` | Read XACC[23:0] (low 24 bits, sign-extended to 32) to AR | +| `ESP.MOVX.W.XACC.H rs1` | Write AR to XACC[39:24] | +| `ESP.MOVX.W.XACC.L rs1` | Write AR to XACC[23:0] | + +### QR Data Movement + +| Instruction | Description | +|-------------|-------------| +| `ESP.VZIP.8 qz, qx, qy` | Zip/interleave two QR by 8-bit elements | +| `ESP.VZIP.16 qz, qx, qy` | Zip/interleave two QR by 16-bit elements | +| `ESP.VZIP.32 qz, qx, qy` | Zip/interleave two QR by 32-bit elements | +| `ESP.VUNZIP.8 qz, qx, qy` | Unzip/deinterleave two QR by 8-bit elements | +| `ESP.VUNZIP.16 qz, qx, qy` | Unzip/deinterleave two QR by 16-bit elements | +| `ESP.VUNZIP.32 qz, qx, qy` | Unzip/deinterleave two QR by 32-bit elements | +| `ESP.VZIPT.8 qz, qx, qy` | Zip three QR by 8-bit elements | +| `ESP.VZIPT.16 qz, qx, qy` | Zip three QR by 16-bit elements | +| `ESP.VUNZIPT.8 qz, qx, qy` | Unzip three QR by 8-bit elements | +| `ESP.VUNZIPT.16 qz, qx, qy` | Unzip three QR by 16-bit elements | + +### Sign/Zero Extension + +| Instruction | Description | +|-------------|-------------| +| `ESP.VEXT.U8 qu, qv` | Zero-extend each 8-bit element to 16-bit | +| `ESP.VEXT.S8 qu, qv` | Sign-extend each 8-bit element to 16-bit | +| `ESP.VEXT.U16 qu, qv` | Zero-extend each 16-bit element to 32-bit | +| `ESP.VEXT.S16 qu, qv` | Sign-extend each 16-bit element to 32-bit | + +### QR to QACC Moves + +Load a QR register's data into QACC_H/L, with sign-extension or zero-extension. + +| Instruction | Description | +|-------------|-------------| +| `ESP.MOV.S8.QACC qu` | Sign-extend each of 16 × 8-bit segments in `qu` to 32-bit, write to QACC_H/L | +| `ESP.MOV.U8.QACC qu` | Zero-extend each of 16 × 8-bit segments in `qu` to 32-bit, write to QACC_H/L | +| `ESP.MOV.S16.QACC qu` | Sign-extend each of 8 × 16-bit segments in `qu` to 64-bit, write to QACC_H/L | +| `ESP.MOV.U16.QACC qu` | Zero-extend each of 8 × 16-bit segments in `qu` to 64-bit, write to QACC_H/L | + +> **Note:** To extract data FROM QACC back TO a QR register, use the SRCMB instructions (see [QACC/XACC Shift and Move](#qaccxacc-shift-and-move)). + +### Register Clears + +| Instruction | Description | +|-------------|-------------| +| `ESP.ZERO.Q qN` | Clear QR register to zero | +| `ESP.ZERO.QACC` | Clear QACC_H and QACC_L to zero | +| `ESP.ZERO.XACC` | Clear XACC to zero | + +--- + +## Arithmetic Instructions + +**Important:** All VADD and VSUB variants (both signed and unsigned) perform **saturating** arithmetic. Results are clamped to the representable range of the destination element type. + +### Vector Addition (Signed, Saturating) + +| Instruction | Description | +|-------------|-------------| +| `ESP.VADD.S8 qv, qx, qy` | 16 × 8-bit signed saturating add: `min(max(qx[i]+qy[i], -2^7), 2^7-1)` | +| `ESP.VADD.S16 qv, qx, qy` | 8 × 16-bit signed saturating add: `min(max(qx[i]+qy[i], -2^15), 2^15-1)` | +| `ESP.VADD.S32 qv, qx, qy` | 4 × 32-bit signed saturating add: `min(max(qx[i]+qy[i], -2^31), 2^31-1)` | +| `ESP.VADD.S8.LD.INCP qu, rs1, qv, qx, qy` | VADD.S8 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VADD.S16.LD.INCP qu, rs1, qv, qx, qy` | VADD.S16 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VADD.S32.LD.INCP qu, rs1, qv, qx, qy` | VADD.S32 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VADD.S8.ST.INCP qu, rs1, qv, qx, qy` | VADD.S8 + store qu to memory, rs1 += 16 | +| `ESP.VADD.S16.ST.INCP qu, rs1, qv, qx, qy` | VADD.S16 + store qu to memory, rs1 += 16 | +| `ESP.VADD.S32.ST.INCP qu, rs1, qv, qx, qy` | VADD.S32 + store qu to memory, rs1 += 16 | + +### Vector Addition (Unsigned, Saturating) + +| Instruction | Description | +|-------------|-------------| +| `ESP.VADD.U8 qv, qx, qy` | 16 × 8-bit unsigned saturating add: `min(qx[i]+qy[i], 2^8-1)` | +| `ESP.VADD.U16 qv, qx, qy` | 8 × 16-bit unsigned saturating add: `min(qx[i]+qy[i], 2^16-1)` | +| `ESP.VADD.U32 qv, qx, qy` | 4 × 32-bit unsigned saturating add: `min(qx[i]+qy[i], 2^32-1)` | +| `ESP.VADD.U8.LD.INCP qu, rs1, qv, qx, qy` | VADD.U8 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VADD.U16.LD.INCP qu, rs1, qv, qx, qy` | VADD.U16 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VADD.U32.LD.INCP qu, rs1, qv, qx, qy` | VADD.U32 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VADD.U8.ST.INCP qu, rs1, qv, qx, qy` | VADD.U8 + store qu to memory, rs1 += 16 | +| `ESP.VADD.U16.ST.INCP qu, rs1, qv, qx, qy` | VADD.U16 + store qu to memory, rs1 += 16 | +| `ESP.VADD.U32.ST.INCP qu, rs1, qv, qx, qy` | VADD.U32 + store qu to memory, rs1 += 16 | + +### Scalar Saturated Vector Addition + +**Note:** VSADDS adds a **scalar** from an AR register (`rs1`) to each element of a QR vector. Different from VADD which uses two QR vectors. + +| Instruction | Description | +|-------------|-------------| +| `ESP.VSADDS.S8 qv, qx, rs1` | 16 × 8-bit: `qv[i] = min(max(qx[i] + rs1[7:0], -2^7), 2^7-1)` | +| `ESP.VSADDS.S16 qv, qx, rs1` | 8 × 16-bit: `qv[i] = min(max(qx[i] + rs1[15:0], -2^15), 2^15-1)` | +| `ESP.VSADDS.U8 qv, qx, rs1` | 16 × 8-bit: `qv[i] = min(qx[i] + rs1[7:0], 2^8-1)` | +| `ESP.VSADDS.U16 qv, qx, rs1` | 8 × 16-bit: `qv[i] = min(qx[i] + rs1[15:0], 2^16-1)` | + +### Vector Subtraction (Signed, Saturating) + +| Instruction | Description | +|-------------|-------------| +| `ESP.VSUB.S8 qv, qx, qy` | 16 × 8-bit signed saturating sub (qx - qy) | +| `ESP.VSUB.S16 qv, qx, qy` | 8 × 16-bit signed saturating sub | +| `ESP.VSUB.S32 qv, qx, qy` | 4 × 32-bit signed saturating sub | +| `ESP.VSUB.S8.LD.INCP qu, rs1, qv, qx, qy` | VSUB.S8 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VSUB.S16.LD.INCP qu, rs1, qv, qx, qy` | VSUB.S16 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VSUB.S32.LD.INCP qu, rs1, qv, qx, qy` | VSUB.S32 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VSUB.S8.ST.INCP qu, rs1, qv, qx, qy` | VSUB.S8 + store qu to memory, rs1 += 16 | +| `ESP.VSUB.S16.ST.INCP qu, rs1, qv, qx, qy` | VSUB.S16 + store qu to memory, rs1 += 16 | +| `ESP.VSUB.S32.ST.INCP qu, rs1, qv, qx, qy` | VSUB.S32 + store qu to memory, rs1 += 16 | + +### Vector Subtraction (Unsigned, Saturating) + +| Instruction | Description | +|-------------|-------------| +| `ESP.VSUB.U8 qv, qx, qy` | 16 × 8-bit unsigned saturating sub (qx - qy): `min(qx[i]-qy[i], 2^8-1)` | +| `ESP.VSUB.U16 qv, qx, qy` | 8 × 16-bit unsigned saturating sub: `min(qx[i]-qy[i], 2^16-1)` | +| `ESP.VSUB.U32 qv, qx, qy` | 4 × 32-bit unsigned saturating sub: `min(qx[i]-qy[i], 2^32-1)` | +| `ESP.VSUB.U8.LD.INCP qu, rs1, qv, qx, qy` | VSUB.U8 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VSUB.U16.LD.INCP qu, rs1, qv, qx, qy` | VSUB.U16 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VSUB.U32.LD.INCP qu, rs1, qv, qx, qy` | VSUB.U32 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VSUB.U8.ST.INCP qu, rs1, qv, qx, qy` | VSUB.U8 + store qu to memory, rs1 += 16 | +| `ESP.VSUB.U16.ST.INCP qu, rs1, qv, qx, qy` | VSUB.U16 + store qu to memory, rs1 += 16 | +| `ESP.VSUB.U32.ST.INCP qu, rs1, qv, qx, qy` | VSUB.U32 + store qu to memory, rs1 += 16 | + +### Scalar Saturated Vector Subtraction + +**Note:** VSSUBS subtracts a **scalar** from an AR register (`rs1`) from each element of a QR vector. + +| Instruction | Description | +|-------------|-------------| +| `ESP.VSSUBS.S8 qv, qx, rs1` | 16 × 8-bit: `qv[i] = min(max(qx[i] - rs1[7:0], -2^7), 2^7-1)` | +| `ESP.VSSUBS.S16 qv, qx, rs1` | 8 × 16-bit: `qv[i] = min(max(qx[i] - rs1[15:0], -2^15), 2^15-1)` | +| `ESP.VSSUBS.U8 qv, qx, rs1` | 16 × 8-bit: `qv[i] = min(qx[i] - rs1[7:0], 2^8-1)` | +| `ESP.VSSUBS.U16 qv, qx, rs1` | 8 × 16-bit: `qv[i] = min(qx[i] - rs1[15:0], 2^16-1)` | + +### Vector Multiplication + +Multiplies are followed by an **arithmetic** right shift of SAR bits; the lower half is kept. The rounding mode (vxrm in CFG) controls rounding during the shift. + +| Instruction | Description | +|-------------|-------------| +| `ESP.VMUL.S8 qz, qx, qy` | 16 × 8-bit signed mul, result >> SAR, keep low 8 | +| `ESP.VMUL.S16 qz, qx, qy` | 8 × 16-bit signed mul, result >> SAR, keep low 16 | +| `ESP.VMUL.U8 qz, qx, qy` | 16 × 8-bit unsigned mul, result >> SAR, keep low 8 | +| `ESP.VMUL.U16 qz, qx, qy` | 8 × 16-bit unsigned mul, result >> SAR, keep low 16 | +| `ESP.VMUL.S8.LD.INCP qu, rs1, qz, qx, qy` | V.MUL.S8 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VMUL.S16.LD.INCP qu, rs1, qz, qx, qy` | V.MUL.S16 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VMUL.U8.LD.INCP qu, rs1, qz, qx, qy` | V.MUL.U8 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VMUL.U16.LD.INCP qu, rs1, qz, qx, qy` | V.MUL.U16 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VMUL.S8.ST.INCP qu, rs1, qz, qx, qy` | V.MUL.S8 + store qu to memory, rs1 += 16 | +| `ESP.VMUL.S16.ST.INCP qu, rs1, qz, qx, qy` | V.MUL.S16 + store qu to memory, rs1 += 16 | +| `ESP.VMUL.U8.ST.INCP qu, rs1, qz, qx, qy` | V.MUL.U8 + store qu to memory, rs1 += 16 | +| `ESP.VMUL.U16.ST.INCP qu, rs1, qz, qx, qy` | V.MUL.U16 + store qu to memory, rs1 += 16 | + +### Extended Output Vector Multiplication + +| Instruction | Description | +|-------------|-------------| +| `ESP.VMUL.S32.S16xS16 qz, qx, qy` | 8 × 16-bit signed mul, result >> SAR, produce 8 × 32-bit results (full 32-bit result kept) | +| `ESP.VMUL.S16.S8xS8 qz, qv, qx, qy` | 16 × 8-bit signed mul → 16 × 16-bit: low 8 results to `qz`, high 8 results to `qv`. Each result >> SAR before storing. | + +### Vector Complex Multiplication + +Operates on pairs as complex numbers (real, imag). The `sel4` immediate (0–3) selects which quadrant of the 128-bit register to operate on. Result is right-shifted by SAR. + +#### Signed Complex Multiplication + +| Instruction | Description | +|-------------|-------------| +| `ESP.CMUL.S16 qz, qx, qy, sel4` | 16-bit signed complex multiply (sel4 controls operand quadrants). `real = (a.re*b.re - a.im*b.im) >> SAR`, `imag = (a.re*b.im + a.im*b.re) >> SAR` (or conjugate variants per sel4) | +| `ESP.CMUL.S16.LD.INCP qu, rs1, qz, qx, qy, sel4` | CMUL.S16 + load 16 bytes to qu, rs1 += 16 | +| `ESP.CMUL.S16.ST.INCP qu, rs1, qz, qx, qy, sel4` | CMUL.S16 + store qu to memory, rs1 += 16 | +| `ESP.CMUL.S8 qz, qx, qy, sel4` | 8-bit signed complex multiply (sel4 controls operand halves) | +| `ESP.CMUL.S8.LD.INCP qu, rs1, qz, qx, qy, sel4` | CMUL.S8 + load 16 bytes to qu, rs1 += 16 | +| `ESP.CMUL.S8.ST.INCP qu, rs1, qz, qx, qy, sel4` | CMUL.S8 + store qu to memory, rs1 += 16 | + +#### Unsigned Complex Multiplication + +| Instruction | Description | +|-------------|-------------| +| `ESP.CMUL.U16 qz, qx, qy, sel4` | 16-bit unsigned complex multiply | +| `ESP.CMUL.U16.LD.INCP qu, rs1, qz, qx, qy, sel4` | CMUL.U16 + load 16 bytes to qu, rs1 += 16 | +| `ESP.CMUL.U16.ST.INCP qu, rs1, qz, qx, qy, sel4` | CMUL.U16 + store qu to memory, rs1 += 16 | +| `ESP.CMUL.U8 qz, qx, qy, sel4` | 8-bit unsigned complex multiply | +| `ESP.CMUL.U8.LD.INCP qu, rs1, qz, qx, qy, sel4` | CMUL.U8 + load 16 bytes to qu, rs1 += 16 | +| `ESP.CMUL.U8.ST.INCP qu, rs1, qz, qx, qy, sel4` | CMUL.U8 + store qu to memory, rs1 += 16 | + +### Vector Multiply-Accumulate to QACC + +Accumulates element-wise products into QACC segments. Results are **saturated** to the accumulator segment width. + +**Segment widths:** +- S8/U8 MAC: 32-bit per QACC segment (16 segments per QACC half) +- S16/U16 MAC: 64-bit per QACC segment (4 segments per QACC half) + +#### Signed MAC to QACC + +| Instruction | Description | +|-------------|-------------| +| `ESP.VMULAS.S8.QACC qx, qy` | 16 × S8 mul, accumulate 32-bit to QACC_H/L (saturated to 32-bit signed) | +| `ESP.VMULAS.S16.QACC qx, qy` | 8 × S16 mul, accumulate 64-bit to QACC_H/L (saturated to 64-bit signed) | + +#### Unsigned MAC to QACC + +| Instruction | Description | +|-------------|-------------| +| `ESP.VMULAS.U8.QACC qx, qy` | 16 × U8 mul, accumulate 32-bit to QACC_H/L (saturated to 32-bit unsigned) | +| `ESP.VMULAS.U16.QACC qx, qy` | 8 × U16 mul, accumulate 64-bit to QACC_H/L (saturated to 64-bit unsigned) | + +#### Fused MAC variants (signed, with memory access) + +| Instruction | Description | +|-------------|-------------| +| `ESP.VMULAS.S8.QACC.LD.IP qu, rs, imm, qx, qy` | MAC S8 to QACC + load (rs += imm) | +| `ESP.VMULAS.S16.QACC.LD.IP qu, rs, imm, qx, qy` | MAC S16 to QACC + load | +| `ESP.VMULAS.S8.QACC.LD.XP qu, rs1, rs2, qx, qy` | MAC S8 to QACC + load (rs1 += rs2) | +| `ESP.VMULAS.S16.QACC.LD.XP qu, rs1, rs2, qx, qy` | MAC S16 to QACC + load (rs1 += rs2) | +| `ESP.VMULAS.S8.QACC.ST.IP qu, rs, imm, qx, qy` | MAC S8 to QACC + store | +| `ESP.VMULAS.S16.QACC.ST.IP qu, rs, imm, qx, qy` | MAC S16 to QACC + store | +| `ESP.VMULAS.S8.QACC.ST.XP qu, rs1, rs2, qx, qy` | MAC S8 to QACC + store (rs1 += rs2) | +| `ESP.VMULAS.S16.QACC.ST.XP qu, rs1, rs2, qx, qy` | MAC S16 to QACC + store (rs1 += rs2) | +| `ESP.VMULAS.S8.QACC.LDBC.INCP qu, rs, imm, qx, qy` | MAC S8 to QACC + broadcast load | +| `ESP.VMULAS.S16.QACC.LDBC.INCP qu, rs, imm, qx, qy` | MAC S16 to QACC + broadcast load | + +#### Fused MAC variants (unsigned, with memory access) + +| Instruction | Description | +|-------------|-------------| +| `ESP.VMULAS.U8.QACC.LD.IP qu, rs, imm, qx, qy` | MAC U8 to QACC + load | +| `ESP.VMULAS.U16.QACC.LD.IP qu, rs, imm, qx, qy` | MAC U16 to QACC + load | +| `ESP.VMULAS.U8.QACC.LD.XP qu, rs1, rs2, qx, qy` | MAC U8 to QACC + load (rs1 += rs2) | +| `ESP.VMULAS.U16.QACC.LD.XP qu, rs1, rs2, qx, qy` | MAC U16 to QACC + load (rs1 += rs2) | +| `ESP.VMULAS.U8.QACC.ST.IP qu, rs, imm, qx, qy` | MAC U8 to QACC + store | +| `ESP.VMULAS.U16.QACC.ST.IP qu, rs, imm, qx, qy` | MAC U16 to QACC + store | +| `ESP.VMULAS.U8.QACC.ST.XP qu, rs1, rs2, qx, qy` | MAC U8 to QACC + store (rs1 += rs2) | +| `ESP.VMULAS.U16.QACC.ST.XP qu, rs1, rs2, qx, qy` | MAC U16 to QACC + store (rs1 += rs2) | +| `ESP.VMULAS.U8.QACC.LDBC.INCP qu, rs, imm, qx, qy` | MAC U8 to QACC + broadcast load | +| `ESP.VMULAS.U16.QACC.LDBC.INCP qu, rs, imm, qx, qy` | MAC U16 to QACC + broadcast load | + +### Vector Multiply-Accumulate to XACC + +Computes the **sum** of all element-wise products (dot-product style). Result accumulated in XACC (40-bit, saturated). + +| Instruction | Description | +|-------------|-------------| +| `ESP.VMULAS.S8.XACC qx, qy` | 16 × S8 mul, sum all to XACC (40-bit, saturated) | +| `ESP.VMULAS.S16.XACC qx, qy` | 8 × S16 mul, sum all to XACC (40-bit, saturated) | +| `ESP.VMULAS.U8.XACC qx, qy` | 16 × U8 mul, sum all to XACC (40-bit, saturated) | +| `ESP.VMULAS.U16.XACC qx, qy` | 8 × U16 mul, sum all to XACC (40-bit, saturated) | + +**Fused variants:** + +| Instruction | Description | +|-------------|-------------| +| `ESP.VMULAS.S8.XACC.LD.IP qu, rs, imm, qx, qy` | MAC to XACC + load | +| `ESP.VMULAS.S16.XACC.LD.IP qu, rs, imm, qx, qy` | MAC to XACC + load | +| `ESP.VMULAS.S8.XACC.LD.XP qu, rs1, rs2, qx, qy` | MAC to XACC + load (rs1 += rs2) | +| `ESP.VMULAS.S16.XACC.LD.XP qu, rs1, rs2, qx, qy` | MAC to XACC + load (rs1 += rs2) | +| `ESP.VMULAS.S8.XACC.ST.IP qu, rs, imm, qx, qy` | MAC to XACC + store | +| `ESP.VMULAS.S16.XACC.ST.IP qu, rs, imm, qx, qy` | MAC to XACC + store | +| `ESP.VMULAS.S8.XACC.ST.XP qu, rs1, rs2, qx, qy` | MAC to XACC + store (rs1 += rs2) | +| `ESP.VMULAS.S16.XACC.ST.XP qu, rs1, rs2, qx, qy` | MAC to XACC + store (rs1 += rs2) | + +### Scalar-Vector Multiply-Accumulate to QACC + +One operand is a vector (`qx`), the other is a **scalar element** selected from `qy` using `sel`. Accumulates into QACC with saturation. + +#### Signed Scalar-Vector MAC + +| Instruction | Description | +|-------------|-------------| +| `ESP.VSMULAS.S8.QACC qx, qy, sel16` | Select 1 of 16 bytes from qy; 16 × S8 MAC to QACC (saturated to 32-bit signed) | +| `ESP.VSMULAS.S16.QACC qx, qy, sel8` | Select 1 of 8 half-words from qy; 8 × S16 MAC to QACC (saturated to 64-bit signed) | +| `ESP.VSMULAS.S8.QACC.LD.INCP qu, rs, qx, qy, sel16` | VSMULAS.S8.QACC + load 16 bytes to qu, rs += 16 | +| `ESP.VSMULAS.S16.QACC.LD.INCP qu, rs, qx, qy, sel8` | VSMULAS.S16.QACC + load 16 bytes to qu, rs += 16 | + +#### Unsigned Scalar-Vector MAC + +| Instruction | Description | +|-------------|-------------| +| `ESP.VSMULAS.U8.QACC qx, qy, sel16` | Select 1 of 16 bytes from qy; 16 × U8 MAC to QACC (saturated to 32-bit unsigned) | +| `ESP.VSMULAS.U16.QACC qx, qy, sel8` | Select 1 of 8 half-words from qy; 8 × U16 MAC to QACC (saturated to 64-bit unsigned) | +| `ESP.VSMULAS.U8.QACC.LD.INCP qu, rs, qx, qy, sel16` | VSMULAS.U8.QACC + load 16 bytes to qu, rs += 16 | +| `ESP.VSMULAS.U16.QACC.LD.INCP qu, rs, qx, qy, sel8` | VSMULAS.U16.QACC + load 16 bytes to qu, rs += 16 | + +### Complex Multiply-Accumulate to QACC + +| Instruction | Description | +|-------------|-------------| +| `ESP.VCMULAS.S8.QACC.H qx, qy` | Complex MAC S8 to QACC_H | +| `ESP.VCMULAS.S8.QACC.L qx, qy` | Complex MAC S8 to QACC_L | +| `ESP.VCMULAS.S16.QACC.H qx, qy` | Complex MAC S16 to QACC_H | +| `ESP.VCMULAS.S16.QACC.L qx, qy` | Complex MAC S16 to QACC_L | + +**Fused variants:** + +| Instruction | Description | +|-------------|-------------| +| `ESP.VCMULAS.S8.QACC.H.LD.IP qu, rs, imm, qx, qy` | Complex MAC to QACC_H + load | +| `ESP.VCMULAS.S8.QACC.L.LD.IP qu, rs, imm, qx, qy` | Complex MAC to QACC_L + load | +| `ESP.VCMULAS.S16.QACC.H.LD.IP qu, rs, imm, qx, qy` | Complex MAC S16 to QACC_H + load | +| `ESP.VCMULAS.S16.QACC.L.LD.IP qu, rs, imm, qx, qy` | Complex MAC S16 to QACC_L + load | +| `ESP.VCMULAS.S8.QACC.H.LD.XP qu, rs1, rs2, qx, qy` | Complex MAC to QACC_H + load (rs1 += rs2) | +| `ESP.VCMULAS.S8.QACC.L.LD.XP qu, rs1, rs2, qx, qy` | Complex MAC to QACC_L + load (rs1 += rs2) | +| `ESP.VCMULAS.S16.QACC.H.LD.XP qu, rs1, rs2, qx, qy` | Complex MAC S16 to QACC_H + load (rs1 += rs2) | +| `ESP.VCMULAS.S16.QACC.L.LD.XP qu, rs1, rs2, qx, qy` | Complex MAC S16 to QACC_L + load (rs1 += rs2) | + +### QACC/XACC Shift and Move + +| Instruction | Description | +|-------------|-------------| +| `ESP.SRCMB.S8.QACC qx, imm` | Shift QACC_H/L right by `imm` per 32-bit segment, saturate to S8, move to qx | +| `ESP.SRCMB.S16.QACC qx, imm` | Shift QACC_H/L right by `imm` per 64-bit segment, saturate to S16, move to qx | +| `ESP.SRCMB.U8.QACC qx, imm` | Shift QACC_H/L right by `imm` per 32-bit segment, saturate to U8, move to qx | +| `ESP.SRCMB.U16.QACC qx, imm` | Shift QACC_H/L right by `imm` per 64-bit segment, saturate to U16, move to qx | +| `ESP.SRCMB.S8.Q.QACC qx, qy` | Same as SRCMB.S8.QACC but shift amount from QR | +| `ESP.SRCMB.S16.Q.QACC qx, qy` | Same as SRCMB.S16.QACC but shift amount from QR | +| `ESP.SRCMB.U8.Q.QACC qx, qy` | Same as SRCMB.U8.QACC but shift amount from QR | +| `ESP.SRCMB.U16.Q.QACC qx, qy` | Same as SRCMB.U16.QACC but shift amount from QR | +| `ESP.SRS.S.XACC rd, rs1` | Arithmetic right shift XACC by `rs1[5:0]`; write 40-bit result back to XACC, and write result saturated to 32-bit signed: `min(max(XACC>>rs1, -2^31), 2^31-1)` into `rd` | +| `ESP.SRS.U.XACC rd, rs1` | Unsigned counterpart: right shift XACC by `rs1[5:0]`, write 40-bit result back to XACC, write result saturated to 32-bit unsigned into `rd` | + +### Activation / Other Arithmetic + +| Instruction | Description | +|-------------|-------------| +| `ESP.VRELU.S8 qz, qx, qy` | 16 × S8 ReLU: `qz[i] = qx[i] > 0 ? qx[i] * qy[0] : 0` | +| `ESP.VRELU.S16 qz, qx, qy` | 8 × S16 ReLU: `qz[i] = qx[i] > 0 ? qx[i] * qy[0] : 0` | +| `ESP.VPRELU.S8 qz, qx, qy` | 16 × S8 PReLU: `qz[i] = qx[i] > 0 ? qx[i] : qx[i] * qy[i]` | +| `ESP.VPRELU.S16 qz, qx, qy` | 8 × S16 PReLU: `qz[i] = qx[i] > 0 ? qx[i] : qx[i] * qy[i]` | +| `ESP.VABS.8 qz, qx` | 16 × 8-bit absolute value | +| `ESP.VABS.16 qz, qx` | 8 × 16-bit absolute value | +| `ESP.VABS.32 qz, qx` | 4 × 32-bit absolute value | +| `ESP.SAT rsd, rs0, rs1` | Saturate `rsd` between clamp bounds derived from `rs0` and `rs1`: `min_t = max(rs1, rs0)`, `max_t = min(rs1, rs0)`, `rsd = max(min(rsd, max_t), min_t)`. rsd is both source and destination (read-modify-write). | +| `ESP.ADDX2 rd, rs1, rs2` | `rd = rs1 + (rs2 << 1)` | +| `ESP.ADDX4 rd, rs1, rs2` | `rd = rs1 + (rs2 << 2)` | +| `ESP.SUBX2 rd, rs1, rs2` | `rd = rs1 - (rs2 << 1)` | +| `ESP.SUBX4 rd, rs1, rs2` | `rd = rs1 - (rs2 << 2)` | + +### Vector Clamp + +**Note:** VCLAMP uses a single QR register `qx` and an immediate `sel16`, clamped to the symmetric range `[-2^sel16, 2^sel16-1]`. + +| Instruction | Description | +|-------------|-------------| +| `ESP.VCLAMP.S16 qz, qx, sel16` | Clamp 8 × S16: `qz[i] = min(max(qx[i], -2^sel16), 2^sel16-1)` | + +### Vector Saturation (VSAT) + +**Note:** VSAT uses **two AR registers** (`rs1`, `rs2`) to define the clamp bounds. The bounds `min_t` and `max_t` are derived as `min_t = max(rs1, rs2)` and `max_t = min(rs1, rs2)` (exchanging the role of comparison to derive the actual asymmetric range). Each element in `qx` is clamped to `[min_t, max_t]`. + +| Instruction | Description | +|-------------|-------------| +| `ESP.VSAT.S8 qz, qx, rs1, rs2` | Clamp each of 16 × S8 in qx to range derived from rs1/rs2 | +| `ESP.VSAT.S16 qz, qx, rs1, rs2` | Clamp each of 8 × S16 in qx to range derived from rs1/rs2 | +| `ESP.VSAT.S32 qz, qx, rs1, rs2` | Clamp each of 4 × S32 in qx to range derived from rs1/rs2 | +| `ESP.VSAT.U8 qz, qx, rs1, rs2` | Clamp each of 16 × U8 in qx to range derived from rs1/rs2 | +| `ESP.VSAT.U16 qz, qx, rs1, rs2` | Clamp each of 8 × U16 in qx to range derived from rs1/rs2 | +| `ESP.VSAT.U32 qz, qx, rs1, rs2` | Clamp each of 4 × U32 in qx to range derived from rs1/rs2 | + +--- + +## Comparison Instructions + +### Vector Maximum (Element-wise) + +For VMAX, each element `qz[i] = (qx[i] >= qy[i]) ? qx[i] : qy[i]`. + +#### Signed Vector Maximum + +| Instruction | Description | +|-------------|-------------| +| `ESP.VMAX.S8 qz, qx, qy` | 16 × 8-bit signed element-wise max | +| `ESP.VMAX.S16 qz, qx, qy` | 8 × 16-bit signed element-wise max | +| `ESP.VMAX.S32 qz, qx, qy` | 4 × 32-bit signed element-wise max | +| `ESP.VMAX.S8.LD.INCP qu, rs1, qz, qx, qy` | VMAX.S8 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VMAX.S16.LD.INCP qu, rs1, qz, qx, qy` | VMAX.S16 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VMAX.S32.LD.INCP qu, rs1, qz, qx, qy` | VMAX.S32 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VMAX.S8.ST.INCP qu, rs1, qz, qx, qy` | VMAX.S8 + store qu to memory, rs1 += 16 | +| `ESP.VMAX.S16.ST.INCP qu, rs1, qz, qx, qy` | VMAX.S16 + store qu to memory, rs1 += 16 | +| `ESP.VMAX.S32.ST.INCP qu, rs1, qz, qx, qy` | VMAX.S32 + store qu to memory, rs1 += 16 | + +#### Unsigned Vector Maximum + +| Instruction | Description | +|-------------|-------------| +| `ESP.VMAX.U8 qz, qx, qy` | 16 × 8-bit unsigned element-wise max | +| `ESP.VMAX.U16 qz, qx, qy` | 8 × 16-bit unsigned element-wise max | +| `ESP.VMAX.U32 qz, qx, qy` | 4 × 32-bit unsigned element-wise max | +| `ESP.VMAX.U8.LD.INCP qu, rs1, qz, qx, qy` | VMAX.U8 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VMAX.U16.LD.INCP qu, rs1, qz, qx, qy` | VMAX.U16 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VMAX.U32.LD.INCP qu, rs1, qz, qx, qy` | VMAX.U32 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VMAX.U8.ST.INCP qu, rs1, qz, qx, qy` | VMAX.U8 + store qu to memory, rs1 += 16 | +| `ESP.VMAX.U16.ST.INCP qu, rs1, qz, qx, qy` | VMAX.U16 + store qu to memory, rs1 += 16 | +| `ESP.VMAX.U32.ST.INCP qu, rs1, qz, qx, qy` | VMAX.U32 + store qu to memory, rs1 += 16 | + +### Scalar Maximum (Vector → AR) + +Finds the maximum value across all elements of a QR register and writes it to an AR register `rd`. + +| Instruction | Description | +|-------------|-------------| +| `ESP.MAX.S8.A qw, rd` | Max of 16 signed 8-bit elements → rd. `rd = {24{max_value[7]}, max_value[7:0]}` (sign-extended) | +| `ESP.MAX.S16.A qw, rd` | Max of 8 signed 16-bit elements → rd. `rd = {16{max_value[15]}, max_value[15:0]}` (sign-extended) | +| `ESP.MAX.S32.A qw, rd` | Max of 4 signed 32-bit elements → rd. `rd = max_value[31:0]` | +| `ESP.MAX.U8.A qw, rd` | Max of 16 unsigned 8-bit elements → rd. `rd = {24'b0, max_value[7:0]}` (zero-extended) | +| `ESP.MAX.U16.A qw, rd` | Max of 8 unsigned 16-bit elements → rd. `rd = {16'b0, max_value[15:0]}` (zero-extended) | +| `ESP.MAX.U32.A qw, rd` | Max of 4 unsigned 32-bit elements → rd. `rd = max_value[31:0]` | + +### Vector Minimum (Element-wise) + +For VMIN, each element `qz[i] = (qx[i] <= qy[i]) ? qx[i] : qy[i]`. + +#### Signed Vector Minimum + +| Instruction | Description | +|-------------|-------------| +| `ESP.VMIN.S8 qz, qx, qy` | 16 × 8-bit signed element-wise min | +| `ESP.VMIN.S16 qz, qx, qy` | 8 × 16-bit signed element-wise min | +| `ESP.VMIN.S32 qz, qx, qy` | 4 × 32-bit signed element-wise min | +| `ESP.VMIN.S8.LD.INCP qu, rs1, qz, qx, qy` | VMIN.S8 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VMIN.S16.LD.INCP qu, rs1, qz, qx, qy` | VMIN.S16 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VMIN.S32.LD.INCP qu, rs1, qz, qx, qy` | VMIN.S32 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VMIN.S8.ST.INCP qu, rs1, qz, qx, qy` | VMIN.S8 + store qu to memory, rs1 += 16 | +| `ESP.VMIN.S16.ST.INCP qu, rs1, qz, qx, qy` | VMIN.S16 + store qu to memory, rs1 += 16 | +| `ESP.VMIN.S32.ST.INCP qu, rs1, qz, qx, qy` | VMIN.S32 + store qu to memory, rs1 += 16 | + +#### Unsigned Vector Minimum + +| Instruction | Description | +|-------------|-------------| +| `ESP.VMIN.U8 qz, qx, qy` | 16 × 8-bit unsigned element-wise min | +| `ESP.VMIN.U16 qz, qx, qy` | 8 × 16-bit unsigned element-wise min | +| `ESP.VMIN.U32 qz, qx, qy` | 4 × 32-bit unsigned element-wise min | +| `ESP.VMIN.U8.LD.INCP qu, rs1, qz, qx, qy` | VMIN.U8 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VMIN.U16.LD.INCP qu, rs1, qz, qx, qy` | VMIN.U16 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VMIN.U32.LD.INCP qu, rs1, qz, qx, qy` | VMIN.U32 + load 16 bytes to qu, rs1 += 16 | +| `ESP.VMIN.U8.ST.INCP qu, rs1, qz, qx, qy` | VMIN.U8 + store qu to memory, rs1 += 16 | +| `ESP.VMIN.U16.ST.INCP qu, rs1, qz, qx, qy` | VMIN.U16 + store qu to memory, rs1 += 16 | +| `ESP.VMIN.U32.ST.INCP qu, rs1, qz, qx, qy` | VMIN.U32 + store qu to memory, rs1 += 16 | + +### Scalar Minimum (Vector → AR) + +Finds the minimum value across all elements of a QR register and writes it to an AR register `rd`. + +| Instruction | Description | +|-------------|-------------| +| `ESP.MIN.S8.A qw, rd` | Min of 16 signed 8-bit elements → rd. `rd = {24{min_value[7]}, min_value[7:0]}` (sign-extended) | +| `ESP.MIN.S16.A qw, rd` | Min of 8 signed 16-bit elements → rd. `rd = {16{min_value[15]}, min_value[15:0]}` (sign-extended) | +| `ESP.MIN.S32.A qw, rd` | Min of 4 signed 32-bit elements → rd. `rd = min_value[31:0]` | +| `ESP.MIN.U8.A qw, rd` | Min of 16 unsigned 8-bit elements → rd. `rd = {24'b0, min_value[7:0]}` (zero-extended) | +| `ESP.MIN.U16.A qw, rd` | Min of 8 unsigned 16-bit elements → rd. `rd = {16'b0, min_value[15:0]}` (zero-extended) | +| `ESP.MIN.U32.A qw, rd` | Min of 4 unsigned 32-bit elements → rd. `rd = min_value[31:0]` | + +### Vector Compare + +Result is all 1s (true) or all 0s (false) per element. The mask width matches the data width: 0xFF for 8-bit, 0xFFFF for 16-bit, 0xFFFFFFFF for 32-bit. + +#### Signed Comparison + +| Instruction | Description | +|-------------|-------------| +| `ESP.VCMP.EQ.S8 qz, qx, qy` | 16 × 8-bit compare equal: `qz[i] = (qx[i]==qy[i]) ? 0xFF : 0` | +| `ESP.VCMP.EQ.S16 qz, qx, qy` | 8 × 16-bit compare equal: `qz[i] = (qx[i]==qy[i]) ? 0xFFFF : 0` | +| `ESP.VCMP.EQ.S32 qz, qx, qy` | 4 × 32-bit compare equal: `qz[i] = (qx[i]==qy[i]) ? 0xFFFFFFFF : 0` | +| `ESP.VCMP.LT.S8 qz, qx, qy` | 16 × 8-bit compare less-than: `qz[i] = (qx[i] < qy[i]) ? 0xFF : 0` | +| `ESP.VCMP.LT.S16 qz, qx, qy` | 8 × 16-bit compare less-than: `qz[i] = (qx[i] < qy[i]) ? 0xFFFF : 0` | +| `ESP.VCMP.LT.S32 qz, qx, qy` | 4 × 32-bit compare less-than: `qz[i] = (qx[i] < qy[i]) ? 0xFFFFFFFF : 0` | +| `ESP.VCMP.GT.S8 qz, qx, qy` | 16 × 8-bit compare greater-than: `qz[i] = (qx[i] > qy[i]) ? 0xFF : 0` | +| `ESP.VCMP.GT.S16 qz, qx, qy` | 8 × 16-bit compare greater-than: `qz[i] = (qx[i] > qy[i]) ? 0xFFFF : 0` | +| `ESP.VCMP.GT.S32 qz, qx, qy` | 4 × 32-bit compare greater-than: `qz[i] = (qx[i] > qy[i]) ? 0xFFFFFFFF : 0` | + +#### Unsigned Comparison + +| Instruction | Description | +|-------------|-------------| +| `ESP.VCMP.EQ.U8 qz, qx, qy` | 16 × 8-bit unsigned compare equal: `qz[i] = (qx[i]==qy[i]) ? 0xFF : 0` | +| `ESP.VCMP.EQ.U16 qz, qx, qy` | 8 × 16-bit unsigned compare equal: `qz[i] = (qx[i]==qy[i]) ? 0xFFFF : 0` | +| `ESP.VCMP.EQ.U32 qz, qx, qy` | 4 × 32-bit unsigned compare equal: `qz[i] = (qx[i]==qy[i]) ? 0xFFFFFFFF : 0` | +| `ESP.VCMP.LT.U8 qz, qx, qy` | 16 × 8-bit unsigned less-than: `qz[i] = (qx[i] < qy[i]) ? 0xFF : 0` | +| `ESP.VCMP.LT.U16 qz, qx, qy` | 8 × 16-bit unsigned less-than: `qz[i] = (qx[i] < qy[i]) ? 0xFFFF : 0` | +| `ESP.VCMP.LT.U32 qz, qx, qy` | 4 × 32-bit unsigned less-than: `qz[i] = (qx[i] < qy[i]) ? 0xFFFFFFFF : 0` | +| `ESP.VCMP.GT.U8 qz, qx, qy` | 16 × 8-bit unsigned greater-than: `qz[i] = (qx[i] > qy[i]) ? 0xFF : 0` | +| `ESP.VCMP.GT.U16 qz, qx, qy` | 8 × 16-bit unsigned greater-than: `qz[i] = (qx[i] > qy[i]) ? 0xFFFF : 0` | +| `ESP.VCMP.GT.U32 qz, qx, qy` | 4 × 32-bit unsigned greater-than: `qz[i] = (qx[i] > qy[i]) ? 0xFFFFFFFF : 0` | + +--- + +## Bitwise Logical Instructions + +All operate on full 128-bit QR registers. + +| Instruction | Description | +|-------------|-------------| +| `ESP.ORQ qz, qx, qy` | 128-bit bitwise OR: `qz = qx \| qy` | +| `ESP.XORQ qz, qx, qy` | 128-bit bitwise XOR: `qz = qx ^ qy` | +| `ESP.ANDQ qz, qx, qy` | 128-bit bitwise AND: `qz = qx & qy` | +| `ESP.NOTQ qz, qx` | 128-bit bitwise NOT: `qz = ~qx` | + +--- + +## Shift Instructions + +### Vector Shift Right (per-element by SAR) + +| Instruction | Description | +|-------------|-------------| +| `ESP.VSR.U32 qz, qx` | 4 × 32-bit unsigned (logical) shift right by SAR | +| `ESP.VSR.S32 qz, qx` | 4 × 32-bit signed (arithmetic) shift right by SAR | + +### Vector Shift Left (by SAR) + +| Instruction | Description | +|-------------|-------------| +| `ESP.VSL.32 qz, qx` | 4 × 32-bit shift left by SAR | + +### Vector Shift by Register (per-element signed shift amount) + +The shift amount is **not** an immediate — it comes from a Q register (`qw`), with one signed field per element aligned to that element's lane. If an element's shift field is negative, that element is shifted right by its absolute value; otherwise it is shifted left. Bits shifted out are discarded, and vacated bits are zero-filled. + +| Instruction | Description | +|-------------|-------------| +| `ESP.VSLD.8 qu, qy, qw` | 16 × 8-bit vector shift; per-lane signed amount from `qw` (negative ⇒ right shift, else left shift) | +| `ESP.VSLD.16 qu, qy, qw` | 8 × 16-bit vector shift; per-lane signed amount from `qw` (negative ⇒ right shift, else left shift) | +| `ESP.VSLD.32 qu, qy, qw` | 4 × 32-bit vector shift; per-lane signed amount from `qw` (negative ⇒ right shift, else left shift) | +| `ESP.VSRD.8 qu, qy, qw` | 16 × 8-bit vector shift; per-lane signed amount from `qw` | +| `ESP.VSRD.16 qu, qy, qw` | 8 × 16-bit vector shift; per-lane signed amount from `qw` | +| `ESP.VSRD.32 qu, qy, qw` | 4 × 32-bit vector shift; per-lane signed amount from `qw` | + +Operation for `ESP.VSLD.16 qu, qy, qw` (8 lanes of 16 bits; each lane's shift amount is the low 5 bits of the corresponding 16-bit segment of `qw`, treated as signed): + +``` +for i in 0..7: + lane_hi = 16*i + 15 + lane_lo = 16*i + shamt = qw[16*i+4 : 16*i] # signed 5-bit field + qu[lane_hi:lane_lo] = (shamt < 0) + ? (qy[lane_hi:lane_lo] >> -shamt) + : (qy[lane_hi:lane_lo] << shamt) +``` + +`ESP.VSLD.8`/`.32` and `ESP.VSRD.8/16/32` follow the same per-lane, sign-selects-direction pattern, just with the shift-amount field width and stride matched to the 8-bit/32-bit lane size instead of 16-bit. + +### Spliced Shift Instructions (for misalignment handling) + +These combine two QR registers with a byte-level shift. + +| Instruction | Description | +|-------------|-------------| +| `ESP.SRC.Q qz, qx, qy` | `qz = (qy \|\| qx) >> SAR_BYTE*8`, keep 128 bits | +| `ESP.SRC.Q.qup qz, qw, qy` | `qz = (qy \|\| qw) >> SAR_BYTE*8` AND `qw = qy` (auto-update qw for pipelined unaligned loads) | +| `ESP.SRC.Q.LD.IP qu, rs, imm, qx, qy` | SRC.Q + load 16 bytes to qu, rs += imm | +| `ESP.SRC.Q.LD.XP qu, rs1, rs2, qx, qy` | SRC.Q + load 16 bytes to qu, rs1 += rs2 | +| `ESP.SLCI.2Q qz, qx, imm` | Concatenate two QR and left shift by immediate | +| `ESP.SLCXXP.2Q qz, qx, qy` | Concatenate two QR and left shift (amount from QR) | +| `ESP.SRCI.2Q qz, qx, imm` | Concatenate two QR and right shift by immediate | +| `ESP.SRCXXP.2Q qz, qx, qy` | Concatenate two QR and right shift (amount from QR) | +| `ESP.SRCQ.128.ST.INCP qz, rs, qx, qy` | SRC.Q + store qz to memory (rs += 16) | + +--- + +## FFT Dedicated Instructions + +### Radix-2 Butterfly + +| Instruction | Description | +|-------------|-------------| +| `ESP.FFT.R2BF.S16 qz, qx, qy` | 4 × S16 radix-2 butterfly on 8 elements | +| `ESP.FFT.R2BF.S16.ST.INCP qz, qx, qy, rs` | Butterfly + store (rs += 16) | + +### Complex Multiplication for FFT + +| Instruction | Description | +|-------------|-------------| +| `ESP.FFT.CMUL.S16.LD.XP qz, qx, qy, rs` | Complex multiply + load (rs += rs2) | +| `ESP.FFT.CMUL.S16.ST.XP qz, qx, qy, rs` | Complex multiply + store (rs += rs2) | + +### Bit-Reverse + +| Instruction | Description | +|-------------|-------------| +| `ESP.BITREV rs1, rs2` | Bit-reverse rs2 (3–10 bits controlled by FFT_BIT_WIDTH), result in rs1 | + +### Real FFT Operations + +| Instruction | Description | +|-------------|-------------| +| `ESP.FFT.AMS.S16.LD.INCP.UAUP qu, rs, qx, qy` | Real FFT: multiply-subtract, load with unaligned update | +| `ESP.FFT.AMS.S16.LD.INCP qu, rs, qx, qy` | Real FFT: multiply-subtract + load | +| `ESP.FFT.AMS.S16.LD.R32.DECP qu, rs, qx, qy` | Real FFT: multiply-subtract, load 32-bit, decrement pointer | +| `ESP.FFT.AMS.S16.ST.INCP qu, rs, qx, qy` | Real FFT: multiply-subtract + store | +| `ESP.FFT.VST.R32.DECP qu, rs` | Store 32-bit real and decrement pointer | + +### FFT Multiply-Subtract Pattern + +The AMS instructions perform: `result = qx * qy - qx_shifted * qy_shifted` used in real FFT computation to extract the real spectrum from complex FFT output. + +--- + +## Instruction Summary by Operand Types + +| Operand Convention | Meaning | +|--------------------|---------| +| `qw`, `qx`, `qy`, `qz`, `qu`, `qv` | 128-bit QR vector registers (q0–q7) | +| `rd`, `rs`, `rs0`, `rs1`, `rs2` | 32-bit AR general-purpose registers | +| `imm` | Signed immediate offset | +| `sel4` | 2-bit element selector (0–3) for 32-bit elements | +| `sel8` | 3-bit element selector (0–7) for 16-bit elements | +| `sel16` | 4-bit element selector (0–15) for 8-bit elements | + +### Naming Convention in Syntax + +- **Input operands**: `qx`, `qy`, `qw` (read-only) +- **Output/destination operands**: `qz`, `qv` (write-only, or read-modify-write for MAC) +- **Load destination**: `qu` (used in fused load+compute instructions) +- **Address register**: `rs1`, `rs` (AR register holding memory address) +- **General AR**: `rd`, `rs2` or `rs0`/`rs1` for scalar operands |
