Introduction
The ESP32-S3 adds a vector unit that Espressif calls PIE (processor instruction extensions). It has eight 128-bit registers
q0 to q7, a 160-bit accumulator pair QACC (low and high halves), a 40-bit accumulator ACCX,
an unaligned-load state UA_STATE, a byte shift amount SAR_BYTE and an FFT width register, all reached as
user registers. The instructions are used by Espressif's neural-network and
DSP libraries (esp-nn, esp-dsp); the real firmware on this machine contains tens of thousands of them.
They come in two sizes. Many are 24-bit (op0 4, plus two at op0 0); the ones that do two things at once
(a multiply and a load, with a pointer update) are 32-bit, op0 14 or 15, where the low bit of op0 is part of a
register field. Operand fields are scattered over the word, so the explorer's table records each operand as a list of bit positions;
the vector registers' three bits are not adjacent.
Suffixes: .ip adds an immediate to the address register afterwards, .xp adds a register, .incp increments
by the access size, .decp decrements, .qup also moves the unaligned-load state, .ld and .st add a load or a
store. The element types are s8, s16, s32, u8, u16.
This is what the Espressif objdump decodes, 190 of the 217 mnemonics of its ESP32-S3 configuration. The remaining 27 (rare 32-bit forms) are not in the explorer's decoder; they show as reserved encodings. The descriptions below are from the instruction names and the operand shapes, not from Espressif's manual, which was not read.
Vector loads and stores
| Instructions | Operands | What they do |
|---|---|---|
ee.vld.128.ip, ee.vld.128.xp | qa, as, imm | at | Load 128 bits into q, then advance the pointer by the offset (a signed 8-bit multiple of 16) or by a register. |
ee.vld.h.64.ip, ee.vld.h.64.xp, ee.vld.l.64.ip, ee.vld.l.64.xp | qa, as, imm | at | Load 64 bits into the high or low half of q (offset a multiple of 8). |
ee.vldbc.8, ee.vldbc.8.ip, ee.vldbc.8.xp, ee.vldbc.16, ee.vldbc.16.ip, ee.vldbc.16.xp, ee.vldbc.32, ee.vldbc.32.ip, ee.vldbc.32.xp | qa, as[, imm | at] | Load a byte, halfword or word and broadcast it to every lane of q (the plain forms do not move the pointer). |
ee.vldhbc.16.incp | qa, qb, as | Load and broadcast halfwords into two registers, then increment the pointer. |
ee.vst.128.ip, ee.vst.128.xp, ee.vst.h.64.ip, ee.vst.h.64.xp, ee.vst.l.64.ip, ee.vst.l.64.xp | qa, as, imm | at | Store 128 bits, or the high or low 64 bits of q, then advance the pointer. |
ee.ld.128.usar.ip, ee.ld.128.usar.xp | qa, as, imm | at | Load 128 bits and set SAR_BYTE from the low address bits (for unaligned streams). |
ld.qr, st.qr | qa, as, imm | Load or store a q register at [as + offset] (offset a signed multiple of 16 from -128 to 112). |
mv.qr | qa, qb | Move a q register. |
Accumulator loads and stores, moves
| Instructions | Operands | What they do |
|---|---|---|
ee.ld.accx.ip, ee.st.accx.ip | as, imm | Load or store the ACCX accumulator, then advance the pointer (offset a multiple of 8). |
ee.ld.qacc_h.h.32.ip, ee.ld.qacc_h.l.128.ip, ee.ld.qacc_l.h.32.ip, ee.ld.qacc_l.l.128.ip, ee.st.qacc_h.h.32.ip, ee.st.qacc_h.l.128.ip, ee.st.qacc_l.h.32.ip, ee.st.qacc_l.l.128.ip | as, imm | Load or store a part of QACC (128 or 32 bits of its high or low half). |
ee.ld.ua_state.ip, ee.st.ua_state.ip | as, imm | Load or store the unaligned-load state. |
ee.ldqa.s16.128.ip, ee.ldqa.s16.128.xp, ee.ldqa.s8.128.ip, ee.ldqa.s8.128.xp, ee.ldqa.u16.128.ip, ee.ldqa.u16.128.xp, ee.ldqa.u8.128.ip, ee.ldqa.u8.128.xp | as, imm | at | Load 128 bits into QACC, widening 8- or 16-bit elements (signed or unsigned), then advance the pointer. |
ee.zero.accx, ee.zero.qacc, ee.zero.q | [qa] | Clear ACCX, QACC or a q register. |
ee.mov.s16.qacc, ee.mov.s8.qacc, ee.mov.u16.qacc, ee.mov.u8.qacc | qa | Move QACC into a q register, saturated to 8 or 16 bits. |
ee.srcmb.s16.qacc, ee.srcmb.s8.qacc | qa, as, sel | Shift QACC right by as and pack it into q as signed 16- or 8-bit values. |
ee.srs.accx | ad, as, sel | Shift ACCX right by as and put the result in an address register. |
ee.movi.32.a, ee.movi.32.q | qa, as, lane | Move a 32-bit lane between a q register and an address register. |
ee.bitrev | qa, as | Bit-reversed address step for the FFT. |
ee.fft.vst.r32.decp | qa, as, sel | FFT: store q as 32-bit values, then decrement the pointer. |
Logic, shifts and rearrangement
| Instructions | Operands | What they do |
|---|---|---|
ee.andq, ee.orq, ee.xorq | qa, qb, qc | Bitwise AND, OR, exclusive OR of two q registers. |
ee.notq | qa, qb | Bitwise NOT. |
ee.vsl.32, ee.vsr.32 | qa, qb | Shift the 32-bit lanes left or right by SAR. |
ee.slci.2q, ee.srci.2q | qa, qb, imm | Shift a pair of q registers left or right as one 256-bit value by an immediate. |
ee.slcxxp.2q, ee.srcxxp.2q | qa, qb, as, ad | The same with the amount in a register, and a pointer update. |
ee.src.q, ee.src.q.qup | qa, qb, qc | Funnel shift of two q registers by SAR_BYTE (for unaligned data); the second form also moves the state. |
ee.srcq.128.st.incp | qa, qb, as | Funnel shift and store, then increment the pointer. |
ee.vunzip.8, ee.vunzip.16, ee.vunzip.32, ee.vzip.8, ee.vzip.16, ee.vzip.32 | qa, qb | Unzip or zip (interleave) the lanes of two registers. |
Lane arithmetic
| Instructions | Operands | What they do |
|---|---|---|
ee.vadds.s8, ee.vadds.s16, ee.vadds.s32, ee.vsubs.s8, ee.vsubs.s16, ee.vsubs.s32 | qa, qb, qc | Saturating add or subtract of signed lanes. |
ee.vmax.s8, ee.vmax.s16, ee.vmax.s32, ee.vmin.s8, ee.vmin.s16, ee.vmin.s32 | qa, qb, qc | Maximum or minimum of signed lanes. |
ee.vcmp.eq.s8, ee.vcmp.eq.s16, ee.vcmp.eq.s32, ee.vcmp.gt.s8, ee.vcmp.gt.s16, ee.vcmp.gt.s32, ee.vcmp.lt.s8, ee.vcmp.lt.s16, ee.vcmp.lt.s32 | qa, qb, qc | Compare lanes; a lane becomes all ones where the test holds. |
ee.vmul.s8, ee.vmul.s16, ee.vmul.u8, ee.vmul.u16 | qa, qb, qc | Multiply lanes. |
ee.cmul.s16 | qa, qb, qc, sel | Complex multiply of 16-bit lanes. |
ee.vprelu.s8, ee.vprelu.s16 | qa, qb, qc, as | Parametric ReLU with a shift in an address register. |
ee.vrelu.s8, ee.vrelu.s16 | qa, ad, as | ReLU with a threshold and a shift in address registers. |
ee.fft.r2bf.s16 | qa, qb, qc, qd, sel | FFT radix-2 butterfly on signed 16-bit lanes. |
Multiply-accumulate
The .accx forms add the sum of the lane products to ACCX; the .qacc forms accumulate each lane into
QACC. The 32-bit forms do the multiply and a load in one instruction.
| Instructions | Operands | What they do |
|---|---|---|
ee.vmulas.s16.accx, ee.vmulas.s8.accx, ee.vmulas.u16.accx, ee.vmulas.u8.accx, ee.vmulas.s16.qacc, ee.vmulas.s8.qacc, ee.vmulas.u16.qacc, ee.vmulas.u8.qacc | qa, qb | Multiply lanes and accumulate. |
ee.vsmulas.s16.qacc, ee.vsmulas.s8.qacc | qa, qb, sel | Multiply by one selected lane of qb and accumulate into QACC. |
ee.vmulas.s16.qacc.ldbc.incp, ee.vmulas.s8.qacc.ldbc.incp, ee.vmulas.u16.qacc.ldbc.incp, ee.vmulas.u8.qacc.ldbc.incp | qa, as, qb, qc | Accumulate into QACC while loading and broadcasting a word, then increment the pointer (24-bit). |
ee.vmulas.s16.accx.ld.ip, ee.vmulas.s8.accx.ld.ip, ee.vmulas.u16.accx.ld.ip, ee.vmulas.u8.accx.ld.ip, ee.vmulas.s16.qacc.ld.ip, ee.vmulas.s8.qacc.ld.ip, ee.vmulas.u16.qacc.ld.ip, ee.vmulas.u8.qacc.ld.ip | qa, as, imm, qb, qc | Multiply-accumulate and load q from [as], then add the immediate (a multiple of 16) to as (32-bit). |
ee.vmulas.s16.accx.ld.xp, ee.vmulas.s8.accx.ld.xp, ee.vmulas.u16.accx.ld.xp, ee.vmulas.u8.accx.ld.xp, ee.vmulas.s16.qacc.ld.xp, ee.vmulas.s8.qacc.ld.xp, ee.vmulas.u16.qacc.ld.xp, ee.vmulas.u8.qacc.ld.xp | qa, as, ad, qb, qc | The same, adding a register to the pointer. |
ee.vmulas.s16.accx.ld.ip.qup, ee.vmulas.s8.accx.ld.ip.qup, ee.vmulas.u16.accx.ld.ip.qup, ee.vmulas.u8.accx.ld.ip.qup, ee.vmulas.s16.qacc.ld.ip.qup, ee.vmulas.s8.qacc.ld.ip.qup, ee.vmulas.u16.qacc.ld.ip.qup, ee.vmulas.u8.qacc.ld.ip.qup | qa, as, imm, qb, qc, qd, qe | Multiply-accumulate, load, move the unaligned-load state, add the immediate to the pointer (32-bit, seven operands). |
ee.vmulas.s16.accx.ld.xp.qup, ee.vmulas.s8.accx.ld.xp.qup, ee.vmulas.u16.accx.ld.xp.qup, ee.vmulas.u8.accx.ld.xp.qup, ee.vmulas.s16.qacc.ld.xp.qup, ee.vmulas.s8.qacc.ld.xp.qup, ee.vmulas.u16.qacc.ld.xp.qup, ee.vmulas.u8.qacc.ld.xp.qup | qa, as, ad, qb, qc, qd, qe | The same with a register added to the pointer. |
ee.vmulas.s16.qacc.ldbc.incp.qup, ee.vmulas.s8.qacc.ldbc.incp.qup, ee.vmulas.u16.qacc.ldbc.incp.qup, ee.vmulas.u8.qacc.ldbc.incp.qup | qa, as, qb, qc, qd, qe | Accumulate into QACC while loading and broadcasting, with the unaligned-load state move. |
ee.vsmulas.s8.qacc.ld.incp, ee.vsmulas.s16.qacc.ld.incp | qa, as, qb, qc, sel | Multiply by a selected lane, accumulate and load (32-bit). |
Lane arithmetic fused with a load or a store (32-bit)
| Instructions | Operands | What they do |
|---|---|---|
ee.vadds.s8.ld.incp, ee.vadds.s16.ld.incp, ee.vadds.s32.ld.incp, ee.vsubs.s8.ld.incp, ee.vsubs.s16.ld.incp, ee.vsubs.s32.ld.incp, ee.vmax.s8.ld.incp, ee.vmax.s16.ld.incp, ee.vmax.s32.ld.incp, ee.vmin.s8.ld.incp, ee.vmin.s16.ld.incp, ee.vmin.s32.ld.incp, ee.vmul.s8.ld.incp, ee.vmul.s16.ld.incp, ee.vmul.u8.ld.incp, ee.vmul.u16.ld.incp | qa, as, qb, qc, qd | Do the lane operation and load a q register, then increment the pointer by 16. |
ee.vadds.s8.st.incp, ee.vadds.s16.st.incp, ee.vadds.s32.st.incp, ee.vsubs.s8.st.incp, ee.vsubs.s16.st.incp, ee.vsubs.s32.st.incp, ee.vmax.s8.st.incp, ee.vmax.s16.st.incp, ee.vmax.s32.st.incp, ee.vmin.s8.st.incp, ee.vmin.s16.st.incp, ee.vmin.s32.st.incp, ee.vmul.s8.st.incp, ee.vmul.s16.st.incp, ee.vmul.u8.st.incp, ee.vmul.u16.st.incp | qa, as, qb, qc, qd | Do the lane operation and store a q register, then increment the pointer. |
ee.cmul.s16.ld.incp, ee.cmul.s16.st.incp | qa, as, qb, qc, qd, sel | Complex multiply with a load or a store. |
ee.ldxq.32, ee.stxq.32 | qa, qb, as, sel, sel | Load into or store from one 32-bit lane, addressed by a register and a lane index. |
ee.src.q.ld.ip, ee.src.q.ld.xp | qa, as, imm | ad, qb, qc | Funnel shift and load a q register, then advance the pointer. |
FFT and floating-point helpers
| Instructions | Operands | What they do |
|---|---|---|
ee.fft.ams.s16.ld.incp, ee.fft.ams.s16.ld.incp.uaup, ee.fft.ams.s16.ld.r32.decp, ee.fft.ams.s16.st.incp | seven q registers, a pointer, a selector | FFT add-multiply-subtract step on signed 16-bit lanes, with a load or a store. |
ee.fft.cmul.s16.ld.xp, ee.fft.cmul.s16.st.xp | q registers, two address registers, selectors | FFT complex multiply with a load or a store, then add a register to the pointer. |
ee.fft.r2bf.s16.st.incp | qa, qb, qc, as, sel | FFT butterfly with a store. |
ee.ldf.64.ip, ee.ldf.64.xp, ee.stf.64.ip, ee.stf.64.xp | fa, fb, as, imm | ad | Load or store two floating-point registers (64 bits) and advance the pointer. |
ee.ldf.128.ip, ee.ldf.128.xp, ee.stf.128.ip, ee.stf.128.xp | fa, fb, fc, fd, as, imm | ad | Load or store four floating-point registers (128 bits) and advance the pointer. |
GPIO
| Instruction | Operands | What it does |
|---|---|---|
ee.clr_bit_gpio_out, ee.set_bit_gpio_out | bit | Clear or set a bit of the GPIO output register (an 8-bit immediate). |
ee.wr_mask_gpio_out | as, at | Write the GPIO output register under a mask. |
ee.get_gpio_in | at | Read the GPIO input pins. |
Not described
Known, not yet covered
- Reference and viewer: 27 of the 217 mnemonics of the S3 configuration, all rare 32-bit forms: they decode as reserved encodings.
- Reference: what each instruction computes exactly (saturation, rounding, the meaning of the selector operands, the state in
UA_STATEandSAR_BYTE).
Unknown
- Reference: Espressif's own description of these instructions was not read; the one-line descriptions come from the mnemonics and the operand shapes, and are hedged accordingly.