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:
| Meaning | |
|---|---|
| rs1 ≠ x0 | AVL = x[rs1]; new vl also written to rd. The strip-mining workhorse. |
| rs1 = x0, rd ≠ x0 | AVL = ~0 → vl = VLMAX, returned in rd — “configure for everything, tell me how much that is.” |
| rs1 = x0, rd = x0 | Keep current vl, change vtype only. |
| vl constraints | vl = 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 vtype | No 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. |
The canonical strip-mine strip mining The V-extension loop idiom: each iteration asks vsetvli for vl = min(remaining, VLMAX), processes vl elements, advances pointers by vl, repeats — handling any application vector length without remainder loops. defined in ch. I·31 — open in glossary 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:
| Address generation | |
|---|---|
| 00 — unit-stride | Contiguous from x[rs1]; lumop/sumop sub-select: 00000 plain, 01000 whole-register, 01011 mask load/store, 10000 fault-only-first. |
| 10 — constant stride | Element i at x[rs1] + i · x[rs2] (byte stride; may be zero or negative). |
| 01 / 11 — indexed unordered / ORDERED | Element address = x[rs1] + vs2[i], offsets in bytes, zero-extended to XLEN, NOT scaled by element size. |
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?