31.6-3V V: vsetvli & the Load/Store Encoding

Part III Linux boot: recommended Vol. I (Unprivileged) pp. 297–304 · ~3 min read

  • strip mining

The configuration instructions

vsetvli rd, rs1, vtypei / vsetivli rd, uimm, vtypei / vsetvl rd, rs1, rs2 (the register form, for context restore) are the only writers of vl and vtype. Assembly spells the config out: vsetvli t0, a0, e32, m4, ta, ma. The rd/rs1 fields encode intent:

Table 49 — AVL encoding, plus the vl constraint rules
Meaning
rs1 ≠ x0AVL = x[rs1]; new vl also written to rd. The strip-mining workhorse.
rs1 = x0, rd ≠ x0AVL = ~0 → vl = VLMAX, returned in rd — “configure for everything, tell me how much that is.”
rs1 = x0, rd = x0Keep current vl, change vtype only.
vl constraintsvl = AVL if AVL ≤ VLMAX; ceil(AVL/2) ≤ vl ≤ VLMAX if AVL < 2·VLMAX (implementations MAY balance the last two strips); vl = VLMAX beyond; deterministic per implementation; vl > 0 iff AVL > 0.
Unsupported vtypeNo trap: vill set, everything else zeroed (vl too). Software probes support by writing and branching on the sign bit. ALL XLEN bits of a vsetvl argument must be validated.
Dotted-underlined cells have explanations — click one.

The canonical strip-mine loop:

# a0 = n, a1 = src, a2 = dst  (32-bit elements)
loop:
    vsetvli t0, a0, e32, m1, ta, ma   # vl = this strip's length
    vle32.v v8, (a1)                  # load strip
    vadd.vi v8, v8, 1                 # compute
    vse32.v v8, (a2)                  # store strip
    sub     a0, a0, t0                # n -= vl
    sh2add  a1, t0, a1                # advance by vl elements
    sh2add  a2, t0, a2
    bnez    a0, loop

Load/store encoding: squatting in LOAD-FP

Vector memory ops reuse LOAD-FP/STORE-FP, carving the FP immediate into fields: nf[31:29] (segment field count), mew[28] + width[14:12] (the element size: 8/16/32/64, values disjoint from scalar FP widths), mop[27:26] (addressing mode), vm[25], then rs2/vs2/lumop in bits 24:20:

mop[1:0] addressing modes
Address generation
00 — unit-strideContiguous from x[rs1]; lumop/sumop sub-select: 00000 plain, 01000 whole-register, 01011 mask load/store, 10000 fault-only-first.
10 — constant strideElement i at x[rs1] + i · x[rs2] (byte stride; may be zero or negative).
01 / 11 — indexed unordered / ORDEREDElement address = x[rs1] + vs2[i], offsets in bytes, zero-extended to XLEN, NOT scaled by element size.
Dotted-underlined cells have explanations — click one.

Unit-stride and strided ops encode the data EEW statically — mixed-width memory routines skip half their vsetvli traffic; indexed ops spend that field on the index width instead.

Hardware Designer Notes

Unit-stride bandwidth IS your vector performance for most real kernels — memcpy, strlen, GEMM edges. Build the unit-stride path as wide as the cache interface allows and let strided/indexed take the slow lane initially.

Minimal Linux-boot hart MUST

  • Validate every bit of vtype candidates (vill on anything unknown) and implement the three AVL forms including the keep-vl reservation
  • Decode the LOAD-FP/STORE-FP overlay exactly — scalar FLW/FLD must still decode beside vle32/vle64 (width values are disjoint)
  • Zero-extend sub-XLEN index elements and truncate super-XLEN ones in the indexed-address adders

MAY simplify / trap-and-emulate

  • Fuse vsetvli+first-vector-op; treat the AVL<2·VLMAX balancing freedom as a scheduling tool (splitting the last two strips evenly)
  • Crack strided/indexed ops into per-element micro-ops on simple cores; unit-stride deserves a real wide path

Check yourself — vsetvli & LS encoding

1.Write the canonical strip-mine loop header for processing a0 elements of 32-bit data.

2.What do the rd/rs1 = x0 special forms of vsetvli mean?

3.Writing an unsupported vtype value does what — and why not trap?

4.In an indexed vector load, which operand's width comes from the instruction and which from vtype?

4 questions