1 # Simple-V (Parallelism Extension Proposal) Specification
3 * Copyright (C) 2017, 2018, 2019 Luke Kenneth Casson Leighton
5 * Last edited: 21 jun 2019
6 * Ancillary resource: [[opcodes]]
7 * Ancillary resource: [[sv_prefix_proposal]]
8 * Ancillary resource: [[abridged_spec]]
9 * Ancillary resource: [[vblock_format]]
20 * The RISC-V Founders, without whom this all would not be possible.
24 # Summary and Background: Rationale
26 Simple-V is a uniform parallelism API for RISC-V hardware that has several
27 unplanned side-effects including code-size reduction, expansion of
28 HINT space and more. The reason for
29 creating it is to provide a manageable way to turn a pre-existing design
30 into a parallel one, in a step-by-step incremental fashion, without adding any new opcodes, thus allowing
31 the implementor to focus on adding hardware where it is needed and necessary.
32 The primary target is for mobile-class 3D GPUs and VPUs, with secondary
33 goals being to reduce executable size (by extending the effectiveness of RV opcodes, RVC in particular) and reduce context-switch latency.
35 Critically: **No new instructions are added**. The parallelism (if any
36 is implemented) is implicitly added by tagging *standard* scalar registers
37 for redirection. When such a tagged register is used in any instruction,
38 it indicates that the PC shall **not** be incremented; instead a loop
39 is activated where *multiple* instructions are issued to the pipeline
40 (as determined by a length CSR), with contiguously incrementing register
41 numbers starting from the tagged register. When the last "element"
42 has been reached, only then is the PC permitted to move on. Thus
43 Simple-V effectively sits (slots) *in between* the instruction decode phase
46 The barrier to entry with SV is therefore very low. The minimum
47 compliant implementation is software-emulation (traps), requiring
48 only the CSRs and CSR tables, and that an exception be thrown if an
49 instruction's registers are detected to have been tagged. The looping
50 that would otherwise be done in hardware is thus carried out in software,
51 instead. Whilst much slower, it is "compliant" with the SV specification,
52 and may be suited for implementation in RV32E and also in situations
53 where the implementor wishes to focus on certain aspects of SV, without
54 unnecessary time and resources into the silicon, whilst also conforming
55 strictly with the API. A good area to punt to software would be the
56 polymorphic element width capability for example.
58 Hardware Parallelism, if any, is therefore added at the implementor's
59 discretion to turn what would otherwise be a sequential loop into a
62 To emphasise that clearly: Simple-V (SV) is *not*:
66 * A Vectorisation Microarchitecture
67 * A microarchitecture of any specific kind
68 * A mandary parallel processor microarchitecture of any kind
69 * A supercomputer extension
71 SV does **not** tell implementors how or even if they should implement
72 parallelism: it is a hardware "API" (Application Programming Interface)
73 that, if implemented, presents a uniform and consistent way to *express*
74 parallelism, at the same time leaving the choice of if, how, how much,
75 when and whether to parallelise operations **entirely to the implementor**.
79 The principle of SV is as follows:
81 * Standard RV instructions are "prefixed" (extended) through a 48/64
82 bit format (single instruction option) or a variable
83 length VLIW-like prefix (multi or "grouped" option).
84 * The prefix(es) indicate which registers are "tagged" as
85 "vectorised". Predicates can also be added, and element widths
86 overridden on any src or dest register.
87 * A "Vector Length" CSR is set, indicating the span of any future
88 "parallel" operations.
89 * If any operation (a **scalar** standard RV opcode) uses a register
90 that has been so "marked" ("tagged"), a hardware "macro-unrolling loop"
91 is activated, of length VL, that effectively issues **multiple**
92 identical instructions using contiguous sequentially-incrementing
93 register numbers, based on the "tags".
94 * **Whether they be executed sequentially or in parallel or a
95 mixture of both or punted to software-emulation in a trap handler
96 is entirely up to the implementor**.
98 In this way an entire scalar algorithm may be vectorised with
99 the minimum of modification to the hardware and to compiler toolchains.
101 To reiterate: **There are *no* new opcodes**. The scheme works *entirely*
102 on hidden context that augments *scalar* RISCV instructions.
104 # CSRs <a name="csrs"></a>
106 * An optional "reshaping" CSR key-value table which remaps from a 1D
107 linear shape to 2D or 3D, including full transposition.
109 There are five additional CSRs, available in any privilege level:
111 * MVL (the Maximum Vector Length)
112 * VL (which has different characteristics from standard CSRs)
113 * SUBVL (effectively a kind of SIMD)
114 * STATE (containing copies of MVL, VL and SUBVL as well as context information)
115 * PCVBLK (the current operation being executed within a VBLOCK Group)
117 For User Mode there are the following CSRs:
119 * uePCVBLK (a copy of the sub-execution Program Counter, that is relative
120 to the start of the current VBLOCK Group, set on a trap).
121 * ueSTATE (useful for saving and restoring during context switch,
122 and for providing fast transitions)
124 There are also two additional CSRs for Supervisor-Mode:
129 And likewise for M-Mode:
134 The u/m/s CSRs are treated and handled exactly like their (x)epc
135 equivalents. On entry to or exit from a privilege level, the contents of its (x)eSTATE are swapped with STATE.
137 Thus for example, a User Mode trap will end up swapping STATE and ueSTATE
138 (on both entry and exit), allowing User Mode traps to have their own
139 Vectorisation Context set up, separated from and unaffected by normal
140 user applications. If an M Mode trap occurs in the middle of the U Mode trap, STATE is swapped with meSTATE, and restored on exit: the U Mode trap continues unaware that the M Mode trap even occurred.
142 Likewise, Supervisor Mode may perform context-switches, safe in the
143 knowledge that its Vectorisation State is unaffected by User Mode.
145 The access pattern for these groups of CSRs in each mode follows the
146 same pattern for other CSRs that have M-Mode and S-Mode "mirrors":
148 * In M-Mode, the S-Mode and U-Mode CSRs are separate and distinct.
149 * In S-Mode, accessing and changing of the M-Mode CSRs is transparently
151 to changing the S-Mode CSRs. Accessing and changing the U-Mode
153 * In U-Mode, accessing and changing of the S-Mode and U-Mode CSRs
156 An interesting side effect of SV STATE being
157 separate and distinct in S Mode
159 Vectorised saving of an entire register file to the stack is a single
160 instruction (through accidental provision of LOAD-MULTI semantics). If the
161 SVPrefix P64-LD-type format is used, LOAD-MULTI may even be done with a
162 single standalone 64 bit opcode (P64 may set up SUBVL, VL and MVL from an
163 immediate field, to cover the full regfile). It can even be predicated, which opens up some very
164 interesting possibilities.
166 (x)EPCVBLK CSRs must be treated exactly like their corresponding (x)epc
167 equivalents. See VBLOCK section for details.
169 ## MAXVECTORLENGTH (MVL) <a name="mvl" />
171 MAXVECTORLENGTH is the same concept as MVL in RVV, except that it
172 is variable length and may be dynamically set. MVL is
173 however limited to the regfile bitwidth XLEN (1-32 for RV32,
174 1-64 for RV64 and so on).
176 The reason for setting this limit is so that predication registers, when
177 marked as such, may fit into a single register as opposed to fanning
178 out over several registers. This keeps the hardware implementation a
181 The other important factor to note is that the actual MVL is internally
182 stored **offset by one**, so that it can fit into only 6 bits (for RV64)
183 and still cover a range up to XLEN bits. Attempts to set MVL to zero will
184 return an exception. This is expressed more clearly in the "pseudocode"
185 section, where there are subtle differences between CSRRW and CSRRWI.
187 ## Vector Length (VL) <a name="vl" />
189 VSETVL is slightly different from RVV. Similar to RVV, VL is set to be within
190 the range 1 <= VL <= MVL (where MVL in turn is limited to 1 <= MVL <= XLEN)
192 VL = rd = MIN(vlen, MVL)
194 where 1 <= MVL <= XLEN
196 However just like MVL it is important to note that the range for VL has
197 subtle design implications, covered in the "CSR pseudocode" section
199 The fixed (specific) setting of VL allows vector LOAD/STORE to be used
200 to switch the entire bank of registers using a single instruction (see
201 Appendix, "Context Switch Example"). The reason for limiting VL to XLEN
202 is down to the fact that predication bits fit into a single register of
205 The second and most important change is that, within the limits set by
206 MVL, the value passed in **must** be set in VL (and in the
207 destination register).
209 This has implication for the microarchitecture, as VL is required to be
210 set (limits from MVL notwithstanding) to the actual value
211 requested. RVV has the option to set VL to an arbitrary value that suits
212 the conditions and the micro-architecture: SV does *not* permit this.
214 The reason is so that if SV is to be used for a context-switch or as a
215 substitute for LOAD/STORE-Multiple, the operation can be done with only
216 2-3 instructions (setup of the CSRs, VSETVL x0, x0, #{regfilelen-1},
217 single LD/ST operation). If VL does *not* get set to the register file
218 length when VSETVL is called, then a software-loop would be needed.
219 To avoid this need, VL *must* be set to exactly what is requested
220 (limits notwithstanding).
222 Therefore, in turn, unlike RVV, implementors *must* provide
223 pseudo-parallelism (using sequential loops in hardware) if actual
224 hardware-parallelism in the ALUs is not deployed. A hybrid is also
225 permitted (as used in Broadcom's VideoCore-IV) however this must be
226 *entirely* transparent to the ISA.
228 The third change is that VSETVL is implemented as a CSR, where the
229 behaviour of CSRRW (and CSRRWI) must be changed to specifically store
230 the *new* value in the destination register, **not** the old value.
231 Where context-load/save is to be implemented in the usual fashion
232 by using a single CSRRW instruction to obtain the old value, the
233 *secondary* CSR must be used (STATE). This CSR by contrast behaves
234 exactly as standard CSRs, and contains more than just VL.
236 One interesting side-effect of using CSRRWI to set VL is that this
237 may be done with a single instruction, useful particularly for a
238 context-load/save. There are however limitations: CSRWI's immediate
239 is limited to 0-31 (representing VL=1-32).
241 Note that when VL is set to 1, vector operations cease (but not subvector
242 operations: that requires setting SUBVL=1) the hardware loop is reduced
243 to a single element: scalar operations. This is in effect the default,
244 normal operating mode. However it is important to appreciate that this
245 does **not** result in the Register table or SUBVL being disabled. Only
246 when the Register table is empty (P48/64 prefix fields notwithstanding)
247 would SV have no effect.
249 ## SUBVL - Sub Vector Length
251 This is a "group by quantity" that effectivrly asks each iteration
252 of the hardware loop to load SUBVL elements of width elwidth at a
253 time. Effectively, SUBVL is like a SIMD multiplier: instead of just 1
254 operation issued, SUBVL operations are issued.
256 Another way to view SUBVL is that each element in the VL length vector is
257 now SUBVL times elwidth bits in length and now comprises SUBVL discrete
258 sub operations. An inner SUBVL for-loop within a VL for-loop in effect,
259 with the sub-element increased every time in the innermost loop. This
260 is best illustrated in the (simplified) pseudocode example, later.
262 The primary use case for SUBVL is for 3D FP Vectors. A Vector of 3D
263 coordinates X,Y,Z for example may be loaded and multiplied the stored, per
264 VL element iteration, rather than having to set VL to three times larger.
266 Legal values are 1, 2, 3 and 4 (and the STATE CSR must hold the 2 bit
267 values 0b00 thru 0b11 to represent them).
269 Setting this CSR to 0 must raise an exception. Setting it to a value
270 greater than 4 likewise.
272 The main effect of SUBVL is that predication bits are applied per
273 **group**, rather than by individual element.
275 This saves a not insignificant number of instructions when handling 3D
276 vectors, as otherwise a much longer predicate mask would have to be set
277 up with regularly-repeated bit patterns.
279 See SUBVL Pseudocode illustration for details.
283 This is a standard CSR that contains sufficient information for a
284 full context save/restore. It contains (and permits setting of):
288 * destoffs - the destination element offset of the current parallel
289 instruction being executed
290 * srcoffs - for twin-predication, the source element offset as well.
292 * svdestoffs - the subvector destination element offset of the current
293 parallel instruction being executed
294 * svsrcoffs - for twin-predication, the subvector source element offset
297 Interestingly STATE may hypothetically also be modified to make the
298 immediately-following instruction to skip a certain number of elements,
299 by playing with destoffs and srcoffs (and the subvector offsets as well)
301 Setting destoffs and srcoffs is realistically intended for saving state
302 so that exceptions (page faults in particular) may be serviced and the
303 hardware-loop that was being executed at the time of the trap, from
304 user-mode (or Supervisor-mode), may be returned to and continued from
305 exactly where it left off. The reason why this works is because setting
306 User-Mode STATE will not change (not be used) in M-Mode or S-Mode (and
307 is entirely why M-Mode and S-Mode have their own STATE CSRs, meSTATE
310 The format of the STATE CSR is as follows:
312 | (29..28 | (27..26) | (25..24) | (23..18) | (17..12) | (11..6) | (5...0) |
313 | ------- | -------- | -------- | -------- | -------- | ------- | ------- |
314 | dsvoffs | ssvoffs | subvl | destoffs | srcoffs | vl | maxvl |
316 When setting this CSR, the following characteristics will be enforced:
318 * **MAXVL** will be truncated (after offset) to be within the range 1 to XLEN
319 * **VL** will be truncated (after offset) to be within the range 1 to MAXVL
320 * **SUBVL** which sets a SIMD-like quantity, has only 4 values so there
321 are no changes needed
322 * **srcoffs** will be truncated to be within the range 0 to VL-1
323 * **destoffs** will be truncated to be within the range 0 to VL-1
324 * **ssvoffs** will be truncated to be within the range 0 to SUBVL-1
325 * **dsvoffs** will be truncated to be within the range 0 to SUBVL-1
327 NOTE: if the following instruction is not a twin predicated instruction,
328 and destoffs or dsvoffs has been set to non-zero, subsequent execution
329 behaviour is undefined. **USE WITH CARE**.
331 ### Hardware rules for when to increment STATE offsets
333 The offsets inside STATE are like the indices in a loop, except
334 in hardware. They are also partially (conceptually) similar to a
335 "sub-execution Program Counter". As such, and to allow proper context
336 switching and to define correct exception behaviour, the following rules
339 * When the VL CSR is set, srcoffs and destoffs are reset to zero.
340 * Each instruction that contains a "tagged" register shall start
341 execution at the *current* value of srcoffs (and destoffs in the case
343 * Unpredicated bits (in nonzeroing mode) shall cause the element operation
344 to skip, incrementing the srcoffs (or destoffs)
345 * On execution of an element operation, Exceptions shall **NOT** cause
346 srcoffs or destoffs to increment.
347 * On completion of the full Vector Loop (srcoffs = VL-1 or destoffs =
348 VL-1 after the last element is executed), both srcoffs and destoffs
349 shall be reset to zero.
351 This latter is why srcoffs and destoffs may be stored as values from
352 0 to XLEN-1 in the STATE CSR, because as loop indices they refer to
353 elements. srcoffs and destoffs never need to be set to VL: their maximum
354 operating values are limited to 0 to VL-1.
356 The same corresponding rules apply to SUBVL, svsrcoffs and svdestoffs.
358 ## MVL and VL Pseudocode
360 The pseudo-code for get and set of VL and MVL use the following internal
361 functions as follows:
363 set_mvl_csr(value, rd):
365 STATE.MVL = MIN(value, STATE.MVL)
370 set_vl_csr(value, rd):
371 STATE.VL = MIN(value, STATE.MVL)
372 regs[rd] = STATE.VL # yes returning the new value NOT the old CSR
379 Note that where setting MVL behaves as a normal CSR (returns the old
380 value), unlike standard CSR behaviour, setting VL will return the **new**
381 value of VL **not** the old one.
383 For CSRRWI, the range of the immediate is restricted to 5 bits. In order to
384 maximise the effectiveness, an immediate of 0 is used to set VL=1,
385 an immediate of 1 is used to set VL=2 and so on:
387 CSRRWI_Set_MVL(value):
388 set_mvl_csr(value+1, x0)
390 CSRRWI_Set_VL(value):
391 set_vl_csr(value+1, x0)
393 However for CSRRW the following pseudocode is used for MVL and VL,
394 where setting the value to zero will cause an exception to be raised.
395 The reason is that if VL or MVL are set to zero, the STATE CSR is
396 not capable of storing that value.
398 CSRRW_Set_MVL(rs1, rd):
400 if value == 0 or value > XLEN:
402 set_mvl_csr(value, rd)
404 CSRRW_Set_VL(rs1, rd):
406 if value == 0 or value > XLEN:
408 set_vl_csr(value, rd)
410 In this way, when CSRRW is utilised with a loop variable, the value
411 that goes into VL (and into the destination register) may be used
412 in an instruction-minimal fashion:
414 CSRvect1 = {type: F, key: a3, val: a3, elwidth: dflt}
415 CSRvect2 = {type: F, key: a7, val: a7, elwidth: dflt}
416 CSRRWI MVL, 3 # sets MVL == **4** (not 3)
417 j zerotest # in case loop counter a0 already 0
419 CSRRW VL, t0, a0 # vl = t0 = min(mvl, a0)
420 ld a3, a1 # load 4 registers a3-6 from x
421 slli t1, t0, 3 # t1 = vl * 8 (in bytes)
422 ld a7, a2 # load 4 registers a7-10 from y
423 add a1, a1, t1 # increment pointer to x by vl*8
424 fmadd a7, a3, fa0, a7 # v1 += v0 * fa0 (y = a * x + y)
425 sub a0, a0, t0 # n -= vl (t0)
426 st a7, a2 # store 4 registers a7-10 to y
427 add a2, a2, t1 # increment pointer to y by vl*8
429 bnez a0, loop # repeat if n != 0
431 With the STATE CSR, just like with CSRRWI, in order to maximise the
432 utilisation of the limited bitspace, "000000" in binary represents
433 VL==1, "00001" represents VL==2 and so on (likewise for MVL):
435 CSRRW_Set_SV_STATE(rs1, rd):
438 STATE.MVL = set_mvl_csr(value[11:6]+1)
439 STATE.VL = set_vl_csr(value[5:0]+1)
440 STATE.destoffs = value[23:18]>>18
441 STATE.srcoffs = value[23:18]>>12
444 regs[rd] = (STATE.MVL-1) | (STATE.VL-1)<<6 | (STATE.srcoffs)<<12 |
448 In both cases, whilst CSR read of VL and MVL return the exact values
449 of VL and MVL respectively, reading and writing the STATE CSR returns
450 those values **minus one**. This is absolutely critical to implement
451 if the STATE CSR is to be used for fast context-switching.
453 ## VL, MVL and SUBVL instruction aliases
455 This table contains pseudo-assembly instruction aliases. Note the
456 subtraction of 1 from the CSRRWI pseudo variants, to compensate for the
457 reduced range of the 5 bit immediate.
461 | SETVL rd, rs | CSRRW VL, rd, rs |
462 | SETVLi rd, #n | CSRRWI VL, rd, #n-1 |
463 | GETVL rd | CSRRW VL, rd, x0 |
464 | SETMVL rd, rs | CSRRW MVL, rd, rs |
465 | SETMVLi rd, #n | CSRRWI MVL,rd, #n-1 |
466 | GETMVL rd | CSRRW MVL, rd, x0 |
468 Note: CSRRC and other bitsetting may still be used, they are however not particularly useful (very obscure).
470 ## Register key-value (CAM) table <a name="regcsrtable" />
472 *NOTE: in prior versions of SV, this table used to be writable and
473 accessible via CSRs. It is now stored in the VBLOCK instruction format. Note
474 that this table does *not* get applied to the SVPrefix P48/64 format,
475 only to scalar opcodes*
477 The purpose of the Register table is three-fold:
479 * To mark integer and floating-point registers as requiring "redirection"
480 if it is ever used as a source or destination in any given operation.
481 This involves a level of indirection through a 5-to-7-bit lookup table,
482 such that **unmodified** operands with 5 bits (3 for some RVC ops) may
483 access up to **128** registers.
484 * To indicate whether, after redirection through the lookup table, the
485 register is a vector (or remains a scalar).
486 * To over-ride the implicit or explicit bitwidth that the operation would
487 normally give the register.
489 Note: clearly, if an RVC operation uses a 3 bit spec'd register (x8-x15)
490 and the Register table contains entried that only refer to registerd
491 x1-x14 or x16-x31, such operations will *never* activate the VL hardware
494 If however the (16 bit) Register table does contain such an entry (x8-x15
495 or x2 in the case of LWSP), that src or dest reg may be redirected
496 anywhere to the *full* 128 register range. Thus, RVC becomes far more
497 powerful and has many more opportunities to reduce code size that in
498 Standard RV32/RV64 executables.
502 | RegCAM | | 15 | (14..8) | 7 | (6..5) | (4..0) |
503 | ------ | | - | - | - | ------ | ------- |
504 | 0 | | isvec0 | regidx0 | i/f | vew0 | regkey |
505 | 1 | | isvec1 | regidx1 | i/f | vew1 | regkey |
506 | .. | | isvec.. | regidx.. | i/f | vew.. | regkey |
507 | 15 | | isvec15 | regidx15 | i/f | vew15 | regkey |
511 | RegCAM | | 7 | (6..5) | (4..0) |
512 | ------ | | - | ------ | ------- |
513 | 0 | | i/f | vew0 | regnum |
515 Showing the mapping (relationship) between 8-bit and 16-bit format:
517 | RegCAM | 15 | (14..8) | 7 | (6..5) | (4..0) |
518 | ------ | - | - | - | ------ | ------- |
519 | 0 | isvec=1 | regnum0<<2 | i/f | vew0 | regnum0 |
520 | 1 | isvec=1 | regnum1<<2 | i/f | vew1 | regnum1 |
521 | 2 | isvec=1 | regnum2<<2 | i/f | vew2 | regnum2 |
522 | 3 | isvec=1 | regnum2<<2 | i/f | vew3 | regnum3 |
524 i/f is set to "1" to indicate that the redirection/tag entry is to
525 be applied to integer registers; 0 indicates that it is relevant to
526 floating-point registers.
528 The 8 bit format is used for a much more compact expression. "isvec"
529 is implicit and, similar to [[sv-prefix-proposal]], the target vector
530 is "regnum<<2", implicitly. Contrast this with the 16-bit format where
531 the target vector is *explicitly* named in bits 8 to 14, and bit 15 may
532 optionally set "scalar" mode.
534 Note that whilst SVPrefix adds one extra bit to each of rd, rs1 etc.,
535 and thus the "vector" mode need only shift the (6 bit) regnum by 1 to
536 get the actual (7 bit) register number to use, there is not enough space
537 in the 8 bit format (only 5 bits for regnum) so "regnum<<2" is required.
539 vew has the following meanings, indicating that the instruction's
540 operand size is "over-ridden" in a polymorphic fashion:
543 | --- | ------------------- |
544 | 00 | default (XLEN/FLEN) |
549 As the above table is a CAM (key-value store) it may be appropriate
550 (faster, implementation-wise) to expand it as follows:
552 struct vectorised fp_vec[32], int_vec[32];
554 for (i = 0; i < len; i++) // from VBLOCK Format
555 tb = int_vec if CSRvec[i].type == 0 else fp_vec
556 idx = CSRvec[i].regkey // INT/FP src/dst reg in opcode
557 tb[idx].elwidth = CSRvec[i].elwidth
558 tb[idx].regidx = CSRvec[i].regidx // indirection
559 tb[idx].isvector = CSRvec[i].isvector // 0=scalar
561 ## Predication Table <a name="predication_csr_table"></a>
563 *NOTE: in prior versions of SV, this table used to be writable and
564 accessible via CSRs. It is now stored in the VBLOCK instruction format.
565 The table does **not** apply to SVPrefix opcodes*
567 The Predication Table is a key-value store indicating whether, if a
568 given destination register (integer or floating-point) is referred to
569 in an instruction, it is to be predicated. Like the Register table, it
570 is an indirect lookup that allows the RV opcodes to not need modification.
572 It is particularly important to note
573 that the *actual* register used can be *different* from the one that is
574 in the instruction, due to the redirection through the lookup table.
576 * regidx is the register that in combination with the
577 i/f flag, if that integer or floating-point register is referred to in a
578 (standard RV) instruction results in the lookup table being referenced
579 to find the predication mask to use for this operation.
580 * predidx is the *actual* (full, 7 bit) register to be used for the
582 * inv indicates that the predication mask bits are to be inverted
583 prior to use *without* actually modifying the contents of the
584 register from which those bits originated.
585 * zeroing is either 1 or 0, and if set to 1, the operation must
586 place zeros in any element position where the predication mask is
587 set to zero. If zeroing is set to 0, unpredicated elements *must*
588 be left alone. Some microarchitectures may choose to interpret
589 this as skipping the operation entirely. Others which wish to
590 stick more closely to a SIMD architecture may choose instead to
591 interpret unpredicated elements as an internal "copy element"
592 operation (which would be necessary in SIMD microarchitectures
593 that perform register-renaming)
594 * ffirst is a special mode that stops sequential element processing when
595 a data-dependent condition occurs, whether a trap or a conditional test.
596 The handling of each (trap or conditional test) is slightly different:
597 see Instruction sections for further details
601 | PrCSR | (15..11) | 10 | 9 | 8 | (7..1) | 0 |
602 | ----- | - | - | - | - | ------- | ------- |
603 | 0 | predidx | zero0 | inv0 | i/f | regidx | ffirst0 |
604 | 1 | predidx | zero1 | inv1 | i/f | regidx | ffirst1 |
605 | 2 | predidx | zero2 | inv2 | i/f | regidx | ffirst2 |
606 | 3 | predidx | zero3 | inv3 | i/f | regidx | ffirst3 |
608 Note: predidx=x0, zero=1, inv=1 is a RESERVED encoding. Its use must
609 generate an illegal instruction trap.
613 | PrCSR | 7 | 6 | 5 | (4..0) |
614 | ----- | - | - | - | ------- |
615 | 0 | zero0 | inv0 | i/f | regnum |
617 The 8 bit format is a compact and less expressive variant of the full
618 16 bit format. Using the 8 bit formatis very different: the predicate
619 register to use is implicit, and numbering begins inplicitly from x9. The
620 regnum is still used to "activate" predication, in the same fashion as
623 Thus if we map from 8 to 16 bit format, the table becomes:
625 | PrCSR | (15..11) | 10 | 9 | 8 | (7..1) | 0 |
626 | ----- | - | - | - | - | ------- | ------- |
627 | 0 | x9 | zero0 | inv0 | i/f | regnum | ff=0 |
628 | 1 | x10 | zero1 | inv1 | i/f | regnum | ff=0 |
629 | 2 | x11 | zero2 | inv2 | i/f | regnum | ff=0 |
630 | 3 | x12 | zero3 | inv3 | i/f | regnum | ff=0 |
632 The 16 bit Predication CSR Table is a key-value store, so
633 implementation-wise it will be faster to turn the table around (maintain
634 topologically equivalent state):
637 bool zero; // zeroing
638 bool inv; // register at predidx is inverted
639 bool ffirst; // fail-on-first
640 bool enabled; // use this to tell if the table-entry is active
641 int predidx; // redirection: actual int register to use
644 struct pred fp_pred_reg[32]; // 64 in future (bank=1)
645 struct pred int_pred_reg[32]; // 64 in future (bank=1)
647 for (i = 0; i < len; i++) // number of Predication entries in VBLOCK
648 tb = int_pred_reg if PredicateTable[i].type == 0 else fp_pred_reg;
649 idx = PredicateTable[i].regidx
650 tb[idx].zero = CSRpred[i].zero
651 tb[idx].inv = CSRpred[i].inv
652 tb[idx].ffirst = CSRpred[i].ffirst
653 tb[idx].predidx = CSRpred[i].predidx
654 tb[idx].enabled = true
656 So when an operation is to be predicated, it is the internal state that
657 is used. In Section 6.4.2 of Hwacha's Manual (EECS-2015-262) the following
658 pseudo-code for operations is given, where p is the explicit (direct)
659 reference to the predication register to be used:
661 for (int i=0; i<vl; ++i)
663 (d ? vreg[rd][i] : sreg[rd]) =
664 iop(s1 ? vreg[rs1][i] : sreg[rs1],
665 s2 ? vreg[rs2][i] : sreg[rs2]); // for insts with 2 inputs
667 This instead becomes an *indirect* reference using the *internal* state
668 table generated from the Predication CSR key-value store, which is used
672 preg = int_pred_reg[rd]
674 preg = fp_pred_reg[rd]
676 for (int i=0; i<vl; ++i)
677 predicate, zeroing = get_pred_val(type(iop) == INT, rd):
678 if (predicate && (1<<i))
679 result = iop(s1 ? regfile[rs1+i] : regfile[rs1],
680 s2 ? regfile[rs2+i] : regfile[rs2]);
681 (d ? regfile[rd+i] : regfile[rd]) = result
682 if preg.ffirst and result == 0:
683 VL = i # result was zero, end loop early, return VL
686 (d ? regfile[rd+i] : regfile[rd]) = 0
690 * d, s1 and s2 are booleans indicating whether destination,
691 source1 and source2 are vector or scalar
692 * key-value CSR-redirection of rd, rs1 and rs2 have NOT been included
693 above, for clarity. rd, rs1 and rs2 all also must ALSO go through
694 register-level redirection (from the Register table) if they are
696 * fail-on-first mode stops execution early whenever an operation
697 returns a zero value. floating-point results count both
698 positive-zero as well as negative-zero as "fail".
700 If written as a function, obtaining the predication mask (and whether
701 zeroing takes place) may be done as follows:
703 def get_pred_val(bool is_fp_op, int reg):
704 tb = int_reg if is_fp_op else fp_reg
705 if (!tb[reg].enabled):
706 return ~0x0, False // all enabled; no zeroing
707 tb = int_pred if is_fp_op else fp_pred
708 if (!tb[reg].enabled):
709 return ~0x0, False // all enabled; no zeroing
710 predidx = tb[reg].predidx // redirection occurs HERE
711 predicate = intreg[predidx] // actual predicate HERE
713 predicate = ~predicate // invert ALL bits
714 return predicate, tb[reg].zero
716 Note here, critically, that **only** if the register is marked
717 in its **register** table entry as being "active" does the testing
718 proceed further to check if the **predicate** table entry is
721 Note also that this is in direct contrast to branch operations
722 for the storage of comparisions: in these specific circumstances
723 the requirement for there to be an active *register* entry
726 ## Fail-on-First Mode <a name="ffirst-mode"></a>
728 ffirst is a special data-dependent predicate mode. There are two
729 variants: one is for faults: typically for LOAD/STORE operations,
730 which may encounter end of page faults during a series of operations.
731 The other variant is comparisons such as FEQ (or the augmented behaviour
732 of Branch), and any operation that returns a result of zero (whether
733 integer or floating-point). In the FP case, this includes negative-zero.
735 Note that the execution order must "appear" to be sequential for ffirst
736 mode to work correctly. An in-order architecture must execute the element
737 operations in sequence, whilst an out-of-order architecture must *commit*
738 the element operations in sequence (giving the appearance of in-order
741 Note also, that if ffirst mode is needed without predication, a special
742 "always-on" Predicate Table Entry may be constructed by setting
743 inverse-on and using x0 as the predicate register. This
744 will have the effect of creating a mask of all ones, allowing ffirst
747 ### Fail-on-first traps
749 Except for the first element, ffault stops sequential element processing
750 when a trap occurs. The first element is treated normally (as if ffirst
751 is clear). Should any subsequent element instruction require a trap,
752 instead it and subsequent indexed elements are ignored (or cancelled in
753 out-of-order designs), and VL is set to the *last* instruction that did
756 Note that predicated-out elements (where the predicate mask bit is zero)
757 are clearly excluded (i.e. the trap will not occur). However, note that
758 the loop still had to test the predicate bit: thus on return,
759 VL is set to include elements that did not take the trap *and* includes
760 the elements that were predicated (masked) out (not tested up to the
761 point where the trap occurred).
763 If SUBVL is being used (SUBVL!=1), the first *sub-group* of elements
764 will cause a trap as normal (as if ffirst is not set); subsequently,
765 the trap must not occur in the *sub-group* of elements. SUBVL will **NOT**
768 Given that predication bits apply to SUBVL groups, the same rules apply
769 to predicated-out (masked-out) sub-groups in calculating the value that VL
772 ### Fail-on-first conditional tests
774 ffault stops sequential element conditional testing on the first element result
775 being zero. VL is set to the number of elements that were processed before
776 the fail-condition was encountered.
778 Note that just as with traps, if SUBVL!=1, the first of any of the *sub-group*
779 will cause the processing to end, and, even if there were elements within
780 the *sub-group* that passed the test, that sub-group is still (entirely)
781 excluded from the count (from setting VL). i.e. VL is set to the total
782 number of *sub-groups* that had no fail-condition up until execution was
785 Note again that, just as with traps, predicated-out (masked-out) elements
786 are included in the count leading up to the fail-condition, even though they
789 The pseudo-code for Predication makes this clearer and simpler than it is
790 in words (the loop ends, VL is set to the current element index, "i").
792 ## REMAP CSR <a name="remap" />
794 (Note: both the REMAP and SHAPE sections are best read after the
795 rest of the document has been read)
797 There is one 32-bit CSR which may be used to indicate which registers,
798 if used in any operation, must be "reshaped" (re-mapped) from a linear
799 form to a 2D or 3D transposed form, or "offset" to permit arbitrary
800 access to elements within a register.
802 The 32-bit REMAP CSR may reshape up to 3 registers:
804 | 29..28 | 27..26 | 25..24 | 23 | 22..16 | 15 | 14..8 | 7 | 6..0 |
805 | ------ | ------ | ------ | -- | ------- | -- | ------- | -- | ------- |
806 | shape2 | shape1 | shape0 | 0 | regidx2 | 0 | regidx1 | 0 | regidx0 |
808 regidx0-2 refer not to the Register CSR CAM entry but to the underlying
809 *real* register (see regidx, the value) and consequently is 7-bits wide.
810 When set to zero (referring to x0), clearly reshaping x0 is pointless,
811 so is used to indicate "disabled".
812 shape0-2 refers to one of three SHAPE CSRs. A value of 0x3 is reserved.
813 Bits 7, 15, 23, 30 and 31 are also reserved, and must be set to zero.
815 It is anticipated that these specialist CSRs not be very often used.
816 Unlike the CSR Register and Predication tables, the REMAP CSRs use
817 the full 7-bit regidx so that they can be set once and left alone,
818 whilst the CSR Register entries pointing to them are disabled, instead.
820 ## SHAPE 1D/2D/3D vector-matrix remapping CSRs
822 (Note: both the REMAP and SHAPE sections are best read after the
823 rest of the document has been read)
825 There are three "shape" CSRs, SHAPE0, SHAPE1, SHAPE2, 32-bits in each,
826 which have the same format. When each SHAPE CSR is set entirely to zeros,
827 remapping is disabled: the register's elements are a linear (1D) vector.
829 | 26..24 | 23 | 22..16 | 15 | 14..8 | 7 | 6..0 |
830 | ------- | -- | ------- | -- | ------- | -- | ------- |
831 | permute | offs[2] | zdimsz | offs[1] | ydimsz | offs[0] | xdimsz |
833 offs is a 3-bit field, spread out across bits 7, 15 and 23, which
834 is added to the element index during the loop calculation.
836 xdimsz, ydimsz and zdimsz are offset by 1, such that a value of 0 indicates
837 that the array dimensionality for that dimension is 1. A value of xdimsz=2
838 would indicate that in the first dimension there are 3 elements in the
839 array. The format of the array is therefore as follows:
841 array[xdim+1][ydim+1][zdim+1]
843 However whilst illustrative of the dimensionality, that does not take the
844 "permute" setting into account. "permute" may be any one of six values
845 (0-5, with values of 6 and 7 being reserved, and not legal). The table
846 below shows how the permutation dimensionality order works:
848 | permute | order | array format |
849 | ------- | ----- | ------------------------ |
850 | 000 | 0,1,2 | (xdim+1)(ydim+1)(zdim+1) |
851 | 001 | 0,2,1 | (xdim+1)(zdim+1)(ydim+1) |
852 | 010 | 1,0,2 | (ydim+1)(xdim+1)(zdim+1) |
853 | 011 | 1,2,0 | (ydim+1)(zdim+1)(xdim+1) |
854 | 100 | 2,0,1 | (zdim+1)(xdim+1)(ydim+1) |
855 | 101 | 2,1,0 | (zdim+1)(ydim+1)(xdim+1) |
857 In other words, the "permute" option changes the order in which
858 nested for-loops over the array would be done. The algorithm below
859 shows this more clearly, and may be executed as a python program:
861 # mapidx = REMAP.shape2
862 xdim = 3 # SHAPE[mapidx].xdim_sz+1
863 ydim = 4 # SHAPE[mapidx].ydim_sz+1
864 zdim = 5 # SHAPE[mapidx].zdim_sz+1
866 lims = [xdim, ydim, zdim]
867 idxs = [0,0,0] # starting indices
868 order = [1,0,2] # experiment with different permutations, here
869 offs = 0 # experiment with different offsets, here
871 for idx in range(xdim * ydim * zdim):
872 new_idx = offs + idxs[0] + idxs[1] * xdim + idxs[2] * xdim * ydim
875 idxs[order[i]] = idxs[order[i]] + 1
876 if (idxs[order[i]] != lims[order[i]]):
881 Here, it is assumed that this algorithm be run within all pseudo-code
882 throughout this document where a (parallelism) for-loop would normally
883 run from 0 to VL-1 to refer to contiguous register
884 elements; instead, where REMAP indicates to do so, the element index
885 is run through the above algorithm to work out the **actual** element
886 index, instead. Given that there are three possible SHAPE entries, up to
887 three separate registers in any given operation may be simultaneously
890 function op_add(rd, rs1, rs2) # add not VADD!
893 for (i = 0; i < VL; i++)
894 xSTATE.srcoffs = i # save context
895 if (predval & 1<<i) # predication uses intregs
896 ireg[rd+remap(id)] <= ireg[rs1+remap(irs1)] +
897 ireg[rs2+remap(irs2)];
898 if (!int_vec[rd ].isvector) break;
899 if (int_vec[rd ].isvector) { id += 1; }
900 if (int_vec[rs1].isvector) { irs1 += 1; }
901 if (int_vec[rs2].isvector) { irs2 += 1; }
903 By changing remappings, 2D matrices may be transposed "in-place" for one
904 operation, followed by setting a different permutation order without
905 having to move the values in the registers to or from memory. Also,
906 the reason for having REMAP separate from the three SHAPE CSRs is so
907 that in a chain of matrix multiplications and additions, for example,
908 the SHAPE CSRs need only be set up once; only the REMAP CSR need be
909 changed to target different registers.
913 * Over-running the register file clearly has to be detected and
914 an illegal instruction exception thrown
915 * When non-default elwidths are set, the exact same algorithm still
916 applies (i.e. it offsets elements *within* registers rather than
918 * If permute option 000 is utilised, the actual order of the
919 reindexing does not change!
920 * If two or more dimensions are set to zero, the actual order does not change!
921 * The above algorithm is pseudo-code **only**. Actual implementations
922 will need to take into account the fact that the element for-looping
923 must be **re-entrant**, due to the possibility of exceptions occurring.
924 See MSTATE CSR, which records the current element index.
925 * Twin-predicated operations require **two** separate and distinct
926 element offsets. The above pseudo-code algorithm will be applied
927 separately and independently to each, should each of the two
928 operands be remapped. *This even includes C.LDSP* and other operations
929 in that category, where in that case it will be the **offset** that is
930 remapped (see Compressed Stack LOAD/STORE section).
931 * Offset is especially useful, on its own, for accessing elements
932 within the middle of a register. Without offsets, it is necessary
933 to either use a predicated MV, skipping the first elements, or
934 performing a LOAD/STORE cycle to memory.
935 With offsets, the data does not have to be moved.
936 * Setting the total elements (xdim+1) times (ydim+1) times (zdim+1) to
937 less than MVL is **perfectly legal**, albeit very obscure. It permits
938 entries to be regularly presented to operands **more than once**, thus
939 allowing the same underlying registers to act as an accumulator of
940 multiple vector or matrix operations, for example.
942 Clearly here some considerable care needs to be taken as the remapping
943 could hypothetically create arithmetic operations that target the
944 exact same underlying registers, resulting in data corruption due to
945 pipeline overlaps. Out-of-order / Superscalar micro-architectures with
946 register-renaming will have an easier time dealing with this than
947 DSP-style SIMD micro-architectures.
949 # Instruction Execution Order
951 Simple-V behaves as if it is a hardware-level "macro expansion system",
952 substituting and expanding a single instruction into multiple sequential
953 instructions with contiguous and sequentially-incrementing registers.
954 As such, it does **not** modify - or specify - the behaviour and semantics of
955 the execution order: that may be deduced from the **existing** RV
956 specification in each and every case.
958 So for example if a particular micro-architecture permits out-of-order
959 execution, and it is augmented with Simple-V, then wherever instructions
960 may be out-of-order then so may the "post-expansion" SV ones.
962 If on the other hand there are memory guarantees which specifically
963 prevent and prohibit certain instructions from being re-ordered
964 (such as the Atomicity Axiom, or FENCE constraints), then clearly
965 those constraints **MUST** also be obeyed "post-expansion".
967 It should be absolutely clear that SV is **not** about providing new
968 functionality or changing the existing behaviour of a micro-architetural
969 design, or about changing the RISC-V Specification.
970 It is **purely** about compacting what would otherwise be contiguous
971 instructions that use sequentially-increasing register numbers down
972 to the **one** instruction.
974 # Instructions <a name="instructions" />
976 Despite being a 98% complete and accurate topological remap of RVV
977 concepts and functionality, no new instructions are needed.
978 Compared to RVV: *All* RVV instructions can be re-mapped, however xBitManip
979 becomes a critical dependency for efficient manipulation of predication
980 masks (as a bit-field). Despite the removal of all operations,
981 with the exception of CLIP and VSELECT.X
982 *all instructions from RVV Base are topologically re-mapped and retain their
983 complete functionality, intact*. Note that if RV64G ever had
984 a MV.X added as well as FCLIP, the full functionality of RVV-Base would
987 Three instructions, VSELECT, VCLIP and VCLIPI, do not have RV Standard
988 equivalents, so are left out of Simple-V. VSELECT could be included if
989 there existed a MV.X instruction in RV (MV.X is a hypothetical
990 non-immediate variant of MV that would allow another register to
991 specify which register was to be copied). Note that if any of these three
992 instructions are added to any given RV extension, their functionality
993 will be inherently parallelised.
995 With some exceptions, where it does not make sense or is simply too
996 challenging, all RV-Base instructions are parallelised:
998 * CSR instructions, whilst a case could be made for fast-polling of
999 a CSR into multiple registers, or for being able to copy multiple
1000 contiguously addressed CSRs into contiguous registers, and so on,
1001 are the fundamental core basis of SV. If parallelised, extreme
1002 care would need to be taken. Additionally, CSR reads are done
1003 using x0, and it is *really* inadviseable to tag x0.
1004 * LUI, C.J, C.JR, WFI, AUIPC are not suitable for parallelising so are
1006 * LR/SC could hypothetically be parallelised however their purpose is
1007 single (complex) atomic memory operations where the LR must be followed
1008 up by a matching SC. A sequence of parallel LR instructions followed
1009 by a sequence of parallel SC instructions therefore is guaranteed to
1010 not be useful. Not least: the guarantees of a Multi-LR/SC
1011 would be impossible to provide if emulated in a trap.
1012 * EBREAK, NOP, FENCE and others do not use registers so are not inherently
1013 paralleliseable anyway.
1015 All other operations using registers are automatically parallelised.
1016 This includes AMOMAX, AMOSWAP and so on, where particular care and
1017 attention must be paid.
1019 Example pseudo-code for an integer ADD operation (including scalar
1020 operations). Floating-point uses the FP Register Table.
1022 function op_add(rd, rs1, rs2) # add not VADD!
1023 int i, id=0, irs1=0, irs2=0;
1024 predval = get_pred_val(FALSE, rd);
1025 rd = int_vec[rd ].isvector ? int_vec[rd ].regidx : rd;
1026 rs1 = int_vec[rs1].isvector ? int_vec[rs1].regidx : rs1;
1027 rs2 = int_vec[rs2].isvector ? int_vec[rs2].regidx : rs2;
1028 for (i = 0; i < VL; i++)
1029 xSTATE.srcoffs = i # save context
1030 if (predval & 1<<i) # predication uses intregs
1031 ireg[rd+id] <= ireg[rs1+irs1] + ireg[rs2+irs2];
1032 if (!int_vec[rd ].isvector) break;
1033 if (int_vec[rd ].isvector) { id += 1; }
1034 if (int_vec[rs1].isvector) { irs1 += 1; }
1035 if (int_vec[rs2].isvector) { irs2 += 1; }
1037 Note that for simplicity there is quite a lot missing from the above
1038 pseudo-code: element widths, zeroing on predication, dimensional
1039 reshaping and offsets and so on. However it demonstrates the basic
1040 principle. Augmentations that produce the full pseudo-code are covered in
1043 ## SUBVL Pseudocode <a name="subvl-pseudocode"></a>
1045 Adding in support for SUBVL is a matter of adding in an extra inner
1046 for-loop, where register src and dest are still incremented inside the
1047 inner part. Not that the predication is still taken from the VL index.
1049 So whilst elements are indexed by "(i * SUBVL + s)", predicate bits are
1052 function op_add(rd, rs1, rs2) # add not VADD!
1053 int i, id=0, irs1=0, irs2=0;
1054 predval = get_pred_val(FALSE, rd);
1055 rd = int_vec[rd ].isvector ? int_vec[rd ].regidx : rd;
1056 rs1 = int_vec[rs1].isvector ? int_vec[rs1].regidx : rs1;
1057 rs2 = int_vec[rs2].isvector ? int_vec[rs2].regidx : rs2;
1058 for (i = 0; i < VL; i++)
1059 xSTATE.srcoffs = i # save context
1060 for (s = 0; s < SUBVL; s++)
1061 xSTATE.ssvoffs = s # save context
1062 if (predval & 1<<i) # predication uses intregs
1063 # actual add is here (at last)
1064 ireg[rd+id] <= ireg[rs1+irs1] + ireg[rs2+irs2];
1065 if (!int_vec[rd ].isvector) break;
1066 if (int_vec[rd ].isvector) { id += 1; }
1067 if (int_vec[rs1].isvector) { irs1 += 1; }
1068 if (int_vec[rs2].isvector) { irs2 += 1; }
1069 if (id == VL or irs1 == VL or irs2 == VL) {
1070 # end VL hardware loop
1071 xSTATE.srcoffs = 0; # reset
1072 xSTATE.ssvoffs = 0; # reset
1077 NOTE: pseudocode simplified greatly: zeroing, proper predicate handling,
1078 elwidth handling etc. all left out.
1080 ## Instruction Format
1082 It is critical to appreciate that there are
1083 **no operations added to SV, at all**.
1085 Instead, by using CSRs to tag registers as an indication of "changed
1086 behaviour", SV *overloads* pre-existing branch operations into predicated
1087 variants, and implicitly overloads arithmetic operations, MV, FCVT, and
1088 LOAD/STORE depending on CSR configurations for bitwidth and predication.
1089 **Everything** becomes parallelised. *This includes Compressed
1090 instructions* as well as any future instructions and Custom Extensions.
1092 Note: CSR tags to change behaviour of instructions is nothing new, including
1093 in RISC-V. UXL, SXL and MXL change the behaviour so that XLEN=32/64/128.
1094 FRM changes the behaviour of the floating-point unit, to alter the rounding
1095 mode. Other architectures change the LOAD/STORE byte-order from big-endian
1096 to little-endian on a per-instruction basis. SV is just a little more...
1097 comprehensive in its effect on instructions.
1099 ## Branch Instructions
1101 Branch operations are augmented slightly to be a little more like FP
1102 Compares (FEQ, FNE etc.), by permitting the cumulation (and storage)
1103 of multiple comparisons into a register (taken indirectly from the predicate
1104 table). As such, "ffirst" - fail-on-first - condition mode can be enabled.
1105 See ffirst mode in the Predication Table section.
1107 ### Standard Branch <a name="standard_branch"></a>
1109 Branch operations use standard RV opcodes that are reinterpreted to
1110 be "predicate variants" in the instance where either of the two src
1111 registers are marked as vectors (active=1, vector=1).
1113 Note that the predication register to use (if one is enabled) is taken from
1114 the *first* src register, and that this is used, just as with predicated
1115 arithmetic operations, to mask whether the comparison operations take
1116 place or not. The target (destination) predication register
1117 to use (if one is enabled) is taken from the *second* src register.
1119 If either of src1 or src2 are scalars (whether by there being no
1120 CSR register entry or whether by the CSR entry specifically marking
1121 the register as "scalar") the comparison goes ahead as vector-scalar
1124 In instances where no vectorisation is detected on either src registers
1125 the operation is treated as an absolutely standard scalar branch operation.
1126 Where vectorisation is present on either or both src registers, the
1127 branch may stil go ahead if any only if *all* tests succeed (i.e. excluding
1128 those tests that are predicated out).
1130 Note that when zero-predication is enabled (from source rs1),
1131 a cleared bit in the predicate indicates that the result
1132 of the compare is set to "false", i.e. that the corresponding
1133 destination bit (or result)) be set to zero. Contrast this with
1134 when zeroing is not set: bits in the destination predicate are
1135 only *set*; they are **not** cleared. This is important to appreciate,
1136 as there may be an expectation that, going into the hardware-loop,
1137 the destination predicate is always expected to be set to zero:
1138 this is **not** the case. The destination predicate is only set
1139 to zero if **zeroing** is enabled.
1141 Note that just as with the standard (scalar, non-predicated) branch
1142 operations, BLE, BGT, BLEU and BTGU may be synthesised by inverting
1145 In Hwacha EECS-2015-262 Section 6.7.2 the following pseudocode is given
1146 for predicated compare operations of function "cmp":
1148 for (int i=0; i<vl; ++i)
1150 preg[pd][i] = cmp(s1 ? vreg[rs1][i] : sreg[rs1],
1151 s2 ? vreg[rs2][i] : sreg[rs2]);
1153 With associated predication, vector-length adjustments and so on,
1154 and temporarily ignoring bitwidth (which makes the comparisons more
1155 complex), this becomes:
1157 s1 = reg_is_vectorised(src1);
1158 s2 = reg_is_vectorised(src2);
1161 if cmp(rs1, rs2) # scalar compare
1165 preg = int_pred_reg[rd]
1168 ps = get_pred_val(I/F==INT, rs1);
1169 rd = get_pred_val(I/F==INT, rs2); # this may not exist
1171 if not exists(rd) or zeroing:
1176 for (int i = 0; i < VL; ++i)
1178 if not (ps & (1<<i))
1180 else if (ps & (1<<i))
1181 if (cmp(s1 ? reg[src1+i]:reg[src1],
1182 s2 ? reg[src2+i]:reg[src2])
1191 preg[rd] = result # store in destination
1197 * Predicated SIMD comparisons would break src1 and src2 further down
1198 into bitwidth-sized chunks (see Appendix "Bitwidth Virtual Register
1199 Reordering") setting Vector-Length times (number of SIMD elements) bits
1200 in Predicate Register rd, as opposed to just Vector-Length bits.
1201 * The execution of "parallelised" instructions **must** be implemented
1202 as "re-entrant" (to use a term from software). If an exception (trap)
1203 occurs during the middle of a vectorised
1204 Branch (now a SV predicated compare) operation, the partial results
1205 of any comparisons must be written out to the destination
1206 register before the trap is permitted to begin. If however there
1207 is no predicate, the **entire** set of comparisons must be **restarted**,
1208 with the offset loop indices set back to zero. This is because
1209 there is no place to store the temporary result during the handling
1212 TODO: predication now taken from src2. also branch goes ahead
1213 if all compares are successful.
1215 Note also that where normally, predication requires that there must
1216 also be a CSR register entry for the register being used in order
1217 for the **predication** CSR register entry to also be active,
1218 for branches this is **not** the case. src2 does **not** have
1219 to have its CSR register entry marked as active in order for
1220 predication on src2 to be active.
1222 Also note: SV Branch operations are **not** twin-predicated
1223 (see Twin Predication section). This would require three
1224 element offsets: one to track src1, one to track src2 and a third
1225 to track where to store the accumulation of the results. Given
1226 that the element offsets need to be exposed via CSRs so that
1227 the parallel hardware looping may be made re-entrant on traps
1228 and exceptions, the decision was made not to make SV Branches
1231 ### Floating-point Comparisons
1233 There does not exist floating-point branch operations, only compare.
1234 Interestingly no change is needed to the instruction format because
1235 FP Compare already stores a 1 or a zero in its "rd" integer register
1236 target, i.e. it's not actually a Branch at all: it's a compare.
1238 In RV (scalar) Base, a branch on a floating-point compare is
1239 done via the sequence "FEQ x1, f0, f5; BEQ x1, x0, #jumploc".
1240 This does extend to SV, as long as x1 (in the example sequence given)
1241 is vectorised. When that is the case, x1..x(1+VL-1) will also be
1242 set to 0 or 1 depending on whether f0==f5, f1==f6, f2==f7 and so on.
1243 The BEQ that follows will *also* compare x1==x0, x2==x0, x3==x0 and
1244 so on. Consequently, unlike integer-branch, FP Compare needs no
1245 modification in its behaviour.
1247 In addition, it is noted that an entry "FNE" (the opposite of FEQ) is missing,
1248 and whilst in ordinary branch code this is fine because the standard
1249 RVF compare can always be followed up with an integer BEQ or a BNE (or
1250 a compressed comparison to zero or non-zero), in predication terms that
1251 becomes more of an impact. To deal with this, SV's predication has
1252 had "invert" added to it.
1254 Also: note that FP Compare may be predicated, using the destination
1255 integer register (rd) to determine the predicate. FP Compare is **not**
1256 a twin-predication operation, as, again, just as with SV Branches,
1257 there are three registers involved: FP src1, FP src2 and INT rd.
1259 Also: note that ffirst (fail first mode) applies directly to this operation.
1261 ### Compressed Branch Instruction
1263 Compressed Branch instructions are, just like standard Branch instructions,
1264 reinterpreted to be vectorised and predicated based on the source register
1265 (rs1s) CSR entries. As however there is only the one source register,
1266 given that c.beqz a10 is equivalent to beqz a10,x0, the optional target
1267 to store the results of the comparisions is taken from CSR predication
1268 table entries for **x0**.
1270 The specific required use of x0 is, with a little thought, quite obvious,
1271 but is counterintuitive. Clearly it is **not** recommended to redirect
1272 x0 with a CSR register entry, however as a means to opaquely obtain
1273 a predication target it is the only sensible option that does not involve
1274 additional special CSRs (or, worse, additional special opcodes).
1276 Note also that, just as with standard branches, the 2nd source
1277 (in this case x0 rather than src2) does **not** have to have its CSR
1278 register table marked as "active" in order for predication to work.
1280 ## Vectorised Dual-operand instructions
1282 There is a series of 2-operand instructions involving copying (and
1283 sometimes alteration):
1286 * FMV, FNEG, FABS, FCVT, FSGNJ, FSGNJN and FSGNJX
1287 * C.LWSP, C.SWSP, C.LDSP, C.FLWSP etc.
1288 * LOAD(-FP) and STORE(-FP)
1290 All of these operations follow the same two-operand pattern, so it is
1291 *both* the source *and* destination predication masks that are taken into
1292 account. This is different from
1293 the three-operand arithmetic instructions, where the predication mask
1294 is taken from the *destination* register, and applied uniformly to the
1295 elements of the source register(s), element-for-element.
1297 The pseudo-code pattern for twin-predicated operations is as
1300 function op(rd, rs):
1301 rd = int_csr[rd].active ? int_csr[rd].regidx : rd;
1302 rs = int_csr[rs].active ? int_csr[rs].regidx : rs;
1303 ps = get_pred_val(FALSE, rs); # predication on src
1304 pd = get_pred_val(FALSE, rd); # ... AND on dest
1305 for (int i = 0, int j = 0; i < VL && j < VL;):
1306 if (int_csr[rs].isvec) while (!(ps & 1<<i)) i++;
1307 if (int_csr[rd].isvec) while (!(pd & 1<<j)) j++;
1308 xSTATE.srcoffs = i # save context
1309 xSTATE.destoffs = j # save context
1310 reg[rd+j] = SCALAR_OPERATION_ON(reg[rs+i])
1311 if (int_csr[rs].isvec) i++;
1312 if (int_csr[rd].isvec) j++; else break
1314 This pattern covers scalar-scalar, scalar-vector, vector-scalar
1315 and vector-vector, and predicated variants of all of those.
1316 Zeroing is not presently included (TODO). As such, when compared
1317 to RVV, the twin-predicated variants of C.MV and FMV cover
1318 **all** standard vector operations: VINSERT, VSPLAT, VREDUCE,
1319 VEXTRACT, VSCATTER, VGATHER, VCOPY, and more.
1323 * elwidth (SIMD) is not covered in the pseudo-code above
1324 * ending the loop early in scalar cases (VINSERT, VEXTRACT) is also
1326 * zero predication is also not shown (TODO).
1328 ### C.MV Instruction <a name="c_mv"></a>
1330 There is no MV instruction in RV however there is a C.MV instruction.
1331 It is used for copying integer-to-integer registers (vectorised FMV
1332 is used for copying floating-point).
1334 If either the source or the destination register are marked as vectors
1335 C.MV is reinterpreted to be a vectorised (multi-register) predicated
1336 move operation. The actual instruction's format does not change:
1339 15 12 | 11 7 | 6 2 | 1 0 |
1340 funct4 | rd | rs | op |
1342 C.MV | dest | src | C0 |
1345 A simplified version of the pseudocode for this operation is as follows:
1347 function op_mv(rd, rs) # MV not VMV!
1348 rd = int_csr[rd].active ? int_csr[rd].regidx : rd;
1349 rs = int_csr[rs].active ? int_csr[rs].regidx : rs;
1350 ps = get_pred_val(FALSE, rs); # predication on src
1351 pd = get_pred_val(FALSE, rd); # ... AND on dest
1352 for (int i = 0, int j = 0; i < VL && j < VL;):
1353 if (int_csr[rs].isvec) while (!(ps & 1<<i)) i++;
1354 if (int_csr[rd].isvec) while (!(pd & 1<<j)) j++;
1355 xSTATE.srcoffs = i # save context
1356 xSTATE.destoffs = j # save context
1357 ireg[rd+j] <= ireg[rs+i];
1358 if (int_csr[rs].isvec) i++;
1359 if (int_csr[rd].isvec) j++; else break
1361 There are several different instructions from RVV that are covered by
1365 src | dest | predication | op |
1366 scalar | vector | none | VSPLAT |
1367 scalar | vector | destination | sparse VSPLAT |
1368 scalar | vector | 1-bit dest | VINSERT |
1369 vector | scalar | 1-bit? src | VEXTRACT |
1370 vector | vector | none | VCOPY |
1371 vector | vector | src | Vector Gather |
1372 vector | vector | dest | Vector Scatter |
1373 vector | vector | src & dest | Gather/Scatter |
1374 vector | vector | src == dest | sparse VCOPY |
1377 Also, VMERGE may be implemented as back-to-back (macro-op fused) C.MV
1378 operations with zeroing off, and inversion on the src and dest predication
1379 for one of the two C.MV operations. The non-inverted C.MV will place
1380 one set of registers into the destination, and the inverted one the other
1381 set. With predicate-inversion, copying and inversion of the predicate mask
1382 need not be done as a separate (scalar) instruction.
1384 Note that in the instance where the Compressed Extension is not implemented,
1385 MV may be used, but that is a pseudo-operation mapping to addi rd, x0, rs.
1386 Note that the behaviour is **different** from C.MV because with addi the
1387 predication mask to use is taken **only** from rd and is applied against
1388 all elements: rs[i] = rd[i].
1390 ### FMV, FNEG and FABS Instructions
1392 These are identical in form to C.MV, except covering floating-point
1393 register copying. The same double-predication rules also apply.
1394 However when elwidth is not set to default the instruction is implicitly
1395 and automatic converted to a (vectorised) floating-point type conversion
1396 operation of the appropriate size covering the source and destination
1399 (Note that FMV, FNEG and FABS are all actually pseudo-instructions)
1401 ### FVCT Instructions
1403 These are again identical in form to C.MV, except that they cover
1404 floating-point to integer and integer to floating-point. When element
1405 width in each vector is set to default, the instructions behave exactly
1406 as they are defined for standard RV (scalar) operations, except vectorised
1407 in exactly the same fashion as outlined in C.MV.
1409 However when the source or destination element width is not set to default,
1410 the opcode's explicit element widths are *over-ridden* to new definitions,
1411 and the opcode's element width is taken as indicative of the SIMD width
1412 (if applicable i.e. if packed SIMD is requested) instead.
1414 For example FCVT.S.L would normally be used to convert a 64-bit
1415 integer in register rs1 to a 64-bit floating-point number in rd.
1416 If however the source rs1 is set to be a vector, where elwidth is set to
1417 default/2 and "packed SIMD" is enabled, then the first 32 bits of
1418 rs1 are converted to a floating-point number to be stored in rd's
1419 first element and the higher 32-bits *also* converted to floating-point
1420 and stored in the second. The 32 bit size comes from the fact that
1421 FCVT.S.L's integer width is 64 bit, and with elwidth on rs1 set to
1422 divide that by two it means that rs1 element width is to be taken as 32.
1424 Similar rules apply to the destination register.
1426 ## LOAD / STORE Instructions and LOAD-FP/STORE-FP <a name="load_store"></a>
1428 An earlier draft of SV modified the behaviour of LOAD/STORE (modified
1429 the interpretation of the instruction fields). This
1430 actually undermined the fundamental principle of SV, namely that there
1431 be no modifications to the scalar behaviour (except where absolutely
1432 necessary), in order to simplify an implementor's task if considering
1433 converting a pre-existing scalar design to support parallelism.
1435 So the original RISC-V scalar LOAD/STORE and LOAD-FP/STORE-FP functionality
1436 do not change in SV, however just as with C.MV it is important to note
1437 that dual-predication is possible.
1439 In vectorised architectures there are usually at least two different modes
1442 * Read (or write for STORE) from sequential locations, where one
1443 register specifies the address, and the one address is incremented
1444 by a fixed amount. This is usually known as "Unit Stride" mode.
1445 * Read (or write) from multiple indirected addresses, where the
1446 vector elements each specify separate and distinct addresses.
1448 To support these different addressing modes, the CSR Register "isvector"
1449 bit is used. So, for a LOAD, when the src register is set to
1450 scalar, the LOADs are sequentially incremented by the src register
1451 element width, and when the src register is set to "vector", the
1452 elements are treated as indirection addresses. Simplified
1453 pseudo-code would look like this:
1455 function op_ld(rd, rs) # LD not VLD!
1456 rdv = int_csr[rd].active ? int_csr[rd].regidx : rd;
1457 rsv = int_csr[rs].active ? int_csr[rs].regidx : rs;
1458 ps = get_pred_val(FALSE, rs); # predication on src
1459 pd = get_pred_val(FALSE, rd); # ... AND on dest
1460 for (int i = 0, int j = 0; i < VL && j < VL;):
1461 if (int_csr[rs].isvec) while (!(ps & 1<<i)) i++;
1462 if (int_csr[rd].isvec) while (!(pd & 1<<j)) j++;
1463 if (int_csr[rd].isvec)
1464 # indirect mode (multi mode)
1465 srcbase = ireg[rsv+i];
1468 srcbase = ireg[rsv] + i * XLEN/8; # offset in bytes
1469 ireg[rdv+j] <= mem[srcbase + imm_offs];
1470 if (!int_csr[rs].isvec &&
1471 !int_csr[rd].isvec) break # scalar-scalar LD
1472 if (int_csr[rs].isvec) i++;
1473 if (int_csr[rd].isvec) j++;
1477 * For simplicity, zeroing and elwidth is not included in the above:
1478 the key focus here is the decision-making for srcbase; vectorised
1479 rs means use sequentially-numbered registers as the indirection
1480 address, and scalar rs is "offset" mode.
1481 * The test towards the end for whether both source and destination are
1482 scalar is what makes the above pseudo-code provide the "standard" RV
1483 Base behaviour for LD operations.
1484 * The offset in bytes (XLEN/8) changes depending on whether the
1485 operation is a LB (1 byte), LH (2 byes), LW (4 bytes) or LD
1486 (8 bytes), and also whether the element width is over-ridden
1487 (see special element width section).
1489 ## Compressed Stack LOAD / STORE Instructions <a name="c_ld_st"></a>
1491 C.LWSP / C.SWSP and floating-point etc. are also source-dest twin-predicated,
1492 where it is implicit in C.LWSP/FLWSP etc. that x2 is the source register.
1493 It is therefore possible to use predicated C.LWSP to efficiently
1494 pop registers off the stack (by predicating x2 as the source), cherry-picking
1495 which registers to store to (by predicating the destination). Likewise
1496 for C.SWSP. In this way, LOAD/STORE-Multiple is efficiently achieved.
1498 The two modes ("unit stride" and multi-indirection) are still supported,
1499 as with standard LD/ST. Essentially, the only difference is that the
1500 use of x2 is hard-coded into the instruction.
1502 **Note**: it is still possible to redirect x2 to an alternative target
1503 register. With care, this allows C.LWSP / C.SWSP (and C.FLWSP) to be used as
1504 general-purpose LOAD/STORE operations.
1506 ## Compressed LOAD / STORE Instructions
1508 Compressed LOAD and STORE are again exactly the same as scalar LOAD/STORE,
1509 where the same rules apply and the same pseudo-code apply as for
1510 non-compressed LOAD/STORE. Again: setting scalar or vector mode
1511 on the src for LOAD and dest for STORE switches mode from "Unit Stride"
1512 to "Multi-indirection", respectively.
1516 TODO: expand. Exceptions may occur at any time, in any given underlying
1517 scalar operation. This implies that context-switching (traps) may
1518 occur, and operation must be returned to where it left off. That in
1519 turn implies that the full state - including the current parallel
1520 element being processed - has to be saved and restored. This is
1521 what the **STATE** CSR is for.
1523 The implications are that all underlying individual scalar operations
1524 "issued" by the parallelisation have to appear to be executed sequentially.
1525 The further implications are that if two or more individual element
1526 operations are underway, and one with an earlier index causes an exception,
1527 it may be necessary for the microarchitecture to **discard** or terminate
1528 operations with higher indices.
1530 This being somewhat dissatisfactory, an "opaque predication" variant
1531 of the STATE CSR is being considered.
1535 A "HINT" is an operation that has no effect on architectural state,
1536 where its use may, by agreed convention, give advance notification
1537 to the microarchitecture: branch prediction notification would be
1538 a good example. Usually HINTs are where rd=x0.
1540 With Simple-V being capable of issuing *parallel* instructions where
1541 rd=x0, the space for possible HINTs is expanded considerably. VL
1542 could be used to indicate different hints. In addition, if predication
1543 is set, the predication register itself could hypothetically be passed
1544 in as a *parameter* to the HINT operation.
1546 No specific hints are yet defined in Simple-V
1548 # Vector Block Format <a name="vliw-format"></a>
1550 See ancillary resource: [[vblock_format]]
1552 # Under consideration <a name="issues"></a>
1554 for element-grouping, if there is unused space within a register
1555 (3 16-bit elements in a 64-bit register for example), recommend:
1557 * For the unused elements in an integer register, the used element
1558 closest to the MSB is sign-extended on write and the unused elements
1559 are ignored on read.
1560 * The unused elements in a floating-point register are treated as-if
1561 they are set to all ones on write and are ignored on read, matching the
1562 existing standard for storing smaller FP values in larger registers.
1568 > One solution is to just not support LR/SC wider than a fixed
1569 > implementation-dependent size, which must be at least
1570 >1 XLEN word, which can be read from a read-only CSR
1571 > that can also be used for info like the kind and width of
1572 > hw parallelism supported (128-bit SIMD, minimal virtual
1573 > parallelism, etc.) and other things (like maybe the number
1574 > of registers supported).
1576 > That CSR would have to have a flag to make a read trap so
1577 > a hypervisor can simulate different values.
1581 > And what about instructions like JALR?
1583 answer: they're not vectorised, so not a problem
1587 * if opcode is in the RV32 group, rd, rs1 and rs2 bitwidth are
1588 XLEN if elwidth==default
1589 * if opcode is in the RV32I group, rd, rs1 and rs2 bitwidth are
1590 *32* if elwidth == default
1594 TODO: document different lengths for INT / FP regfiles, and provide
1595 as part of info register. 00=32, 01=64, 10=128, 11=reserved.
1599 TODO, update to remove RegCam and PredCam CSRs, just use SVprefix and
1604 Could the 8 bit Register VBLOCK format use regnum<<1 instead, only accessing regs 0 to 64?
1608 Expand the range of SUBVL and its associated svsrcoffs and svdestoffs by
1609 adding a 2nd STATE CSR (or extending STATE to 64 bits). Future version?