(no commit message)
[libreriscv.git] / openpower / sv / overview.mdwn
index e2ecc69c0c6402654d1ef752f8fc3016cb979439..9f37c0198a2187f20dfd4eccf9afc034714c135b 100644 (file)
@@ -1,17 +1,29 @@
 # SV Overview
 
+**SV is in DRAFT STATUS**. SV has not yet been submitted to the OpenPOWER Foundation ISA WG for review.
+
 This document provides an overview and introduction as to why SV (a
-Cray-style Vector augmentation to OpenPOWER) exists, and how it works.
+[[!wikipedia Cray]]-style Vector augmentation to [[!wikipedia OpenPOWER]]) exists, and how it works.
+
+**Sponsored by NLnet under the Privacy and Enhanced Trust Programme**
 
 Links:
 
+* This page: [http://libre-soc.org/openpower/sv/overview](http://libre-soc.org/openpower/sv/overview)
+* [FOSDEM2021 SimpleV for OpenPOWER](https://fosdem.org/2021/schedule/event/the_libresoc_project_simple_v_vectorisation/)
+* FOSDEM2021 presentation <https://www.youtube.com/watch?v=FS6tbfyb2VA>
 * [[discussion]] and
   [bugreport](https://bugs.libre-soc.org/show_bug.cgi?id=556)
   feel free to add comments, questions.
 * [[SV|sv]]
 * [[sv/svp64]]
+* [x86 REP instruction](https://c9x.me/x86/html/file_module_x86_id_279.html):
+  a useful way to quickly understand that the core of the SV concept
+  is not new.
+* [Article about register tagging](http://science.lpnu.ua/sites/default/files/journal-paper/2019/jul/17084/volum3number1text-9-16_1.pdf) showing
+  that tagging is not a new idea either. Register tags
+  are also used in the Mill Architecture.
 
-Contents:
 
 [[!toc]]
 
@@ -37,6 +49,10 @@ the typical dimensions that result in such massive proliferation:
 * Conversion between bitwidths (FP16-FP32-64)
 * Signed/unsigned
 * HI/LO swizzle (Audio L/R channels)
+   - HI/LO selection on src 1
+   - selection on src 2
+   - selection on dest
+   - Example: AndesSTAR Audio DSP 
 * Saturation (Clamping at max range)
 
 These typically are multiplied up to produce explicit opcodes numbering
@@ -53,13 +69,14 @@ Unlike in SIMD, powers of two limitations are not involved in the ISA
 or in the assembly code.
 
 SimpleV takes the Cray style Vector principle and applies it in the
-abstract to a Scalar ISA, in the process allowing register file size
-increases using "tagging" (similar to how x86 originally extended
+abstract to a Scalar ISA in the same way that x86 used to do its "REP" instruction.  In the process, "context" is applied, allowing amongst other things
+a register file size
+increase using "tagging" (similar to how x86 originally extended
 registers from 32 to 64 bit).
 
 ## SV
 
-The fundamentals are:
+The fundamentals are (just like x86 "REP"):
 
 * The Program Counter (PC) gains a "Sub Counter" context (Sub-PC)
 * Vectorisation pauses the PC and runs a Sub-PC loop from 0 to VL-1
@@ -279,7 +296,7 @@ in Vector ISAs:
   and the destination a vector, and having no predicate set or having
   multiple bits set.
 * VSELECT is provided by setting up (at least one of) the sources as a
-  vector, using a single bit in olthe predicate, and the destination as
+  vector, using a single bit in the predicate, and the destination as
   a scalar.
 
 All of this capability and coverage without even adding one single actual
@@ -456,7 +473,7 @@ element width.  Our first simple loop thus becomes:
        src1 = get_polymorphed_reg(RA, srcwid, i)
        src2 = get_polymorphed_reg(RB, srcwid, i)
        result = src1 + src2 # actual add here
-       set_polymorphed_reg(rd, destwid, i, result)
+       set_polymorphed_reg(RT, destwid, i, result)
 
 With this loop, if elwidth=16 and VL=3 the first 48 bits of the target
 register will contain three 16 bit addition results, and the upper 16
@@ -464,10 +481,9 @@ bits will be *unaltered*.
 
 Note that things such as zero/sign-extension (and predication) have
 been left out to illustrate the elwidth concept. Also note that it turns
-out to be important to perform the operation at the maximum bitwidth -
-`max(srcwid, destwid)` - such that any truncation, rounding errors or
+out to be important to perform the operation internally at effectively an *infinite* bitwidth such that any truncation, rounding errors or
 other artefacts may all be ironed out.  This turns out to be important
-when applying Saturation for Audio DSP workloads.
+when applying Saturation for Audio DSP workloads, particularly for multiply and IEEE754 FP rounding.  By "infinite" this is conceptual only: in reality, the application of the different truncations and width-extensions set a fixed deterministic practical limit on the internal precision needed, on a per-operation basis.
 
 Other than that, element width overrides, which can be applied to *either*
 source or destination or both, are pretty straightforward, conceptually.
@@ -482,7 +498,7 @@ of the destination.  The only situation where a full overwrite occurs
 is on "default" behaviour.  This is extremely important to consider the
 register file as a byte-level store, not a 64-bit-level store.
 
-## Why LE regfile?
+## Why LE regfile?
 
 The concept of having a regfile where the byte ordering of the underlying
 SRAM seems utter nonsense.  Surely, a hardware implementation gets to
@@ -503,18 +519,28 @@ clarity and assumes significant working knowledge of OpenPOWER, with
 arbitrary insertions of 7-index here and 3-bitindex there, the decision
 to pick LE was extremely easy.
 
-Without such a decision, if two words are packed as elements into a
-64 bit register, what does this mean? Should they be inverted so that
-the lower indexed element goes into the HI or the LO word? should the
-8 bytes of each register be inverted? Should the bytes in each element
-be inverted? These arw all equally valid and legitimate interpretations
-of what constitutes "BE" and rhey all cause merry mayhem.
+Without such a decision, if two words are packed as elements into a 64
+bit register, what does this mean? Should they be inverted so that the
+lower indexed element goes into the HI or the LO word? should the 8
+bytes of each register be inverted? Should the bytes in each element
+be inverted? Should the element indexing loop order be broken onto
+discontiguous chunks such as 32107654 rather than 01234567, and if so
+at what granularity of discontinuity? These are all equally valid and
+legitimate interpretations of what constitutes "BE" and they all cause
+merry mayhem.
 
 The decision was therefore made: the c typedef union is the canonical
 definition, and its members are defined as being in LE order. From there,
 implementations may choose whatever internal HDL wire order they like
 as long as the results produced conform to the elwidth pseudocode.
 
+*Note: it turns out that both x86 SIMD and NEON SIMD follow this convention, namely that both are implicitly LE, even though their ISA Manuals may not explicitly spell this out*
+
+* <https://developer.arm.com/documentation/ddi0406/c/Application-Level-Architecture/Application-Level-Memory-Model/Endian-support/Endianness-in-Advanced-SIMD?lang=en>
+* <https://stackoverflow.com/questions/24045102/how-does-endianness-work-with-simd-registers>
+* <https://llvm.org/docs/BigEndianNEON.html>
+
+
 ## Source and Destination overrides
 
 A minor fly in the ointment: what happens if the source and destination
@@ -522,18 +548,23 @@ are over-ridden to different widths?  For example, FP16 arithmetic is
 not accurate enough and may introduce rounding errors when up-converted
 to FP32 output.  The rule is therefore set:
 
-    The operation MUST take place at the larger of the two widths.
+    The operation MUST take place effectively at infinite precision:
+    actual precision determined by the operation and the operand widths
 
 In pseudocode this is:
 
     for i = 0 to VL-1:
        src1 = get_polymorphed_reg(RA, srcwid, i)
        src2 = get_polymorphed_reg(RB, srcwid, i)
-       opwidth = max(srcwid, destwid)
+       opwidth = max(srcwid, destwid) # usually
        result = op_add(src1, src2, opwidth) # at max width
        set_polymorphed_reg(rd, destwid, i, result)
 
-It will turn out that under some conditions the combination of the
+In reality the source and destination widths determine the actual required
+precision in a given ALU.  The reason for setting "effectively" infinite precision
+is illustrated for example by Saturated-multiply, where if the internal precision was insufficient it would not be possible to correctly determine the maximum clip range had been exceeded.
+
+Thus it will turn out that under some conditions the combination of the
 extension of the source registers followed by truncation of the result
 gets rid of bits that didn't matter, and the operation might as well have
 taken place at the narrower width and could save resources that way.
@@ -560,8 +591,8 @@ be first read at their overridden bitwidth and *then* sign-extended:
        src2 = get_polymorphed_reg(RB, srcwid, i)
        opwidth = max(srcwid, destwid)
        # srces known to be less than result width
-       src1 = sign_extend(src1, srcwid, destwid)
-       src2 = sign_extend(src2, srcwid, destwid)
+       src1 = sign_extend(src1, srcwid, opwidth)
+       src2 = sign_extend(src2, srcwid, opwidth)
        result = op_signed(src1, src2, opwidth) # at max width
        set_polymorphed_reg(rd, destwid, i, result)
 
@@ -583,19 +614,19 @@ truncated.  Only then can the arithmetic saturation condition be detected:
        # unsigned add
        result = op_add(src1, src2, opwidth) # at max width
        # now saturate (unsigned)
-       sat = max(result, (1<<destwid)-1)
+       sat = min(result, (1<<destwid)-1)
        set_polymorphed_reg(rd, destwid, i, sat)
        # set sat overflow
        if Rc=1:
-          CR.ov = (sat != result)
+          CR[i].ov = (sat != result)
 
 So the actual computation took place at the larger width, but was
 post-analysed as an unsigned operation.  If however "signed" saturation
 is requested then the actual arithmetic operation has to be carefully
 analysed to see what that actually means.
 
-In terms of FP arithmetic, which by definition always has a sign bit do
-always takes place as a signed operation anyway, the request to saturate
+In terms of FP arithmetic, which by definition has a sign bit (so
+always takes place as a signed operation anyway), the request to saturate
 to signed min/max is pretty clear.  However for integer arithmetic such
 as shift (plain shift, not arithmetic shift), or logical operations
 such as XOR, which were never designed to have the assumption that its
@@ -615,9 +646,9 @@ truncating down to 8 bit for example.
        opwidth = max(srcwid, destwid)
        # logical op, signed has no meaning
        result = op_xor(src1, src2, opwidth)
-       # now saturate (unsigned)
-       sat = max(result, (1<<destwid-1)-1)
-       sat = min(result, -(1<<destwid-1))
+       # now saturate (signed)
+       sat = min(result, (1<<destwid-1)-1)
+       sat = max(result, -(1<<destwid-1))
        set_polymorphed_reg(rd, destwid, i, sat)
 
 Overall here the rule is: apply common sense then document the behaviour
@@ -673,7 +704,7 @@ reordering of XYZW, ARGB etc. and access of sub-portions of the same in
 arbitrary order *without* requiring timeconsuming scalar mv instructions
 (scalar due to the convoluted offsets).
 
-Swizzling does not just do permutations: it allows multiple copying of
+Swizzling does not just do permutations: it allows arbitrary selection and multiple copying of
 vec2/3/4 elements, such as XXXZ as the source operand, which will take
 3 copies of the vec4 first element (vec4[0]), placing them at positions vec4[0],
 vec4[1] and vec4[2], whilst the "Z" element (vec4[2]) was copied into vec4[3].
@@ -707,7 +738,8 @@ Compute workloads it is unusual: it is only because SV is targetted at
 3D and Video that it is being considered.
 
 Some 3D GPU ISAs also allow for two-operand subvector swizzles.  These are
-sufficiently unusual, and the immediate opcode space required so large,
+sufficiently unusual, and the immediate opcode space required so large
+(12 bits per vec4 source),
 that the tradeoff balance was decided in SV to only add mv.swizzle.
 
 # Twin Predication
@@ -739,7 +771,7 @@ are termed single-source, single-destination.
 LDST Address-generation, or AGEN, is a special case of single source,
 because elwidth overriding does not make sense to apply to the computation
 of the 64 bit address itself, but it *does* make sense to apply elwidth
-overrides to the data being accessed *at* that address.
+overrides to the data being accessed *at* that memory address.
 
 It also turns out that by using a single bit set in the source or
 destination, *all* the sequential ordered standard patterns of Vector
@@ -877,6 +909,42 @@ implementations may cause pipeline stalls.  This was one of the reasons
 why CR-based pred-result analysis was added, because that at least is
 entirely paralleliseable.
 
+# Vertical-First Mode
+
+This is a relatively new addition to SVP64 under development as of
+July 2021.  Where Horizontal-First is the standard Cray-style for-loop,
+Vertical-First typically executes just the **one** scalar element
+in each Vectorised operation. That element is selected by srcstep
+and dststep *neither of which are changed as a side-effect of execution*.
+Illustrating this in pseodocode, with a branch/loop.
+To create loops, a new instruction `svstep` must be called,
+explicitly, with Rc=1:
+
+```
+loop:
+  sv.addi r0.v, r8.v, 5 # GPR(0+dststep) = GPR(8+srcstep) + 5
+  sv.addi r0.v, r8, 5   # GPR(0+dststep) = GPR(8        ) + 5
+  sv.addi r0, r8.v, 5   # GPR(0        ) = GPR(8+srcstep) + 5
+  svstep.               # srcstep++, dststep++, CR0.eq = srcstep==VL
+  beq loop
+```
+
+Three examples are illustrated of different types of Scalar-Vector
+operations. Note that in its simplest form  **only one** element is
+executed per instruction **not** multiple elements per instruction.
+(The more advanced version of Vertical-First mode may execute multiple
+elements per instruction, however the number executed **must** remain
+a fixed quantity.)
+
+Now that such explicit loops can increment inexorably towards VL,
+of course we now need a way to test if srcstep or dststep have reached
+VL. This is achieved in one of two ways: [[sv/svstep]] has an Rc=1 mode
+where CR0 will be updated if VL is reached. A standard v3.0B Branch
+Conditional may rely on that.  Alternatively, the number of elements
+may be transferred into CTR, as is standard practice in Power ISA.
+Here, SVP64 [[sv/branches]] have a mode which allows CTR to be decremented
+by the number of vertical elements executed.
+
 # Instruction format
 
 Whilst this overview shows the internals, it does not go into detail