(no commit message)
[libreriscv.git] / simple_v_extension / specification.mdwn
index 9d552b2919feaac9cba33640aa82137f3d9e0c4e..2b1160f8c58e6b3042a7b505a916321f26e6320f 100644 (file)
@@ -1,14 +1,25 @@
+
 # Simple-V (Parallelism Extension Proposal) Specification
 
-* Status: DRAFTv0.1
-* Last edited: 30 sep 2018
+* Copyright (C) 2017, 2018, 2019 Luke Kenneth Casson Leighton
+* Status: DRAFTv0.6.1
+* Last edited: 10 sep 2019
+* Ancillary resource: [[opcodes]]
+* Ancillary resource: [[sv_prefix_proposal]]
+* Ancillary resource: [[abridged_spec]]
+* Ancillary resource: [[vblock_format]]
+* Ancillary resource: [[appendix]]
 
-With thanks to:
+Authors/Contributors:
 
+* Luke Kenneth Casson Leighton
 * Allen Baum
+* Bruce Hoult
+* comp.arch
 * Jacob Bachmeyer
 * Guy Lemurieux
 * Jacob Lifshay
+* Terje Mathisen
 * The RISC-V Founders, without whom this all would not be possible.
 
 [[!toc ]]
@@ -19,8 +30,10 @@ Simple-V is a uniform parallelism API for RISC-V hardware that has several
 unplanned side-effects including code-size reduction, expansion of
 HINT space and more.  The reason for
 creating it is to provide a manageable way to turn a pre-existing design
-into a parallel one, in a step-by-step incremental fashion, allowing
+into a parallel one, in a step-by-step incremental fashion, without adding any new opcodes, thus allowing
 the implementor to focus on adding hardware where it is needed and necessary.
+The primary target is for mobile-class 3D GPUs and VPUs, with secondary
+goals being to reduce executable size (by extending the effectiveness of RV opcodes, RVC in particular) and reduce context-switch latency.
 
 Critically: **No new instructions are added**.  The parallelism (if any
 is implemented) is implicitly added by tagging *standard* scalar registers
@@ -34,7 +47,7 @@ Simple-V effectively sits (slots) *in between* the instruction decode phase
 and the ALU(s).
 
 The barrier to entry with SV is therefore very low.  The minimum
-compliantt implementation is software-emulation (traps), requiring
+compliant implementation is software-emulation (traps), requiring
 only the CSRs and CSR tables, and that an exception be thrown if an
 instruction's registers are detected to have been tagged.  The looping
 that would otherwise be done in hardware is thus carried out in software,
@@ -55,7 +68,7 @@ To emphasise that clearly: Simple-V (SV) is *not*:
 * A SIMT system
 * A Vectorisation Microarchitecture
 * A microarchitecture of any specific kind
-* A mandary parallel processor microarchitecture of any kind
+* A mandatory parallel processor microarchitecture of any kind
 * A supercomputer extension
 
 SV does **not** tell implementors how or even if they should implement
@@ -64,87 +77,105 @@ that, if implemented, presents a uniform and consistent way to *express*
 parallelism, at the same time leaving the choice of if, how, how much,
 when and whether to parallelise operations **entirely to the implementor**.
 
-# CSRs <a name="csrs"></a>
+# Basic Operation
+
+The principle of SV is as follows:
+
+* Standard RV instructions are "prefixed" (extended) through a 48/64
+  bit format (single instruction option) or a variable
+ length VLIW-like prefix (multi or "grouped" option).
+* The prefix(es) indicate which registers are "tagged" as
+  "vectorised". Predicates can also be added, and element widths
+  overridden on any src or dest register.
+* A "Vector Length" CSR is set, indicating the span of any future
+  "parallel" operations.
+* If any operation (a **scalar** standard RV opcode) uses a register
+  that has been so "marked" ("tagged"), a hardware "macro-unrolling loop"
+  is activated, of length VL, that effectively issues **multiple**
+  identical instructions using contiguous sequentially-incrementing
+  register numbers, based on the "tags".
+* **Whether they be executed sequentially or in parallel or a
+  mixture of both or punted to software-emulation in a trap handler
+  is entirely up to the implementor**.
+
+In this way an entire scalar algorithm may be vectorised with
+the minimum of modification to the hardware and to compiler toolchains.
+
+To reiterate: **There are *no* new opcodes**. The scheme works *entirely*
+on hidden context that augments *scalar* RISCV instructions.
 
-For U-Mode there are two CSR key-value stores needed to create lookup
-tables which are used at the register decode phase.
+# CSRs <a name="csrs"></a>
 
-* A register CSR key-value table (typically 8 32-bit CSRs of 2 16-bits each)
-* A predication CSR key-value table (again, 8 32-bit CSRs of 2 16-bits each)
-* Small U-Mode and S-Mode register and predication CSR key-value tables
-  (2 32-bit CSRs of 2x 16-bit entries each).
 * An optional "reshaping" CSR key-value table which remaps from a 1D
   linear shape to 2D or 3D, including full transposition.
 
-There are also four additional CSRs for User-Mode:
+There are five additional CSRs, available in any privilege level:
 
-* CFG subsets the CSR tables
 * MVL (the Maximum Vector Length)
-* VL (which has different characteristics from standard CSRs)
-* STATE (useful for saving and restoring during context switch,
+* VL (sets which scalar register is to be the Vector Length)
+* SUBVL (effectively a kind of SIMD)
+* STATE (containing copies of MVL, VL and SUBVL as well as context information)
+* SVPSTATE (state information for SVPrefix)
+* PCVBLK (the current operation being executed within a VBLOCK Group)
+
+For User Mode there are the following CSRs:
+
+* uePCVBLK (a copy of the sub-execution Program Counter, that is relative
+  to the start of the current VBLOCK Group, set on a trap).
+* ueSTATE (useful for saving and restoring during context switch,
   and for providing fast transitions)
+* ueSVPSTATE when SVPrefix is implemented
+ Note: ueSVPSTATE is mirrored in the top 32 bits of ueSTATE.
 
 There are also three additional CSRs for Supervisor-Mode:
 
-* SMVL
-* SVL
-* SSTATE
+* sePCVBLK
+* seSTATE (which contains seSVPSTATE)
+* seSVPSTATE
 
 And likewise for M-Mode:
 
-* MMVL
-* MVL
-* MSTATE
-
-Both Supervisor and M-Mode have their own (small) CSR register and
-predication tables of only 4 entries each.
-
-## CFG
-
-This CSR may be used to switch between subsets of the CSR Register and
-Predication Tables: it is kept to 5 bits so that a single CSRRWI instruction
-can be used.  A setting of all ones is reserved to indicate that SimpleV
-is disabled.
+* mePCVBLK
+* meSTATE (which contains meSVPSTATE)
+* meSVPSTATE
 
-| (4..3) | (2...0) |
-| ------ | ------- |
-| size   | bank    |
+The u/m/s CSRs are treated and handled exactly like their (x)epc
+equivalents. On entry to or exit from a privilege level, the contents
+of its (x)eSTATE are swapped with STATE.
 
-Bank is 3 bits in size, and indicates the starting index of the CSR
-Register and Predication Table entries that are "enabled".  Given that
-each CSR table row is 16 bits and contains 2 CAM entries each, there
-are only 8 CSRs to cover in each table, so 8 bits is sufficient.
+Thus for example, a User Mode trap will end up swapping STATE and ueSTATE
+(on both entry and exit), allowing User Mode traps to have their own
+Vectorisation Context set up, separated from and unaffected by normal
+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.
 
-Size is 2 bits.  With the exception of when bank == 7 and size == 3,
-the number of elements enabled is taken by right-shifting 2 by size:
+Likewise, Supervisor Mode may perform context-switches, safe in the
+knowledge that its Vectorisation State is unaffected by User Mode.
 
-| size   | elements |
-| ------ | -------- |
-| 0      | 2        |
-| 1      | 4        |
-| 2      | 8        |
-| 3      | 16       |
+The access pattern for these groups of CSRs in each mode follows the
+same pattern for other CSRs that have M-Mode and S-Mode "mirrors":
 
-Given that there are 2 16-bit CAM entries per CSR table row, this
-may also be viewed as the number of CSR rows to enable, by raising size to
-the power of 2.
+* In M-Mode, the S-Mode and U-Mode CSRs are separate and distinct.
+* In S-Mode, accessing and changing of the M-Mode CSRs is transparently
+  identical
+  to changing the S-Mode CSRs.  Accessing and changing the U-Mode
+  CSRs is permitted.
+* In U-Mode, accessing and changing of the S-Mode and U-Mode CSRs
+  is prohibited.
 
-Examples:
+An interesting side effect of SV STATE being separate and distinct in S
+Mode is that Vectorised saving of an entire register file to the stack
+is a single instruction (through accidental provision of LOAD-MULTI
+semantics).  If the SVPrefix P64-LD-type format is used, LOAD-MULTI may
+even be done with a single standalone 64 bit opcode (P64 may set up SVPSTATE.SUBVL,
+SVPSTATE.VL and SVPSTATE.MVL from an immediate field, to cover the full regfile). It can
+even be predicated, which opens up some very interesting possibilities.
 
-* When bank = 0 and size = 3, SVREGCFG0 through to SVREGCFG7 are
-  enabled, and SVPREDCFG0 through to SVPREGCFG7 are enabled.
-* When bank = 1 and size = 3, SVREGCFG1 through to SVREGCFG7 are
-  enabled, and SVPREDCFG1 through to SVPREGCFG7 are enabled.
-* When bank = 3 and size = 0, SVREGCFG3 and SVPREDCFG3 are enabled.
-* When bank = 3 and size = 0, SVREGCFG3 and SVPREDCFG3 are enabled.
-* When bank = 7 and size = 1, SVREGCFG7 and SVPREDCFG7 are enabled.
-* When bank = 7 and size = 3, SimpleV is entirely disabled.
+(x)EPCVBLK CSRs must be treated exactly like their corresponding (x)epc
+equivalents. See VBLOCK section for details.
 
-In this way it is possible to enable and disable SimpleV with a
-single instruction, and, furthermore, on context-switching the quantity
-of CSRs to be saved and restored is greatly reduced.
-
-## MAXVECTORLENGTH (MVL)
+## MAXVECTORLENGTH (MVL) <a name="mvl" />
 
 MAXVECTORLENGTH is the same concept as MVL in RVV, except that it
 is variable length and may be dynamically set.  MVL is
@@ -152,126 +183,173 @@ however limited to the regfile bitwidth XLEN (1-32 for RV32,
 1-64 for RV64 and so on).
 
 The reason for setting this limit is so that predication registers, when
-marked as such, may fit into a single register as opposed to fanning out
-over several registers.  This keeps the implementation a little simpler.
-
-The other important factor to note is that the actual MVL is **offset
-by one**, so that it can fit into only 6 bits (for RV64) and still cover
-a range up to XLEN bits.  So, when setting the MVL CSR to 0, this actually
-means that MVL==1.  When setting the MVL CSR to 3, this actually means
-that MVL==4, and so on.  This is expressed more clearly in the "pseudocode"
+marked as such, may fit into a single register as opposed to fanning
+out over several registers.  This keeps the hardware implementation a
+little simpler.
+
+The other important factor to note is that the actual MVL is internally
+stored **offset by one**, so that it can fit into only 6 bits (for RV64)
+and still cover a range up to XLEN bits.  Attempts to set MVL to zero will
+return an exception.  This is expressed more clearly in the "pseudocode"
 section, where there are subtle differences between CSRRW and CSRRWI.
 
-## Vector Length (VL)
-
-VSETVL is slightly different from RVV.  Like RVV, VL is set to be within
-the range 1 <= VL <= MVL (where MVL in turn is limited to 1 <= MVL <= XLEN)
+## Vector Length (VL) <a name="vl" />
 
-    VL = rd = MIN(vlen, MVL)
+VL is very different from RVV's VL.  It contains the scalar register *number* that is to be treated as the Vector Length. It is a sub-field of STATE. When set to zero (x0) VL (vectorisation) is disabled.
 
-where 1 <= MVL <= XLEN
+Implementations realistically should keep a cached copy of the register pointed to by VL in the instruction issue and decode phases. Out of Order Engines must then, if it is not x0, add this register to Vectorised instruction Dependency Checking as an additional read/write hazard as appropriate.
 
-However just like MVL it is important to note that the range for VL has
-subtle design implications, covered in the "CSR pseudocode" section
+Setting VL via this CSR is very unusual. It should not normally be needed except when [[specification/sv.setvl]] is not implemented.  Note that unlike in sv.setvl, setting VL does not change the contents of the scalar register that it points to, although if the scalar register's contents are not within the range of MVL at the time that VL is set, an illegal instruction exception must be raised.
 
-The fixed (specific) setting of VL allows vector LOAD/STORE to be used
-to switch the entire bank of registers using a single instruction (see
-Appendix, "Context Switch Example").  The reason for limiting VL to XLEN
-is down to the fact that predication bits fit into a single register of
-length XLEN bits.
+## SUBVL - Sub Vector Length
 
-The second change is that when VSETVL is requested to be stored
-into x0, it is *ignored* silently (VSETVL x0, x5)
+This is a "group by quantity" that effectively asks each iteration
+of the hardware loop to load SUBVL elements of width elwidth at a
+time. Effectively, SUBVL is like a SIMD multiplier: instead of just 1
+operation issued, SUBVL operations are issued.
 
-The third and most important change is that, within the limits set by
-MVL, the value passed in **must** be set in VL (and in the
-destination register).
+Another way to view SUBVL is that each element in the VL length vector is
+now SUBVL times elwidth bits in length and now comprises SUBVL discrete
+sub operations.  This can be viewed as an inner SUBVL hardware for-loop within a VL hardware for-loop in effect,
+with the sub-element increased every time in the innermost loop. This
+is best illustrated in the (simplified) pseudocode example, in the
+[[appendix]].
 
-This has implication for the microarchitecture, as VL is required to be
-set (limits from MVL notwithstanding) to the actual value
-requested.  RVV has the option to set VL to an arbitrary value that suits
-the conditions and the micro-architecture: SV does *not* permit this.
+The primary use case for SUBVL is for 3D FP Vectors. A Vector of 3D
+coordinates X,Y,Z for example may be loaded and multiplied then stored, per
+VL element iteration, rather than having to set VL to three times larger.
 
-The reason is so that if SV is to be used for a context-switch or as a
-substitute for LOAD/STORE-Multiple, the operation can be done with only
-2-3 instructions (setup of the CSRs, VSETVL x0, x0, #{regfilelen-1},
-single LD/ST operation).  If VL does *not* get set to the register file
-length when VSETVL is called, then a software-loop would be needed.
-To avoid this need, VL *must* be set to exactly what is requested
-(limits notwithstanding).
+Setting this CSR to 0 must raise an exception.  Setting it to a value
+greater than 4 likewise.  To see the relationship with STATE, see below.
 
-Therefore, in turn, unlike RVV, implementors *must* provide
-pseudo-parallelism (using sequential loops in hardware) if actual
-hardware-parallelism in the ALUs is not deployed.  A hybrid is also
-permitted (as used in Broadcom's VideoCore-IV) however this must be
-*entirely* transparent to the ISA.
+The main effect of SUBVL is that predication bits are applied per
+**group**, rather than by individual element.
 
-The fourth change is that VSETVL is implemented as a CSR, where the
-behaviour of CSRRW (and CSRRWI) must be changed to specifically store
-the *new* value in the destination register, **not** the old value.
-Where context-load/save is to be implemented in the usual fashion
-by using a single CSRRW instruction to obtain the old value, the
-*secondary* CSR must be used (SVSTATE).  This CSR behaves
-exactly as standard CSRs, and contains more than just VL.
+This saves a not insignificant number of instructions when handling 3D
+vectors, as otherwise a much longer predicate mask would have to be set
+up with regularly-repeated bit patterns.
 
-One interesting side-effect of using CSRRWI to set VL is that this
-may be done with a single instruction, useful particularly for a
-context-load/save.  There are however limitations: CSRWWI's immediate
-is limited to 0-31.
+See SUBVL Pseudocode illustration in the [[appendix]], for details.
 
 ## STATE
 
+out of date, see <http://lists.libre-riscv.org/pipermail/libre-riscv-dev/2019-June/001896.html>
+
 This is a standard CSR that contains sufficient information for a
-full context save/restore.  It contains (and permits setting of)
-MVL, VL, CFG, the destination element offset of the current parallel
-instruction being executed, and, for twin-predication, the source
-element offset as well.  Interestingly it may hypothetically
-also be used to make the immediately-following instruction to skip a
-certain number of elements, however the recommended method to do
-this is predication.
+full context save/restore.  It contains (and permits setting of):
+
+* MVL
+* VL
+* destoffs - the destination element offset of the current parallel
+  instruction being executed
+* srcoffs - for twin-predication, the source element offset as well.
+* SUBVL
+* svdestoffs - the subvector destination element offset of the current
+  parallel instruction being executed
+
+Interestingly STATE may hypothetically also be modified to make the
+immediately-following instruction to skip a certain number of elements,
+by playing with destoffs and srcoffs (and the subvector offsets as well)
 
 Setting destoffs and srcoffs is realistically intended for saving state
 so that exceptions (page faults in particular) may be serviced and the
 hardware-loop that was being executed at the time of the trap, from
 user-mode (or Supervisor-mode), may be returned to and continued from
-where it left off.  The reason why this works is because setting
-User-Mode STATE will not change (not be used) in M-Mode or S-Mode
-(and is entirely why M-Mode and S-Mode have their own STATE CSRs).
+exactly where it left off.  The reason why this works is because setting
+User-Mode STATE will not change (not be used) in M-Mode or S-Mode (and
+is entirely why M-Mode and S-Mode have their own STATE CSRs, meSTATE
+and seSTATE).
 
 The format of the STATE CSR is as follows:
 
-| (28..26) | (25..24) | (23..18) | (17..12) | (11..6) | (5...0) |
-| -------- | -------- | -------- | -------- | ------- | ------- |
-| size     | bank     | destoffs | srcoffs  | vl      | maxvl   |
+| (31..28) | (27..26) | (25..24) | (23..18) | (17..12) | (11..6) | (5...0) |
+| -------- | -------- | -------- | -------- | -------- | ------- | ------- |
+| rsvd     | dsvoffs  | subvl    | destoffs | srcoffs  | vl      | maxvl   |
+
+Legal values of vl are between 0 and 31.
+
+The relationship between SUBVL and the subvl field is:
+
+| SUBVL | (25..24) |
+| ----- | -------- |
+| 1     | 0b00     |
+| 2     | 0b01     |
+| 3     | 0b10     |
+| 4     | 0b11     |
 
 When setting this CSR, the following characteristics will be enforced:
 
 * **MAXVL** will be truncated (after offset) to be within the range 1 to XLEN
-* **VL** will be truncated (after offset) to be within the range 1 to MAXVL
+* **VL** must be set to a scalar register between 0 and 31.
+* **SUBVL** which sets a SIMD-like quantity, has only 4 values so there
+  are no changes needed
 * **srcoffs** will be truncated to be within the range 0 to VL-1
 * **destoffs** will be truncated to be within the range 0 to VL-1
+* **dsvoffs** will be truncated to be within the range 0 to SUBVL-1
+
+NOTE: if the following instruction is not a twin predicated instruction,
+and destoffs or dsvoffs has been set to non-zero, subsequent execution
+behaviour is undefined. **USE WITH CARE**.
 
-## MVL, VL and CSR Pseudocode
+NOTE: sub-vector looping does not require a twin-predicate corresponding
+index, because sub-vectors use the *main* (VL) loop predicate bit.
 
-The pseudo-code for get and set of VL and MVL are as follows:
+When SVPrefix is implemented, it can have its own VL, MVL and SUBVL, as well as element offsets. SVSTATE.VL acts slightly differently in that it is no longer a pointer to a scalar register but is an actual value just like RVV's VL.
+
+The format of SVSTATE, which fits into *both* the top bits of STATE and also into a separate CSR, is as follows:
+
+| (31..28) | (27..26) | (25..24) | (23..18) | (17..12) | (11..6) | (5...0) |
+| -------- | -------- | -------- | -------- | -------- | ------- | ------- |
+| rsvd     | dsvoffs  | subvl    | destoffs | srcoffs  | vl      | maxvl   |
+
+### Hardware rules for when to increment STATE offsets
+
+The offsets inside STATE are like the indices in a loop, except
+in hardware. They are also partially (conceptually) similar to a
+"sub-execution Program Counter". As such, and to allow proper context
+switching and to define correct exception behaviour, the following rules
+must be observed:
+
+* When the VL CSR is set, srcoffs and destoffs are reset to zero.
+* Each instruction that contains a "tagged" register shall start
+  execution at the *current* value of srcoffs (and destoffs in the case
+  of twin predication)
+* Unpredicated bits (in nonzeroing mode) shall cause the element operation
+  to skip, incrementing the srcoffs (or destoffs)
+* On execution of an element operation, Exceptions shall **NOT** cause
+  srcoffs or destoffs to increment.
+* On completion of the full Vector Loop (srcoffs = VL-1 or destoffs =
+  VL-1 after the last element is executed), both srcoffs and destoffs
+  shall be reset to zero.
+
+This latter is why srcoffs and destoffs may be stored as values from
+0 to XLEN-1 in the STATE CSR, because as loop indices they refer to
+elements. srcoffs and destoffs never need to be set to VL: their maximum
+operating values are limited to 0 to VL-1.
+
+The same corresponding rules apply to SUBVL, svsrcoffs and svdestoffs.
+
+## MVL and VL Pseudocode
+
+The pseudo-code for get and set of VL and MVL use the following internal
+functions as follows:
 
     set_mvl_csr(value, rd):
-        regs[rd] = MVL
-        MVL = MIN(value, MVL)
+        STATE.MVL = MIN(value, STATE.MVL)
 
     get_mvl_csr(rd):
-        regs[rd] = VL
+        regs[rd] = STATE.VL
 
     set_vl_csr(value, rd):
-        VL = MIN(value, MVL)
-        regs[rd] = VL # yes returning the new value NOT the old CSR
+        STATE.VL = rd
+        return STATE.VL
 
     get_vl_csr(rd):
-        regs[rd] = VL
+        return STATE.VL
 
-Note that where setting MVL behaves as a normal CSR, unlike standard CSR
-behaviour, setting VL will return the **new** value of VL **not** the old
-one.
+Note that where setting MVL behaves as a normal CSR (returns the old
+value), unlike standard CSR behaviour, setting VL will return the **new**
+value of VL **not** the old one.
 
 For CSRRWI, the range of the immediate is restricted to 5 bits.  In order to
 maximise the effectiveness, an immediate of 0 is used to set VL=1,
@@ -283,20 +361,20 @@ an immediate of 1 is used to set VL=2 and so on:
     CSRRWI_Set_VL(value):
         set_vl_csr(value+1, x0)
 
-However for CSRRW the following pseudocide is used for MVL and VL,
+However for CSRRW the following pseudocode is used for MVL and VL,
 where setting the value to zero will cause an exception to be raised.
 The reason is that if VL or MVL are set to zero, the STATE CSR is
-not capable of returning that value.
+not capable of storing that value.
 
     CSRRW_Set_MVL(rs1, rd):
         value = regs[rs1]
-        if value == 0:
+        if value == 0 or value > XLEN:
             raise Exception
         set_mvl_csr(value, rd)
 
     CSRRW_Set_VL(rs1, rd):
         value = regs[rs1]
-        if value == 0:
+        if value == 0 or value > XLEN:
             raise Exception
         set_vl_csr(value, rd)
 
@@ -328,15 +406,14 @@ VL==1, "00001" represents VL==2 and so on (likewise for MVL):
     CSRRW_Set_SV_STATE(rs1, rd):
         value = regs[rs1]
         get_state_csr(rd)
-        MVL = set_mvl_csr(value[11:6]+1)
-        VL = set_vl_csr(value[5:0]+1)
-        CFG = value[28:24]>>24
-        destoffs = value[23:18]>>18
-        srcoffs = value[23:18]>>12
+        STATE.MVL = set_mvl_csr(value[11:6]+1)
+        STATE.VL = set_vl_csr(value[5:0]+1)
+        STATE.destoffs = value[23:18]>>18
+        STATE.srcoffs = value[23:18]>>12
 
     get_state_csr(rd):
-        regs[rd] = (MVL-1) | (VL-1)<<6 | (srcoffs)<<12 |
-                   (destoffs)<<18 | (CFG)<<24
+        regs[rd] = (STATE.MVL-1) | (STATE.VL-1)<<6 | (STATE.srcoffs)<<12 |
+                   (STATE.destoffs)<<18
         return regs[rd]
 
 In both cases, whilst CSR read of VL and MVL return the exact values
@@ -344,125 +421,109 @@ of VL and MVL respectively, reading and writing the STATE CSR returns
 those values **minus one**.  This is absolutely critical to implement
 if the STATE CSR is to be used for fast context-switching.
 
-## Register CSR key-value (CAM) table
+## VL, MVL and SUBVL instruction aliases
 
-The purpose of the Register CSR table is four-fold:
+This table contains pseudo-assembly instruction aliases. Note the
+subtraction of 1 from the CSRRWI pseudo variants, to compensate for the
+reduced range of the 5 bit immediate.
+
+| alias           | CSR                  |
+| -               | -                    |
+| SETVL rd, rs    | CSRRW  VL, rd, rs    |
+| SETVLi rd, #n   | CSRRWI VL, rd, #n-1  |
+| GETVL rd        | CSRRW  VL, rd, x0    |
+| SETMVL rd, rs   | CSRRW  MVL, rd, rs   |
+| SETMVLi rd, #n  | CSRRWI MVL,rd, #n-1  |
+| GETMVL rd       | CSRRW  MVL, rd, x0   |
+
+Note: CSRRC and other bitsetting may still be used, they are however not particularly useful (very obscure).
+
+## Register key-value (CAM) table <a name="regcsrtable" />
+
+*NOTE: in prior versions of SV, this table used to be writable and
+accessible via CSRs. It is now stored in the VBLOCK instruction format. Note
+that this table does *not* get applied to the SVPrefix P48/64 format,
+only to scalar opcodes*
+
+The purpose of the Register table is three-fold:
 
 * To mark integer and floating-point registers as requiring "redirection"
   if it is ever used as a source or destination in any given operation.
   This involves a level of indirection through a 5-to-7-bit lookup table,
-  such that **unmodified** operands with 5 bit (3 for Compressed) may
-  access up to **64** registers.
+  such that **unmodified** operands with 5 bits (3 for some RVC ops) may
+  access up to **128** registers.
 * To indicate whether, after redirection through the lookup table, the
   register is a vector (or remains a scalar).
 * To over-ride the implicit or explicit bitwidth that the operation would
   normally give the register.
 
-| RgCSR | | 15       | (14..8)  | 7   | (6..5) | (4..0)  |
-| ----- | | -        | -        | -   | ------ | ------- |
-| 0     | | isvec0   | regidx0  | i/f | vew0   | regkey  |
-| 1     | | isvec1   | regidx1  | i/f | vew1   | regkey  |
-| ..    | | isvec..  | regidx.. | i/f | vew..  | regkey  |
-| 15    | | isvec15  | regidx15 | i/f | vew15  | regkey  |
+Note: clearly, if an RVC operation uses a 3 bit spec'd register (x8-x15)
+and the Register table contains entried that only refer to registerd
+x1-x14 or x16-x31, such operations will *never* activate the VL hardware
+loop!
+
+If however the (16 bit) Register table does contain such an entry (x8-x15
+or x2 in the case of LWSP), that src or dest reg may be redirected
+anywhere to the *full* 128 register range. Thus, RVC becomes far more
+powerful and has many more opportunities to reduce code size that in
+Standard RV32/RV64 executables.
+
+[[!inline raw="yes" pages="simple_v_extension/reg_table_format" ]]
+
+i/f is set to "1" to indicate that the redirection/tag entry is to
+be applied to integer registers; 0 indicates that it is relevant to
+floating-point registers.
 
-i/f is set to "1" to indicate that the redirection/tag entry is to be applied
-to integer registers; 0 indicates that it is relevant to floating-point
-registers.  vew has the following meanings, indicating that the instruction's
+The 8 bit format is used for a much more compact expression. "isvec"
+is implicit and, similar to [[sv_prefix_proposal]], the target vector
+is "regnum<<2", implicitly. Contrast this with the 16-bit format where
+the target vector is *explicitly* named in bits 8 to 14, and bit 15 may
+optionally set "scalar" mode.
+
+Note that whilst SVPrefix adds one extra bit to each of rd, rs1 etc.,
+and thus the "vector" mode need only shift the (6 bit) regnum by 1 to
+get the actual (7 bit) register number to use, there is not enough space
+in the 8 bit format (only 5 bits for regnum) so "regnum<<2" is required.
+
+vew has the following meanings, indicating that the instruction's
 operand size is "over-ridden" in a polymorphic fashion:
 
-| vew | bitwidth   |
-| --- | ---------- |
-| 00  | default    |
-| 01  | default/2  |
-| 10  | default\*2 |
-| 11  | 8          |
+| vew | bitwidth            |
+| --- | ------------------- |
+| 00  | default (XLEN/FLEN) |
+| 01  | 8 bit               |
+| 10  | 16 bit              |
+| 11  | 32 bit              |
 
 As the above table is a CAM (key-value store) it may be appropriate
 (faster, implementation-wise) to expand it as follows:
 
-    struct vectorised fp_vec[32], int_vec[32];
-
-    for (i = 0; i < 16; i++) // 16 CSRs?
-       tb = int_vec if CSRvec[i].type == 0 else fp_vec
-       idx = CSRvec[i].regkey // INT/FP src/dst reg in opcode
-       tb[idx].elwidth  = CSRvec[i].elwidth
-       tb[idx].regidx   = CSRvec[i].regidx  // indirection
-       tb[idx].isvector = CSRvec[i].isvector // 0=scalar
-       tb[idx].packed   = CSRvec[i].packed  // SIMD or not
-
-The actual size of the CSR Register table depends on the platform
-and on whether other Extensions are present (RV64G, RV32E, etc.).
-For details see "Subsets" section.
-
-16-bit CSR Register CAM entries are mapped directly into 32-bit
-on any RV32-based system, however RV64 (XLEN=64) and RV128 (XLEN=128)
-are slightly different: the 16-bit entries appear (and can be set)
-multiple times, in an overlapping fashion.  Here is the table for RV64:
-
-| CSR#  | 63..48  | 47..32  | 31..16  | 15..0   |
-| 0x4c0 | RgCSR3  | RgCSR2  | RgCSR1  | RgCSR0  |
-| 0x4c1 | RgCSR5  | RgCSR4  | RgCSR3  | RgCSR2  |
-| 0x4c2 | ...     | ...     | ...     | ...     |
-| 0x4c1 | RgCSR15 | RgCSR14 | RgCSR13 | RgCSR12 |
-| 0x4c8 | n/a     | n/a     | RgCSR15 | RgCSR4  |
-
-The rules for writing to these CSRs are that any entries above the ones
-being set will be automatically wiped (to zero), so to fill several entries
-they must be written in a sequentially increasing manner.  This functionality
-was in an early draft of RVV and it means that, firstly, compilers do not have
-to spend time zero-ing out CSRs unnecessarily, and secondly, that on
-context-switching (and function calls) the number of CSRs that may need
-saving is implicitly known.
-
-The reason for the overlapping entries is that in the worst-case on an
-RV64 system, only 4 64-bit CSR reads/writes are required for a full
-context-switch (and an RV128 system, only 2 128-bit CSR reads/writes).
-
---
-
-TODO: move elsewhere
-
-    # TODO: use elsewhere (retire for now)
-    vew = CSRbitwidth[rs1]
-    if (vew == 0)
-        bytesperreg = (XLEN/8) # or FLEN as appropriate
-    elif (vew == 1)
-        bytesperreg = (XLEN/4) # or FLEN/2 as appropriate
-    else:
-        bytesperreg = bytestable[vew] # 8 or 16
-    simdmult = (XLEN/8) / bytesperreg # or FLEN as appropriate
-    vlen = CSRvectorlen[rs1] * simdmult
-    CSRvlength = MIN(MIN(vlen, MAXVECTORLENGTH), rs2)
+[[!inline raw="yes" pages="simple_v_extension/reg_table" ]]
 
-The reason for multiplying the vector length by the number of SIMD elements
-(in each individual register) is so that each SIMD element may optionally be
-predicated.
+## Predication Table <a name="predication_csr_table"></a>
 
-An example of how to subdivide the register file when bitwidth != default
-is given in the section "Bitwidth Virtual Register Reordering".
+*NOTE: in prior versions of SV, this table used to be writable and
+accessible via CSRs. It is now stored in the VBLOCK instruction format. 
+The table does **not** apply to SVPrefix opcodes*
 
-## Predication CSR <a name="predication_csr_table"></a>
+The Predication Table is a key-value store indicating whether, if a
+given destination register (integer or floating-point) is referred to
+in an instruction, it is to be predicated. Like the Register table, it
+is an indirect lookup that allows the RV opcodes to not need modification.
 
-TODO: update CSR tables, now 7-bit for regidx
-
-The Predication CSR is a key-value store indicating whether, if a given
-destination register (integer or floating-point) is referred to in an
-instruction, it is to be predicated.  Tt is particularly important to note
+It is particularly important to note
 that the *actual* register used can be *different* from the one that is
 in the instruction, due to the redirection through the lookup table.
 
-* regidx is the actual register that in combination with the
-  i/f flag, if that integer or floating-point register is referred to,
-  results in the lookup table being referenced to find the predication
-  mask to use on the operation in which that (regidx) register has
-  been used
-* predidx (in combination with the bank bit in the future) is the
-  *actual* register to be used for the predication mask.  Note:
-  in effect predidx is actually a 6-bit register address, as the bank
-  bit is the MSB (and is nominally set to zero for now).
+* regidx is the register that in combination with the
+  i/f flag, if that integer or floating-point register is referred to in a
+  (standard RV) instruction results in the lookup table being referenced
+  to find the predication mask to use for this operation.
+* predidx is the *actual* (full, 7 bit) register to be used for the
+  predication mask.
 * inv indicates that the predication mask bits are to be inverted
   prior to use *without* actually modifying the contents of the
-  register itself.
+  register from which those bits originated.
 * zeroing is either 1 or 0, and if set to 1, the operation must
   place zeros in any element position where the predication mask is
   set to zero.  If zeroing is set to 0, unpredicated elements *must*
@@ -472,41 +533,27 @@ in the instruction, due to the redirection through the lookup table.
   interpret unpredicated elements as an internal "copy element"
   operation (which would be necessary in SIMD microarchitectures
   that perform register-renaming)
-* "packed" indicates if the register is to be interpreted as SIMD
-  i.e. containing multiple contiguous elements of size equal to "bitwidth".
-  (Note: in earlier drafts this was in the Register CSR table.
-  However after extending to 7 bits there was not enough space.
-  To use "unpredicated" packed SIMD, set the predicate to x0 and
-  set "invert".  This has the effect of setting a predicate of all 1s)
-
-| PrCSR | 13     | 12     | 11    | 10  | (9..5)  | (4..0)  |
-| ----- | -      | -      | -     | -   | ------- | ------- |
-| 0     | bank0  | zero0  | inv0  | i/f | regidx  | predkey |
-| 1     | bank1  | zero1  | inv1  | i/f | regidx  | predkey |
-| ..    | bank.. | zero.. | inv.. | i/f | regidx  | predkey |
-| 15    | bank15 | zero15 | inv15 | i/f | regidx  | predkey |
-
-The Predication CSR Table is a key-value store, so implementation-wise
-it will be faster to turn the table around (maintain topologically
-equivalent state):
-
-    struct pred {
-        bool zero;
-        bool inv;
-        bool enabled;
-        int predidx; // redirection: actual int register to use
-    }
-
-    struct pred fp_pred_reg[32];   // 64 in future (bank=1)
-    struct pred int_pred_reg[32];  // 64 in future (bank=1)
-
-    for (i = 0; i < 16; i++)
-      tb = int_pred_reg if CSRpred[i].type == 0 else fp_pred_reg;
-      idx = CSRpred[i].regidx
-      tb[idx].zero = CSRpred[i].zero
-      tb[idx].inv  = CSRpred[i].inv
-      tb[idx].predidx  = CSRpred[i].predidx
-      tb[idx].enabled  = true
+* ffirst is a special mode that stops sequential element processing when
+  a data-dependent condition occurs, whether a trap or a conditional test.
+  The handling of each (trap or conditional test) is slightly different:
+  see Instruction sections for further details
+
+[[!inline raw="yes" pages="simple_v_extension/pred_table_format" ]]
+
+The 8 bit format is a compact and less expressive variant of the full
+16 bit format.  Using the 8 bit format is very different: the predicate
+register to use is implicit, and numbering begins inplicitly from x9. The
+regnum is still used to "activate" predication, in the same fashion as
+described above.
+
+The 16 bit Predication CSR Table is a key-value store, so
+implementation-wise it will be faster to turn the table around (maintain
+topologically equivalent state).  Opportunities then exist to access
+registers in unary form instead of binary, saving gates and power by
+only activating "redirection" with a single AND gate, instead of
+multiple multi-bit XORs (a CAM):
+
+[[!inline raw="yes" pages="simple_v_extension/pred_table" ]]
 
 So when an operation is to be predicated, it is the internal state that
 is used.  In Section 6.4.2 of Hwacha's Manual (EECS-2015-262) the following
@@ -520,7 +567,7 @@ reference to the predication register to be used:
                 s2 ? vreg[rs2][i] : sreg[rs2]); // for insts with 2 inputs
 
 This instead becomes an *indirect* reference using the *internal* state
-table generated from the Predication CSR key-value store, which iwws used
+table generated from the Predication CSR key-value store, which is used
 as follows.
 
     if type(iop) == INT:
@@ -531,9 +578,12 @@ as follows.
     for (int i=0; i<vl; ++i)
         predicate, zeroing = get_pred_val(type(iop) == INT, rd):
         if (predicate && (1<<i))
-           (d ? regfile[rd+i] : regfile[rd]) =
-            iop(s1 ? regfile[rs1+i] : regfile[rs1],
-                s2 ? regfile[rs2+i] : regfile[rs2]); // for insts with 2 inputs
+           result = iop(s1 ? regfile[rs1+i] : regfile[rs1],
+                        s2 ? regfile[rs2+i] : regfile[rs2]);
+           (d ? regfile[rd+i] : regfile[rd]) = result
+           if preg.ffirst and result == 0:
+              VL = i # result was zero, end loop early, return VL
+              return
         else if (zeroing)
            (d ? regfile[rd+i] : regfile[rd]) = 0
 
@@ -543,166 +593,60 @@ Note:
   source1 and source2 are vector or scalar
 * key-value CSR-redirection of rd, rs1 and rs2 have NOT been included
   above, for clarity.  rd, rs1 and rs2 all also must ALSO go through
-  register-level redirection (from the Register CSR table) if they are
+  register-level redirection (from the Register table) if they are
   vectors.
+* fail-on-first mode stops execution early whenever an operation
+  returns a zero value.  floating-point results count both
+  positive-zero as well as negative-zero as "fail".
 
 If written as a function, obtaining the predication mask (and whether
 zeroing takes place) may be done as follows:
 
-    def get_pred_val(bool is_fp_op, int reg):
-       tb = int_reg if is_fp_op else fp_reg
-       if (!tb[reg].enabled):
-          return ~0x0, False       // all enabled; no zeroing
-       tb = int_pred if is_fp_op else fp_pred
-       if (!tb[reg].enabled):
-          return ~0x0, False       // all enabled; no zeroing
-       predidx = tb[reg].predidx   // redirection occurs HERE
-       predicate = intreg[predidx] // actual predicate HERE
-       if (tb[reg].inv):
-          predicate = ~predicate   // invert ALL bits
-       return predicate, tb[reg].zero
+[[!inline raw="yes" pages="simple_v_extension/get_pred_value" ]]
 
 Note here, critically, that **only** if the register is marked
-in its CSR **register** table entry as being "active" does the testing
-proceed further to check if the CSR **predicate** table entry is
+in its **register** table entry as being "active" does the testing
+proceed further to check if the **predicate** table entry is
 also active.
 
 Note also that this is in direct contrast to branch operations
 for the storage of comparisions: in these specific circumstances
-the requirement for there to be an active CSR *register* entry
+the requirement for there to be an active *register* entry
 is removed.
 
-## REMAP CSR
-
-(Note: both the REMAP and SHAPE sections are best read after the
- rest of the document has been read)
-
-There is one 32-bit CSR which may be used to indicate which registers,
-if used in any operation, must be "reshaped" (re-mapped) from a linear
-form to a 2D or 3D transposed form.  The 32-bit REMAP CSR may reshape
-up to 3 registers:
-
-| 29..28 | 27..26 | 25..24 | 23 | 22..16  | 15 | 14..8   | 7  | 6..0    |
-| ------ | ------ | ------ | -- | ------- | -- | ------- | -- | ------- |
-| shape2 | shape1 | shape0 | 0  | regidx2 | 0  | regidx1 | 0  | regidx0 |
-
-regidx0-2 refer not to the Register CSR CAM entry but to the underlying
-*real* register (see regidx, the value) and consequently is 7-bits wide.
-shape0-2 refers to one of three SHAPE CSRs.  A value of 0x3 is reserved.
-Bits 7, 15, 23, 30 and 31 are also reserved, and must be set to zero.
-
-## SHAPE 1D/2D/3D vector-matrix remapping CSRs
-
-(Note: both the REMAP and SHAPE sections are best read after the
- rest of the document has been read)
-
-There are three "shape" CSRs, SHAPE0, SHAPE1, SHAPE2, 32-bits in each,
-which have the same format.  When each SHAPE CSR is set entirely to zeros,
-remapping is disabled: the register's elements are a linear (1D) vector.
-
-| 26..24  | 23 | 22..16  | 15 | 14..8   | 7  | 6..0    |
-| ------- | -- | ------- | -- | ------- | -- | ------- |
-| permute | 0  | zdimsz  | 0  | ydimsz  | 0  | xdimsz  |
-
-xdimsz, ydimsz and zdimsz are offset by 1, such that a value of 0 indicates
-that the array dimensionality for that dimension is 1.  A value of xdimsz=2
-would indicate that in the first dimension there are 3 elements in the
-array.  The format of the array is therefore as follows:
-
-    array[xdim+1][ydim+1][zdim+1]
-
-However whilst illustrative of the dimensionality, that does not take the
-"permute" setting into account.  "permute" may be any one of six values
-(0-5, with values of 6 and 7 being reserved, and not legal).  The table
-below shows how the permutation dimensionality order works:
-
-| permute | order | array format             |
-| ------- | ----- | ------------------------ |
-| 000     | 0,1,2 | (xdim+1)(ydim+1)(zdim+1) |
-| 001     | 0,2,1 | (xdim+1)(zdim+1)(ydim+1) |
-| 010     | 1,0,2 | (ydim+1)(xdim+1)(zdim+1) |
-| 011     | 1,2,0 | (ydim+1)(zdim+1)(xdim+1) |
-| 100     | 2,0,1 | (zdim+1)(xdim+1)(ydim+1) |
-| 101     | 2,1,0 | (zdim+1)(ydim+1)(xdim+1) |
-
-In other words, the "permute" option changes the order in which
-nested for-loops over the array would be done.  The algorithm below
-shows this more clearly, and may be executed as a python program:
-
-    # mapidx = REMAP.shape2
-    xdim = 3 # SHAPE[mapidx].xdim_sz+1
-    ydim = 4 # SHAPE[mapidx].ydim_sz+1
-    zdim = 5 # SHAPE[mapidx].zdim_sz+1
-
-    lims = [xdim, ydim, zdim]
-    idxs = [0,0,0] # starting indices
-    order = [1,0,2] # experiment with different permutations, here
-
-    for idx in range(xdim * ydim * zdim):
-        new_idx = idxs[0] + idxs[1] * xdim + idxs[2] * xdim * ydim
-        print new_idx,
-        for i in range(3):
-            idxs[order[i]] = idxs[order[i]] + 1
-            if (idxs[order[i]] != lims[order[i]]):
-                break
-            print
-            idxs[order[i]] = 0
-
-Here, it is assumed that this algorithm be run within all pseudo-code
-throughout this document where a (parallelism) for-loop would normally
-run from 0 to VL-1 to refer to contiguous register
-elements; instead, where REMAP indicates to do so, the element index
-is run through the above algorithm to work out the **actual** element
-index, instead.  Given that there are three possible SHAPE entries, up to
-three separate registers in any given operation may be simultaneously
-remapped:
-
-    function op_add(rd, rs1, rs2) # add not VADD!
-      ...
-      ...
-      for (i = 0; i < VL; i++)
-        if (predval & 1<<i) # predication uses intregs
-           ireg[rd+remap(id)] <= ireg[rs1+remap(irs1)] +
-                                 ireg[rs2+remap(irs2)];
-        if (int_vec[rd ].isvector)  { id += 1; }
-        if (int_vec[rs1].isvector)  { irs1 += 1; }
-        if (int_vec[rs2].isvector)  { irs2 += 1; }
-
-By changing remappings, 2D matrices may be transposed "in-place" for one
-operation, followed by setting a different permutation order without
-having to move the values in the registers to or from memory.  Also,
-the reason for having REMAP separate from the three SHAPE CSRs is so
-that in a chain of matrix multiplications and additions, for example,
-the SHAPE CSRs need only be set up once; only the REMAP CSR need be
-changed to target different registers.
-
-Note that:
-
-* If permute option 000 is utilised, the actual order of the
-  reindexing does not change!
-* If two or more dimensions are set to zero, the actual order does not change!
-* The above algorithm is pseudo-code **only**.  Actual implementations
-  will need to take into account the fact that the element for-looping
-  must be **re-entrant**, due to the possibility of exceptions occurring.
-  See MSTATE CSR, which records the current element index.
-* Twin-predicated operations require **two** separate and distinct
-  element offsets.  The above pseudo-code algorithm will be applied
-  separately and independently to each, should each of the two
-  operands be remapped.  *This even includes C.LDSP* and other operations
-  in that category, where in that case it will be the **offset** that is
-  remapped (see Compressed Stack LOAD/STORE section).
-* Setting the total elements (xdim+1) times (ydim+1) times (zdim+1) to
-  less than MVL is **perfectly legal**, albeit very obscure.  It permits
-  entries to be regularly presented to operands **more than once**, thus
-  allowing the same underlying registers to act as an accumulator of
-  multiple vector or matrix operations, for example.
-
-Clearly here some considerable care needs to be taken as the remapping
-could hypothetically create arithmetic operations that target the
-exact same underlying registers, resulting in data corruption due to
-pipeline overlaps.  Out-of-order / Superscalar micro-architectures with
-register-renaming will have an easier time dealing with this than
-DSP-style SIMD micro-architectures.
+## Fail-on-First Mode <a name="ffirst-mode"></a>
+
+ffirst is a special data-dependent predicate mode.  There are two
+variants: one is for faults: typically for LOAD/STORE operations,
+which may encounter end of page faults during a series of operations.
+The other variant is comparisons such as FEQ (or the augmented behaviour
+of Branch), and any operation that returns a result of zero (whether
+integer or floating-point).  In the FP case, this includes negative-zero.
+
+ffirst interacts with zero- and non-zero predication.  In non-zeroing
+mode, masked-out operations are simply excluded from testing (can never
+fail).  However for fail-comparisons (not faults) in zeroing mode, the
+result will be zero: this *always* "fails", thus on the very first
+masked-out element ffirst will always terminate.
+
+Note that ffirst mode works because the execution order must "appear" to be
+(in "program order").  An in-order architecture must execute the element
+operations in sequence, whilst an out-of-order architecture must *commit*
+the element operations in sequence and cancel speculatively-executed
+ones (giving the appearance of in-order execution).
+
+Note also, that if ffirst mode is needed without predication, a special
+"always-on" Predicate Table Entry may be constructed by setting
+inverse-on and using x0 as the predicate register.  This
+will have the effect of creating a mask of all ones, allowing ffirst
+to be set.
+
+See [[appendix]] for more details on fail-on-first modes, as well as
+pseudo-code, below.
+
+## REMAP and SHAPE CSRs <a name="remap" />
+
+See optional [[remap]] section.
 
 # Instruction Execution Order
 
@@ -729,439 +673,43 @@ It is **purely** about compacting what would otherwise be contiguous
 instructions that use sequentially-increasing register numbers down
 to the **one** instruction.
 
-# Instructions
-
-Despite being a 98% complete and accurate topological remap of RVV
-concepts and functionality, no new instructions are needed.
-Compared to RVV: *All* RVV instructions can be re-mapped, however xBitManip
-becomes a critical dependency for efficient manipulation of predication
-masks (as a bit-field).  Despite the removal of all operations,
-with the exception of CLIP and VSELECT.X
-*all instructions from RVV Base are topologically re-mapped and retain their
-complete functionality, intact*.  Note that if RV64G ever had
-a MV.X added as well as FCLIP, the full functionality of RVV-Base would
-be obtained in SV.
-
-Three instructions, VSELECT, VCLIP and VCLIPI, do not have RV Standard
-equivalents, so are left out of Simple-V.  VSELECT could be included if
-there existed a MV.X instruction in RV (MV.X is a hypothetical
-non-immediate variant of MV that would allow another register to
-specify which register was to be copied).  Note that if any of these three
-instructions are added to any given RV extension, their functionality
-will be inherently parallelised.
-
-With some exceptions, where it does not make sense or is simply too
-challenging, all RV-Base instructions are parallelised:
-
-* CSR instructions, whilst a case could be made for fast-polling of
-  a CSR into multiple registers, would require guarantees of strict
-  sequential ordering that SV does not provide.  Therefore, CSRs are
-  not really suitable and are left out.
-* LUI, C.J, C.JR, WFI, AUIPC are not suitable for parallelising so are
-  left as scalar.
-* LR/SC could hypothetically be parallelised however their purpose is
-  single (complex) atomic memory operations where the LR must be followed
-  up by a matching SC.  A sequence of parallel LR instructions followed
-  by a sequence of parallel SC instructions therefore is guaranteed to
-  not be useful. Not least: the guarantees of LR/SC
-  would be impossible to provide if emulated in a trap.
-* EBREAK, NOP, FENCE and others do not use registers so are not inherently
-  paralleliseable anyway.
-
-All other operations using registers are automatically parallelised.
-This includes AMOMAX, AMOSWAP and so on, where particular care and
-attention must be paid.
-
-Example pseudo-code for an integer ADD operation (including scalar operations).
-Floating-point uses fp csrs.
-
-    function op_add(rd, rs1, rs2) # add not VADD!
-      int i, id=0, irs1=0, irs2=0;
-      predval = get_pred_val(FALSE, rd);
-      rd  = int_vec[rd ].isvector ? int_vec[rd ].regidx : rd;
-      rs1 = int_vec[rs1].isvector ? int_vec[rs1].regidx : rs1;
-      rs2 = int_vec[rs2].isvector ? int_vec[rs2].regidx : rs2;
-      for (i = 0; i < VL; i++)
-        if (predval & 1<<i) # predication uses intregs
-           ireg[rd+id] <= ireg[rs1+irs1] + ireg[rs2+irs2];
-        if (int_vec[rd ].isvector)  { id += 1; }
-        if (int_vec[rs1].isvector)  { irs1 += 1; }
-        if (int_vec[rs2].isvector)  { irs2 += 1; }
-
-## Instruction Format
-
-There are **no operations added to SV, at all**.
-Instead SV  *overloads* pre-existing branch operations into predicated
-variants, and implicitly overloads arithmetic operations, MV,
-FCVT, and LOAD/STORE
-depending on CSR configurations for bitwidth and
-predication.  **Everything** becomes parallelised.  *This includes
-Compressed instructions* as well as any
-future instructions and Custom Extensions.
-
-## Branch Instructions
-
-### Standard Branch <a name="standard_branch"></a>
-
-Branch operations use standard RV opcodes that are reinterpreted to
-be "predicate variants" in the instance where either of the two src
-registers are marked as vectors (active=1, vector=1).
-
-Note that he predication register to use (if one is enabled) is taken from
-the *first* src register.  The target (destination) predication register
-to use (if one is enabled) is taken from the *second* src register.
-
-If either of src1 or src2 are scalars (whether by there being no
-CSR register entry or whether by the CSR entry specifically marking
-the register as "scalar") the comparison goes ahead as vector-scalar
-or scalar-vector.
-
-In instances where no vectorisation is detected on either src registers
-the operation is treated as an absolutely standard scalar branch operation.
-Where vectorisation is present on either or both src registers, the
-branch may stil go ahead if any only if *all* tests succeed (i.e. excluding
-those tests that are predicated out).
-
-Note that just as with the standard (scalar, non-predicated) branch
-operations, BLE, BGT, BLEU and BTGU may be synthesised by inverting
-src1 and src2.
-
-In Hwacha EECS-2015-262 Section 6.7.2 the following pseudocode is given
-for predicated compare operations of function "cmp":
+# Instructions <a name="instructions" />
 
-    for (int i=0; i<vl; ++i)
-      if ([!]preg[p][i])
-         preg[pd][i] = cmp(s1 ? vreg[rs1][i] : sreg[rs1],
-                           s2 ? vreg[rs2][i] : sreg[rs2]);
-
-With associated predication, vector-length adjustments and so on,
-and temporarily ignoring bitwidth (which makes the comparisons more
-complex), this becomes:
-
-    s1 = reg_is_vectorised(src1);
-    s2 = reg_is_vectorised(src2);
-
-    if not s1 && not s2
-        if cmp(rs1, rs2) # scalar compare
-            goto branch
-        return
-
-    preg = int_pred_reg[rd]
-    reg = int_regfile
-
-    ps = get_pred_val(I/F==INT, rs1);
-    rd = get_pred_val(I/F==INT, rs2); # this may not exist
-
-    if not exists(rd)
-        temporary_result = 0
-    else
-        preg[rd] = 0; # initialise to zero
-
-    for (int i = 0; i < VL; ++i)
-      if (ps & (1<<i)) && (cmp(s1 ? reg[src1+i]:reg[src1],
-                               s2 ? reg[src2+i]:reg[src2])
-          if not exists(rd)
-              temporary_result |= 1<<i;
-          else
-              preg[rd] |= 1<<i;  # bitfield not vector
-
-     if not exists(rd)
-        if temporary_result == ps
-            goto branch
-     else
-        if preg[rd] == ps
-            goto branch
-
-Notes:
-
-* zeroing has been temporarily left out of the above pseudo-code,
-  for clarity
-* Predicated SIMD comparisons would break src1 and src2 further down
-  into bitwidth-sized chunks (see Appendix "Bitwidth Virtual Register
-  Reordering") setting Vector-Length times (number of SIMD elements) bits
-  in Predicate Register rd, as opposed to just Vector-Length bits.
-
-TODO: predication now taken from src2.  also branch goes ahead
-if all compares are successful.
-
-Note also that where normally, predication requires that there must
-also be a CSR register entry for the register being used in order
-for the **predication** CSR register entry to also be active,
-for branches this is **not** the case.  src2 does **not** have
-to have its CSR register entry marked as active in order for
-predication on src2 to be active.
-
-### Floating-point Comparisons
-
-There does not exist floating-point branch operations, only compare.
-Interestingly no change is needed to the instruction format because
-FP Compare already stores a 1 or a zero in its "rd" integer register
-target, i.e. it's not actually a Branch at all: it's a compare.
-Thus, no change is made to the floating-point comparison, so
-
-It is however noted that an entry "FNE" (the opposite of FEQ) is missing,
-and whilst in ordinary branch code this is fine because the standard
-RVF compare can always be followed up with an integer BEQ or a BNE (or
-a compressed comparison to zero or non-zero), in predication terms that
-becomes more of an impact.  To deal with this, SV's predication has
-had "invert" added to it.
-
-### Compressed Branch Instruction
-
-Compressed Branch instructions are, just like standard Branch instructions,
-reinterpreted to be vectorised and predicated based on the source register
-(rs1s) CSR entries.  As however there is only the one source register,
-given that c.beqz a10 is equivalent to beqz a10,x0, the optional target
-to store the results of the comparisions is taken from CSR predication
-table entries for **x0**.
-
-The specific required use of x0 is, with a little thought, quite obvious,
-but is counterintuitive.  Clearly it is **not** recommended to redirect
-x0 with a CSR register entry, however as a means to opaquely obtain
-a predication target it is the only sensible option that does not involve
-additional special CSRs (or, worse, additional special opcodes).
-
-Note also that, just as with standard branches, the 2nd source
-(in this case x0 rather than src2) does **not** have to have its CSR
-register table marked as "active" in order for predication to work.
-
-## Vectorised Dual-operand instructions
-
-There is a series of 2-operand instructions involving copying (and
-sometimes alteration):
-
-* C.MV
-* FMV, FNEG, FABS, FCVT, FSGNJ, FSGNJN and FSGNJX
-* C.LWSP, C.SWSP, C.LDSP, C.FLWSP etc.
-* LOAD(-FP) and STORE(-FP)
-
-All of these operations follow the same two-operand pattern, so it is
-*both* the source *and* destination predication masks that are taken into
-account.  This is different from
-the three-operand arithmetic instructions, where the predication mask
-is taken from the *destination* register, and applied uniformly to the
-elements of the source register(s), element-for-element.
-
-The pseudo-code pattern for twin-predicated operations is as
-follows:
-
-    function op(rd, rs):
-      rd = int_csr[rd].active ? int_csr[rd].regidx : rd;
-      rs = int_csr[rs].active ? int_csr[rs].regidx : rs;
-      ps = get_pred_val(FALSE, rs); # predication on src
-      pd = get_pred_val(FALSE, rd); # ... AND on dest
-      for (int i = 0, int j = 0; i < VL && j < VL;):
-        if (int_csr[rs].isvec) while (!(ps & 1<<i)) i++;
-        if (int_csr[rd].isvec) while (!(pd & 1<<j)) j++;
-        reg[rd+j] = SCALAR_OPERATION_ON(reg[rs+i])
-        if (int_csr[rs].isvec) i++;
-        if (int_csr[rd].isvec) j++;
-
-This pattern covers scalar-scalar, scalar-vector, vector-scalar
-and vector-vector, and predicated variants of all of those.
-Zeroing is not presently included (TODO).  As such, when compared
-to RVV, the twin-predicated variants of C.MV and FMV cover
-**all** standard vector operations: VINSERT, VSPLAT, VREDUCE,
-VEXTRACT, VSCATTER, VGATHER, VCOPY, and more.
-
-Note that:
-
-* elwidth (SIMD) is not covered in the pseudo-code above
-* ending the loop early in scalar cases (VINSERT, VEXTRACT) is also
-  not covered
-* zero predication is also not shown (TODO).
-
-### C.MV Instruction <a name="c_mv"></a>
-
-There is no MV instruction in RV however there is a C.MV instruction.
-It is used for copying integer-to-integer registers (vectorised FMV
-is used for copying floating-point).
-
-If either the source or the destination register are marked as vectors
-C.MV is reinterpreted to be a vectorised (multi-register) predicated
-move operation.  The actual instruction's format does not change:
-
-[[!table  data="""
-15  12 | 11   7 | 6  2 | 1  0 |
-funct4 | rd     | rs   | op   |
-4      | 5      | 5    | 2    |
-C.MV   | dest   | src  | C0   |
-"""]]
-
-A simplified version of the pseudocode for this operation is as follows:
-
-    function op_mv(rd, rs) # MV not VMV!
-      rd = int_csr[rd].active ? int_csr[rd].regidx : rd;
-      rs = int_csr[rs].active ? int_csr[rs].regidx : rs;
-      ps = get_pred_val(FALSE, rs); # predication on src
-      pd = get_pred_val(FALSE, rd); # ... AND on dest
-      for (int i = 0, int j = 0; i < VL && j < VL;):
-        if (int_csr[rs].isvec) while (!(ps & 1<<i)) i++;
-        if (int_csr[rd].isvec) while (!(pd & 1<<j)) j++;
-        ireg[rd+j] <= ireg[rs+i];
-        if (int_csr[rs].isvec) i++;
-        if (int_csr[rd].isvec) j++;
-
-There are several different instructions from RVV that are covered by
-this one opcode:
-
-[[!table  data="""
-src    | dest    | predication   | op             |
-scalar | vector  | none          | VSPLAT         |
-scalar | vector  | destination   | sparse VSPLAT  |
-scalar | vector  | 1-bit dest    | VINSERT        |
-vector | scalar  | 1-bit? src    | VEXTRACT       |
-vector | vector  | none          | VCOPY          |
-vector | vector  | src           | Vector Gather  |
-vector | vector  | dest          | Vector Scatter |
-vector | vector  | src & dest    | Gather/Scatter |
-vector | vector  | src == dest   | sparse VCOPY   |
-"""]]
-
-Also, VMERGE may be implemented as back-to-back (macro-op fused) C.MV
-operations with inversion on the src and dest predication for one of the
-two C.MV operations.
-
-Note that in the instance where the Compressed Extension is not implemented,
-MV may be used, but that is a pseudo-operation mapping to addi rd, x0, rs.
-Note that the behaviour is **different** from C.MV because with addi the
-predication mask to use is taken **only** from rd and is applied against
-all elements: rs[i] = rd[i].
-
-### FMV, FNEG and FABS Instructions
-
-These are identical in form to C.MV, except covering floating-point
-register copying.  The same double-predication rules also apply.
-However when elwidth is not set to default the instruction is implicitly
-and automatic converted to a (vectorised) floating-point type conversion
-operation of the appropriate size covering the source and destination
-register bitwidths.
-
-(Note that FMV, FNEG and FABS are all actually pseudo-instructions)
-
-### FVCT Instructions
-
-These are again identical in form to C.MV, except that they cover
-floating-point to integer and integer to floating-point.  When element
-width in each vector is set to default, the instructions behave exactly
-as they are defined for standard RV (scalar) operations, except vectorised
-in exactly the same fashion as outlined in C.MV.
-
-However when the source or destination element width is not set to default,
-the opcode's explicit element widths are *over-ridden* to new definitions,
-and the opcode's element width is taken as indicative of the SIMD width
-(if applicable i.e. if packed SIMD is requested) instead.
-
-For example FCVT.S.L would normally be used to convert a 64-bit
-integer in register rs1 to a 64-bit floating-point number in rd.
-If however the source rs1 is set to be a vector, where elwidth is set to
-default/2 and "packed SIMD" is enabled, then the first 32 bits of
-rs1 are converted to a floating-point number to be stored in rd's
-first element and the higher 32-bits *also* converted to floating-point
-and stored in the second.  The 32 bit size comes from the fact that
-FCVT.S.L's integer width is 64 bit, and with elwidth on rs1 set to
-divide that by two it means that rs1 element width is to be taken as 32.
-
-Similar rules apply to the destination register.
-
-## LOAD / STORE Instructions and LOAD-FP/STORE-FP <a name="load_store"></a>
-
-An earlier draft of SV modified the behaviour of LOAD/STORE.  This
-actually undermined the fundamental principle of SV, namely that there
-be no modifications to the scalar behaviour (except where absolutely
-necessary), in order to simplify an implementor's task if considering
-converting a pre-existing scalar design to support parallelism.
-
-So the original RISC-V scalar LOAD/STORE and LOAD-FP/STORE-FP functionality
-do not change in SV, however just as with C.MV it is important to note
-that dual-predication is possible.  Using the template outlined in
-the section "Vectorised dual-op instructions", the pseudo-code covering
-scalar-scalar, scalar-vector, vector-scalar and vector-vector applies,
-where SCALAR\_OPERATION is as follows, exactly as for a standard
-scalar RV LOAD operation:
-
-        srcbase = ireg[rs+i];
-        return mem[srcbase + imm];
-
-Whilst LOAD and STORE remain as-is when compared to their scalar
-counterparts, the incrementing on the source register (for LOAD)
-means that pointers-to-structures can be easily implemented, and
-if contiguous offsets are required, those pointers (the contents
-of the contiguous source registers) may simply be set up to point
-to contiguous locations.
-
-## Compressed Stack LOAD / STORE Instructions <a name="c_ld_st"></a>
-
-C.LWSP / C.SWSP and floating-point etc. are also source-dest twin-predicated,
-where it is implicit in C.LWSP/FLWSP that x2 is the source register.
-It is therefore possible to use predicated C.LWSP to efficiently
-pop registers off the stack (by predicating x2 as the source), cherry-picking
-which registers to store to (by predicating the destination).  Likewise
-for C.SWSP.  In this way, LOAD/STORE-Multiple is efficiently achieved.
-
-However, to do so, the behaviour of C.LWSP/C.SWSP needs to be slightly
-different: where x2 is marked as vectorised, instead of incrementing
-the register on each loop (x2, x3, x4...), instead it is the *immediate*
-that must be incremented.  Pseudo-code follows:
-
-    function lwsp(rd, rs):
-      rd = int_csr[rd].active ? int_csr[rd].regidx : rd;
-      rs = x2 # effectively no redirection on x2.
-      ps = get_pred_val(FALSE, rs); # predication on src
-      pd = get_pred_val(FALSE, rd); # ... AND on dest
-      for (int i = 0, int j = 0; i < VL && j < VL;):
-        if (int_csr[rs].isvec) while (!(ps & 1<<i)) i++;
-        if (int_csr[rd].isvec) while (!(pd & 1<<j)) j++;
-        reg[rd+j] = mem[x2 + ((offset+i) * 4)]
-        if (int_csr[rs].isvec) i++;
-        if (int_csr[rd].isvec) j++;
-
-For C.LDSP, the offset (and loop) multiplier would be 8, and for
-C.LQSP it would be 16.  Effectively this makes C.LWSP etc. a Vector
-"Unit Stride" Load instruction.
-
-**Note**: it is still possible to redirect x2 to an alternative target
-register.  With care, this allows C.LWSP / C.SWSP (and C.FLWSP) to be used as
-general-purpose Vector "Unit Stride" LOAD/STORE operations.
-
-## Compressed LOAD / STORE Instructions
-
-Compressed LOAD and STORE are again exactly the same as scalar LOAD/STORE,
-where the same rules apply and the same pseudo-code apply as for
-non-compressed LOAD/STORE.  This is **different** from Compressed Stack
-LOAD/STORE (C.LWSP / C.SWSP), which have been augmented to become
-Vector "Unit Stride" capable.
-
-Just as with uncompressed LOAD/STORE C.LD / C.ST increment the *register*
-during the hardware loop, **not** the offset.
-
-# Element bitwidth polymorphism
-
-Element bitwidth is best covered as its own special section, as it
-is quite involved and applies uniformly across-the-board.
+See [[appendix]] for specific cases where instruction behaviour is
+augmented.  A greatly simplified example is below.  Note that this
+is the ADD implementation, not a separate VADD instruction:
+
+[[!inline raw="yes" pages="simple_v_extension/simple_add_example" ]]
+
+Note that several things have been left out of this example.
+See [[appendix]] for additional examples that show how to add
+support for additional features (twin predication, elwidth,
+zeroing, SUBVL etc.)
+
+Branches in particular have been transparently augmented to include
+"collation" of comparison results into a tagged register.
 
 # Exceptions
 
-TODO: expand.  Exceptions may occur at any time, in any given underlying
-scalar operation.  This implies that context-switching (traps) may
-occur, and operation must be returned to where it left off.  That in
-turn implies that the full state - including the current parallel
-element being processed - has to be saved and restored.  This is
-what the **STATE** CSR is for.
+Exceptions may occur at any time, in any given underlying scalar
+operation.  This implies that context-switching (traps) may occur, and
+operation must be returned to where it left off.  That in turn implies
+that the full state - including the current parallel element being
+processed - has to be saved and restored.  This is what the **STATE**
+and **PCVBLK** CSRs are for.
 
 The implications are that all underlying individual scalar operations
 "issued" by the parallelisation have to appear to be executed sequentially.
 The further implications are that if two or more individual element
 operations are underway, and one with an earlier index causes an exception,
-it may be necessary for the microarchitecture to **discard** or terminate
-operations with higher indices.
-
-This being somewhat dissatisfactory, an "opaque predication" variant
-of the STATE CSR is being considered.
+it will be necessary for the microarchitecture to **discard** or terminate
+operations with higher indices.  Optimisated microarchitectures could
+hypothetically store (cache) results, for subsequent replay if appropriate.
 
-> And what about instructions like JALR? 
-
-answer: they're not vectorised, so not a problem
+In short: exception handling **MUST** be precise, in-order, and exactly
+like Standard RISC-V as far as the instruction execution order is
+concerned, regardless of whether it is PC, PCVBLK, VL or SUBVL that
+is currently being incremented.
 
 # Hints
 
@@ -1178,73 +726,15 @@ in as a *parameter* to the HINT operation.
 
 No specific hints are yet defined in Simple-V
 
-# Subsets of RV functionality
-
-This section describes the differences when SV is implemented on top of
-different subsets of RV.
-
-## Common options
-
-It is permitted to limit the size of either (or both) the register files
-down to the original size of the standard RV architecture.  However, below
-the mandatory limits set in the RV standard will result in non-compliance
-with the SV Specification.
-
-## RV32 / RV32F
-
-When RV32 or RV32F is implemented, XLEN is set to 32, and thus the
-maximum limit for predication is also restricted to 32 bits.  Whilst not
-actually specifically an "option" it is worth noting.
+# Vector Block Format <a name="vliw-format"></a>
 
-## RV32G
+The VBLOCK Format allows Register, Predication and Vector Length to be contextually associated with a group of RISC-V scalar opcodes.  The format is as follows:
 
-Normally in standard RV32 it does not make much sense to have
-RV32G, however it is automatically implied to exist in RV32+SV due to
-the option for the element width to be doubled.  This may be sufficient
-for implementors, such that actually needing RV32G itself (which makes
-no sense given that the RV32 integer register file is 32-bit) may be
-redundant.
+[[!inline raw="yes" pages="simple_v_extension/vblock_format_table" ]]
 
-It is a strange combination that may make sense on closer inspection,
-particularly given that under the standard RV32 system many of the opcodes
-to convert and sign-extend 64-bit integers to 64-bit floating-point will
-be missing, as they are assumed to only be present in an RV64 context.
-
-## RV32 (not RV32F / RV32G) and RV64 (not RV64F / RV64G)
-
-When floating-point is not implemented, the size of the User Register and
-Predication CSR tables may be halved, to only 4 2x16-bit CSRs (8 entries
-per table).
-
-## RV32E
-
-In embedded scenarios the User Register and Predication CSRs may be
-dropped entirely, or optionally limited to 1 CSR, such that the combined
-number of entries from the M-Mode CSR Register table plus U-Mode
-CSR Register table is either 4 16-bit entries or (if the U-Mode is
-zero) only 2 16-bit entries (M-Mode CSR table only).  Likewise for
-the Predication CSR tables.
-
-RV32E is the most likely candidate for simply detecting that registers
-are marked as "vectorised", and generating an appropriate exception
-for the VL loop to be implemented in software.
-
-## RV128
-
-RV128 has not been especially considered, here, however it has some
-extremely large possibilities: double the element width implies
-256-bit operands, spanning 2 128-bit registers each, and predication
-of total length 128 bit given that XLEN is now 128.
+For more details, including the CSRs, see ancillary resource: [[vblock_format]]
 
 # Under consideration <a name="issues"></a>
 
-for element-grouping, if there is unused space within a register
-(3 16-bit elements in a 64-bit register for example), recommend:
-
-* For the unused elements in an integer register, the used element
-  closest to the MSB is sign-extended on write and the unused elements
-  are ignored on read.
-* The unused elements in a floating-point register are treated as-if
-  they are set to all ones on write and are ignored on read, matching the
-  existing standard for storing smaller FP values in larger registers.
+See [[discussion]]