31.3V II: The Programmer's Model — CSRs, vtype, LMUL

Part III Linux boot: recommended Vol. I (Unprivileged) pp. 279–287 · ~4 min read

  • lmul
  • sew

The extension adds 32 vector registers (v0–v31, VLEN bits each) and seven unprivileged CSRs. Context status rides in mstatus.VS [10:9] — an exact FS analog: Off traps everything, vector-state changes dirty it, and under the H extension vsstatus.VS gates in parallel.

The seven vector CSRs
Role
vtype (0xC21, RO)The interpretation contract: SEW, LMUL, tail/mask policies, vill. Writable ONLY via vset{i}vl{i}.
vl (0xC20, RO)Elements each instruction updates. Written only by vset{i}vl{i} and fault-only-first loads.
vlenb (0xC22, RO)VLEN/8 — a design constant, readable without disturbing vl/vtype (context-save sizing).
vstart (0x008, RW)Trap-resume element index: hardware writes it on a mid-vector trap; EVERY vector instruction resets it to 0. User-writable for threading libraries; apps should leave it alone.
vxrm (0x00A) / vxsat (0x009) / vcsr (0x00F)Fixed-point rounding mode (rnu/rne/rdn/rod; increment r per Table 46), sticky saturation flag, and their combined mirror.
Dotted-underlined cells have explanations — click one.

vtype — the interpretation register

vtype (0xC21) — Table 43

vill310308vma7vta6vsew53vlmul20
WPRI/RO WLRL WARL RW
Click a field for semantics, reset state, and the minimal-implementation note.

LMUL gangs registers: LMUL=2 pairs v2n, v2n+1, LMUL=4 takes multiples of four, LMUL=8 multiples of eight — misaligned bases are reserved encodings. Its raison d’être is mixed-width math: a widening add producing 2× SEW results needs 2×LMUL registers to keep the same element count. Fractional LMUL (1/2, 1/4, 1/8) parks narrow operands in a fraction of one register so a mixed-width loop can still name 32 distinct operands. Implementations must support LMUL ≥ SEWmin/ELEN (with SEW up to LMUL·ELEN at each fraction); masks always fit one register.

Tail & mask policies

Elements outside the action — the tail (≥ vl) and inactive (masked-off) sets — follow vta/vma: undisturbed keeps old values; agnostic leaves each element old or all-1s, non-deterministically. The ta/tu + ma/mu flags are mandatory in vsetvli assembly.

At reset: vill set, rest of vtype zero, vl zero (recommended) — a single vsetvl can save/restore the pair. vxrm/vxsat/vstart and the vector registers may hold garbage; the initial vsetvli sorts everything but vxrm/vxsat, which software resets explicitly.

Hardware Designer Notes

vtype/vl renaming is the key perf decision: unfused, every vsetvli serializes the pipeline. Treat the pair as a speculatively renamed operand and strip-mine loops run at full issue rate.

Minimal Linux-boot hart MUST

  • Implement all four ta/ma combinations (accepting them is mandatory even if you execute everything undisturbed)
  • Reset vstart to zero at the end of EVERY vector instruction including vset{i}vl{i}, and write the faulting element index on resumable traps
  • Keep vill in the sign bit with the all-other-bits-zero rule, and bounce vtype-dependent instructions when it’s set

MAY simplify / trap-and-emulate

  • Trap nonzero-vstart arithmetic if your pipeline never produces it (no mid-instruction interrupts on arithmetic)
  • Store vtype in ~7 physical bits for an ELEN=32 embedded core (vill encoded in the reserved vsew pattern)
  • Rename vl/vtype as one internal control register and fuse vsetvli into the consuming vector op

Check yourself — vector programmer's model

1.Why can vtype only be written by vset{i}vl{i} instructions, never by CSR writes?

2.With VLEN=128, SEW=32, LMUL=4, what is VLMAX and which registers form the group starting at v8?

3.What may an 'agnostic' tail element contain after an instruction executes, and why so loose?

4.What is vstart for, and why does every vector instruction reset it to zero?

4 questions