1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
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
|