X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=simple_v_extension.mdwn;h=4cf0937edde233618bbd012a4a8ad571c9acd473;hb=989d018425f243ab9d3959108356eba243ad57ae;hp=a5fa3d208e8fef4bd3ed71f79cae6391adce1910;hpb=71b8774b2be94aea9a70ef57306506521b75a13a;p=libreriscv.git diff --git a/simple_v_extension.mdwn b/simple_v_extension.mdwn index a5fa3d208..4cf0937ed 100644 --- a/simple_v_extension.mdwn +++ b/simple_v_extension.mdwn @@ -1,9 +1,5 @@ # Variable-width Variable-packed SIMD / Simple-V / Parallelism Extension Proposal -* TODO 23may2018: CSR-CAM-ify regfile tables -* TODO 23may2018: zero-mark predication CSR -* TODO 28may2018: sort out VSETVL: CSR length to be removed? - Key insight: Simple-V is intended as an abstraction layer to provide a consistent "API" to parallelisation of existing *and future* operations. *Actual* internal hardware-level parallelism is *not* required, such @@ -13,8 +9,8 @@ instruction queue (FIFO), pending execution. *Actual* parallelism, if added independently of Simple-V in the form of Out-of-order restructuring (including parallel ALU lanes) or VLIW -implementations, or SIMD, or anything else, would then benefit *if* -Simple-V was added on top. +implementations, or SIMD, or anything else, would then benefit from +the uniformity of a consistent API. [[!toc ]] @@ -130,7 +126,8 @@ reducing power consumption for the same. SIMD again has a severe disadvantage here, over Vector: huge proliferation of specialist instructions that target 8-bit, 16-bit, 32-bit, 64-bit, and have to then have operations *for each and between each*. It gets very -messy, very quickly. +messy, very quickly: *six* separate dimensions giving an O(N^6) instruction +proliferation profile. The V-Extension on the other hand proposes to set the bit-width of future instructions on a per-register basis, such that subsequent instructions @@ -360,16 +357,14 @@ level all-hardware parallelism. Options are covered in the Appendix. # CSRs -There are a number of CSRs needed, which are used at the instruction -decode phase to re-interpret RV opcodes (a practice that has -precedent in the setting of MISA to enable / disable extensions). +There are two CSR tables needed to create lookup tables which are used at +the register decode phase. -* Integer Register N is Vector of length M: r(N) -> r(N..N+M-1) +* Integer Register N is Vector * Integer Register N is of implicit bitwidth M (M=default,8,16,32,64) * Floating-point Register N is Vector of length M: r(N) -> r(N..N+M-1) * Floating-point Register N is of implicit bitwidth M (M=default,8,16,32,64) * Integer Register N is a Predication Register (note: a key-value store) -* Vector Length CSR (VSETVL, VGETVL) Also (see Appendix, "Context Switch Example") it may turn out to be important to have a separate (smaller) set of CSRs for M-Mode (and S-Mode) so that @@ -397,19 +392,40 @@ Notes: 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. The first entry is whether predication -is enabled. The second entry is whether the register index refers to a -floating-point or an integer register. The third entry is the index -of that register which is to be predicated (if referred to). The fourth entry -is the integer register that is treated as a bitfield, indexable by the -vector element index. - -| PrCSR | 7 | 6 | 5 | (4..0) | (4..0) | -| ----- | - | - | - | ------- | ------- | -| 0 | zero0 | inv0 | i/f | regidx | predidx | -| 1 | zero1 | inv1 | i/f | regidx | predidx | -| .. | zero.. | inv.. | i/f | regidx | predidx | -| 15 | zero15 | inv15 | i/f | regidx | predidx | +instruction, it is to be predicated. However it is important to note +that the *actual* register is *different* from the one that ends up +being used, due to the level of indirection through the lookup table. +This includes (in the future) redirecting to a *second* bank of +integer registers (as a future option) + +* 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). +* inv indicates that the predication mask bits are to be inverted + prior to use *without* actually modifying the contents of the + register itself. +* 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 1, unpredicated elements *must* + be left alone. Some microarchitectures may choose to interpret + this as skipping the operation entirely. Others which wish to + stick more closely to a SIMD architecture may choose instead to + interpret unpredicated elements as an internal "copy element" + operation (which would be necessary in SIMD microarchitectures + that perform register-renaming) + +| PrCSR | 13 | 12 | 11 | 10 | (9..5) | (4..0) | +| ----- | - | - | - | - | ------- | ------- | +| 0 | bank0 | zero0 | inv0 | i/f | regidx | predidx | +| 1 | bank1 | zero1 | inv1 | i/f | regidx | predidx | +| .. | bank.. | zero.. | inv.. | i/f | regidx | predidx | +| 15 | bank15 | zero15 | inv15 | i/f | regidx | predidx | The Predication CSR Table is a key-value store, so implementation-wise it will be faster to turn the table around (maintain topologically @@ -418,18 +434,20 @@ equivalent state): struct pred { bool zero; bool inv; + bool bank; // 0 for now, 1=rsvd bool enabled; - int predidx; // redirection: actual int register to use + int predidx; // redirection: actual int register to use } - struct pred fp_pred_reg[32]; - struct pred int_pred_reg[32]; + 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].bank = CSRpred[i].bank tb[idx].predidx = CSRpred[i].predidx tb[idx].enabled = true @@ -477,9 +495,22 @@ Note: register-level redirection (from the Register CSR table) if they are vectors. -## MAXVECTORDEPTH +If written as a function, obtaining the predication mask (but not whether +zeroing takes place) may be done as follows: + + def get_pred_val(bool is_fp_op, int reg): + tb = int_pred if is_fp_op else fp_pred + if (!tb[reg].enabled): + return ~0x0 // all ops enabled + predidx = tb[reg].predidx // redirection occurs HERE + predicate = intreg[predidx] // actual predicate HERE + if (tb[reg].inv): + predicate = ~predicate // invert ALL bits + return predicate + +## MAXVECTORLENGTH -MAXVECTORDEPTH is the same concept as MVL in RVV. However in Simple-V, +MAXVECTORLENGTH is the same concept as MVL in RVV. However in Simple-V, given that its primary (base, unextended) purpose is for 3D, Video and other purposes (not requiring supercomputing capability), it makes sense to limit MAXVECTORDEPTH to the regfile bitwidth (32 for RV32, 64 for RV64 @@ -494,87 +525,64 @@ and 31 for RV32 or RV64). Note that RVV on top of Simple-V may choose to over-ride this decision. -## Vector-length CSRs - -Vector lengths are interpreted as meaning "any instruction referring to -r(N) generates implicit identical instructions referring to registers -r(N+M-1) where M is the Vector Length". Vector Lengths may be set to -use up to 16 registers in the register file. - -One separate CSR table is needed for each of the integer and floating-point -register files: - -| RegNo | (3..0) | -| ----- | ------ | -| r0 | vlen0 | -| r1 | vlen1 | -| .. | vlen.. | -| r31 | vlen31 | - -An array of 32 4-bit CSRs is needed (4 bits per register) to indicate -whether a register was, if referred to in any standard instructions, -implicitly to be treated as a vector. - -Note: - -* A vector length of 1 indicates that it is to be treated as a scalar. - Bitwidths (on the same register) are interpreted and meaningful. -* A vector length of 0 indicates that the parallelism is to be switched - off for this register (treated as a scalar). When length is 0, - the bitwidth CSR for the register is *ignored*. +## Register CSR key-value (CAM) table -Internally, implementations may choose to use the non-zero vector length -to set a bit-field per register, to be used in the instruction decode phase. -In this way any standard (current or future) operation involving -register operands may detect if the operation is to be vector-vector, -vector-scalar or scalar-scalar (standard) simply through a single -bit test. +The purpose of the Register CSR table is four-fold: -Note that when using the "vsetl rs1, rs2" instruction (caveat: when the -bitwidth is specifically not set) it becomes: +* 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-6-bit lookup table + (where the 6th bit - bank - is always set to 0 for now). +* 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. +* To indicate if the register is to be interpreted as "packed" (SIMD) + i.e. containing multiple contiguous elements of size equal to "bitwidth". - CSRvlength = MIN(MIN(CSRvectorlen[rs1], MAXVECTORDEPTH), rs2) - -This is in contrast to RVV: - - CSRvlength = MIN(MIN(rs1, MAXVECTORDEPTH), rs2) - -## Element (SIMD) bitwidth CSRs - -Element bitwidths may be specified with a per-register CSR, and indicate -how a register (integer or floating-point) is to be subdivided. - -| RegNo | (2..0) | -| ----- | ------ | -| r0 | vew0 | -| r1 | vew1 | -| .. | vew.. | -| r31 | vew31 | +| RgCSR | 15 | 14 | 13 | (12..11) | 10 | (9..5) | (4..0) | +| ----- | - | - | - | - | - | ------- | ------- | +| 0 | simd0 | bank0 | isvec0 | vew0 | i/f | regidx | predidx | +| 1 | simd1 | bank1 | isvec1 | vew1 | i/f | regidx | predidx | +| .. | simd.. | bank.. | isvec.. | vew.. | i/f | regidx | predidx | +| 15 | simd15 | bank15 | isvec15 | vew15 | i/f | regidx | predidx | vew may be one of the following (giving a table "bytestable", used below): -| vew | bitwidth | -| --- | -------- | -| 000 | default | -| 001 | 8 | -| 010 | 16 | -| 011 | 32 | -| 100 | 64 | -| 101 | 128 | -| 110 | rsvd | -| 111 | rsvd | +| vew | bitwidth | +| --- | --------- | +| 00 | default | +| 01 | default/2 | +| 10 | 8 | +| 11 | 16 | Extending this table (with extra bits) is covered in the section "Implementing RVV on top of Simple-V". -Note that when using the "vsetl rs1, rs2" instruction, taking bitwidth -into account, it becomes: +As the above table is a CAM (key-value store) it may be appropriate +to expand it as follows: + struct vectorised fp_vec[32], int_vec[32]; // 64 in future + + 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 + tb[idx].bank = CSRvec[i].bank // 0 (1=rsvd) + +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] # 1 2 4 8 16 + bytesperreg = bytestable[vew] # 8 or 16 simdmult = (XLEN/8) / bytesperreg # or FLEN as appropriate vlen = CSRvectorlen[rs1] * simdmult CSRvlength = MIN(MIN(vlen, MAXVECTORDEPTH), rs2) @@ -588,12 +596,21 @@ is given in the section "Bitwidth Virtual Register Reordering". # Instructions -By being a topological remap of RVV concepts, the following RVV instructions -remain exactly the same: VMPOP, VMFIRST, VEXTRACT, VINSERT, VMERGE, VSELECT, -VSLIDE, VCLASS and VPOPC. Two instructions, VCLIP and VCLIPI, do not -have RV Standard equivalents, so are left out of Simple-V. -All other instructions from RVV are topologically re-mapped and retain -their complete functionality, intact. +Despite being a 98% complete and accurate topological remap of RVV +concepts and functionality, the only instructions needed are VSETVL +and VGETVL. *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 but VSETVL and VGETVL, +*all instructions from RVV are topologically re-mapped and retain their +complete functionality, intact*. + +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. ## Instruction Format @@ -601,9 +618,11 @@ The instruction format for Simple-V does not actually have *any* explicit compare operations, *any* arithmetic, floating point or *any* memory instructions. Instead it *overloads* pre-existing branch operations into predicated -variants, and implicitly overloads arithmetic operations and LOAD/STORE -depending on CSR configurations for vector length, bitwidth and -predication. *This includes Compressed instructions* as well as any +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. * For analysis of RVV see [[v_comparative_analysis]] which begins to @@ -634,58 +653,46 @@ the entire bank of registers using a single instruction (see Appendix, down to the fact that predication bits fit into a single register of length XLEN bits. -The second minor change is that when VSETVL is requested to be stored -into x0, it is *ignored* silently. - -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 second change is that when VSETVL is requested to be stored +into x0, it is *ignored* silently (VSETVL x0, x5, #4) -### Under review / discussion: remove CSR vector length, use VSETVL +The third change is that there is an additional immediate added to VSETVL, +to which VL is set after first going through MIN-filtering. +So When using the "vsetl rs1, rs2, #vlen" instruction, it becomes: -So the issue is as follows: - -* CSRs are used to set the "span" of a vector (how many of the standard - register file to contiguously use) -* VSETVL in RVV works as follows: it sets the vector length (copy of which - is placed in a dest register), and if the "required" length is longer - than the *available* length, the dest reg is set to the MIN of those - two. -* **HOWEVER**... in SV, *EVERY* vector register has its own separate - length and thus there is no way (at the time that VSETVL is called) to - know what to set the vector length *to*. -* At first glance it seems that it would be perfectly fine to just limit - the vector operation to the length specified in the destination - register's CSR, at the time that each instruction is issued... - except that that cannot possibly be guaranteed to match - with the value *already loaded into the target register from VSETVL*. + VL = MIN(MIN(vlen, MAXVECTORDEPTH), rs2) -Therefore a different approach is needed. +where RegfileLen <= MAXVECTORDEPTH < XLEN -Possible options include: +This has implication for the microarchitecture, as VL is required to be +set (limits from MAXVECTORDEPTH notwithstanding) to the actual value +requested in the #immediate parameter. RVV has the option to set VL +to an arbitrary value that suits the conditions and the micro-architecture: +SV does *not* permit that. -* Removing the CSR "Vector Length" and always using the value from - VSETVL. "VSETVL destreg, counterreg, #lenimmed" will set VL *and* - destreg equal to MIN(counterreg, lenimmed), with register-based - variant "VSETVL destreg, counterreg, lenreg" doing the same. -* Keeping the CSR "Vector Length" and having the lenreg version have - a "twist": "if lengreg is vectorised, read the length from the CSR" -* Other (TBD) +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). -The first option (of the ones brainstormed so far) is a lot simpler. -It does however mean that the length set in VSETVL will apply across-the-board -to all src1, src2 and dest vectorised registers until it is otherwise changed -(by another VSETVL call). This is probably desirable behaviour. +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. ## Branch Instruction: -Branch operations use standard RV opcodes that are reinterpreted to be -"predicate variants" in the instance where either of the two src registers -have their corresponding CSRvectorlen[src] entry as non-zero. When this -reinterpretation is enabled the predicate target register rs3 is to be -treated as a bitfield (up to a maximum of XLEN bits corresponding to a -maximum of XLEN elements). +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 (isvector=1). When this reinterpretation +is enabled the "immediate" field of the branch operation is taken to be a +predication target register, rs3. The predicate target register rs3 is +to be treated as a bitfield (up to a maximum of XLEN bits corresponding +to a maximum of XLEN elements). If either of src1 or src2 are scalars (CSRvectorlen[src] == 0) the comparison goes ahead as vector-scalar or scalar-vector. Implementors should note that @@ -736,16 +743,15 @@ 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 as an explicit (scalar) instruction is needed -to invert the predicate bitmask. An additional encoding funct3=011 is -therefore proposed to cater for this. +becomes more of an impact. To deal with this, SV's predication has +had "invert" added to it. [[!table data=""" 31 .. 27| 26 .. 25 |24 ... 20 | 19 15 | 14 12 | 11 .. 7 | 6 ... 0 | funct5 | fmt | rs2 | rs1 | funct3 | rd | opcode | 5 | 2 | 5 | 5 | 3 | 4 | 7 | 10100 | 00/01/11 | src2 | src1 | 010 | pred rs3 | FEQ | -10100 | 00/01/11 | src2 | src1 | **011**| pred rs3 | FNE | +10100 | 00/01/11 | src2 | src1 | **011**| pred rs3 | rsvd | 10100 | 00/01/11 | src2 | src1 | 001 | pred rs3 | FLT | 10100 | 00/01/11 | src2 | src1 | 000 | pred rs3 | FLE | """]] @@ -769,19 +775,19 @@ and temporarily ignoring bitwidth (which makes the comparisons more complex), this becomes: if I/F == INT: # integer type cmp - pred_enabled = int_pred_enabled # TODO: exception if not set! preg = int_pred_reg[rd] reg = int_regfile else: - pred_enabled = fp_pred_enabled # TODO: exception if not set! preg = fp_pred_reg[rd] reg = fp_regfile - s1 = CSRvectorlen[src1] > 1; - s2 = CSRvectorlen[src2] > 1; - for (int i=0; i + +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_vec[rd].isvector ? int_vec[rd].regidx : rd; +  rs = int_vec[rs].isvector ? int_vec[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_vec[rs].isvec) while (!(ps & 1< What does an ADD of two different-sized vectors do in simple-V? @@ -941,6 +1059,168 @@ Section "Virtual Memory Page Faults". * Throw an exception. Whether that actually results in spawning threads as part of the trap-handling remains to be seen. +# Under consideration + +From the Chennai 2018 slides the following issues were raised. +Efforts to analyse and answer these questions are below. + +* Should future extra bank be included now? +* How many Register and Predication CSRs should there be? + (and how many in RV32E) +* How many in M-Mode (for doing context-switch)? +* Should use of registers be allowed to "wrap" (x30 x31 x1 x2)? +* Can CLIP be done as a CSR (mode, like elwidth) +* SIMD saturation (etc.) also set as a mode? +* Include src1/src2 predication on Comparison Ops? + (same arrangement as C.MV, with same flexibility/power) +* 8/16-bit ops is it worthwhile adding a "start offset"? + (a bit like misaligned addressing... for registers) + or just use predication to skip start? + +## Future (extra) bank be included (made mandatory) + +The implications of expanding the *standard* register file from +32 entries per bank to 64 per bank is quite an extensive architectural +change. Also it has implications for context-switching. + +Therefore, on balance, it is not recommended and certainly should +not be made a *mandatory* requirement for the use of SV. SV's design +ethos is to be minimally-disruptive for implementors to shoe-horn +into an existing design. + +## How large should the Register and Predication CSR key-value stores be? + +This is something that definitely needs actual evaluation and for +code to be run and the results analysed. At the time of writing +(12jul2018) that is too early to tell. An approximate best-guess +however would be 16 entries. + +RV32E however is a special case, given that it is highly unlikely +(but not outside the realm of possibility) that it would be used +for performance reasons but instead for reducing instruction count. +The number of CSR entries therefore has to be considered extremely +carefully. + +## How many CSR entries in M-Mode or S-Mode (for context-switching)? + +The minimum required CSR entries would be 1 for each register-bank: +one for integer and one for floating-point. However, as shown +in the "Context Switch Example" section, for optimal efficiency +(minimal instructions in a low-latency situation) the CSRs for +the context-switch should be set up *and left alone*. + +This means that it is not really a good idea to touch the CSRs +used for context-switching in the M-Mode (or S-Mode) trap, so +if there is ever demonstrated a need for vectors then there would +need to be *at least* one more free. However just one does not make +much sense (as it one only covers scalar-vector ops) so it is more +likely that at least two extra would be needed. + +This *in addition* - in the RV32E case - if an RV32E implementation +happens also to support U/S/M modes. This would be considered quite +rare but not outside of the realm of possibility. + +Conclusion: all needs careful analysis and future work. + +## Should use of registers be allowed to "wrap" (x30 x31 x1 x2)? + +On balance it's a neat idea however it does seem to be one where the +benefits are not really clear. It would however obviate the need for +an exception to be raised if the VL runs out of registers to put +things in (gets to x31, tries a non-existent x32 and fails), however +the "fly in the ointment" is that x0 is hard-coded to "zero". The +increment therefore would need to be double-stepped to skip over x0. +Some microarchitectures could run into difficulties (SIMD-like ones +in particular) so it needs a lot more thought. + +## Can CLIP be done as a CSR (mode, like elwidth) + +RVV appears to be going this way. At the time of writing (12jun2018) +it's noted that in V2.3-Draft V0.4 RVV Chapter, RVV intends to do +clip by way of exactly this method: setting a "clip mode" in a CSR. + +No details are given however the most sensible thing to have would be +to extend the 16-bit Register CSR table to 24-bit (or 32-bit) and have +extra bits specifying the type of clipping to be carried out, on +a per-register basis. Other bits may be used for other purposes +(see SIMD saturation below) + +## SIMD saturation (etc.) also set as a mode? + +Similar to "CLIP" as an extension to the CSR key-value store, "saturate" +may also need extra details (what the saturation maximum is for example). + +## Include src1/src2 predication on Comparison Ops? + +In the C.MV (and other ops - see "C.MV Instruction"), the decision +was taken, unlike in ADD (etc.) which are 3-operand ops, to use +*both* the src *and* dest predication masks to give an extremely +powerful and flexible instruction that covers a huge number of +"traditional" vector opcodes. + +The natural question therefore to ask is: where else could this +flexibility be deployed? What about comparison operations? + +Unfortunately, C.MV is basically "regs[dest] = regs[src]" whilst +predicated comparison operations are actually a *three* operand +instruction: + + regs[pred] |= 1<< (cmp(regs[src1], regs[src2]) ? 1 : 0) + +Therefore at first glance it does not make sense to use src1 and src2 +predication masks, as it breaks the rule of 3-operand instructions +to use the *destination* predication register. + +In this case however, the destination *is* a predication register +as opposed to being a predication mask that is applied *to* the +(vectorised) operation, element-at-a-time on src1 and src2. + +Thus the question is directly inter-related to whether the modification +of the predication mask should *itself* be predicated. + +It is quite complex, in other words, and needs careful consideration. + +## 8/16-bit ops is it worthwhile adding a "start offset"? + +The idea here is to make it possible, particularly in a "Packed SIMD" +case, to be able to avoid doing unaligned Load/Store operations +by specifying that operations, instead of being carried out +element-for-element, are offset by a fixed amount *even* in 8 and 16-bit +element Packed SIMD cases. + +For example rather than take 2 32-bit registers divided into 4 8-bit +elements and have them ADDed element-for-element as follows: + + r3[0] = add r4[0], r6[0] + r3[1] = add r4[1], r6[1] + r3[2] = add r4[2], r6[2] + r3[3] = add r4[3], r6[3] + +an offset of 1 would result in four operations as follows, instead: + + r3[0] = add r4[1], r6[0] + r3[1] = add r4[2], r6[1] + r3[2] = add r4[3], r6[2] + r3[3] = add r5[0], r6[3] + +In non-packed-SIMD mode there is no benefit at all, as a vector may +be created using a different CSR that has the offset built-in. So this +leaves just the packed-SIMD case to consider. + +Two ways in which this could be implemented / emulated (without special +hardware): + +* bit-manipulation that shuffles the data along by one byte (or one word) + either prior to or as part of the operation requiring the offset. +* just use an unaligned Load/Store sequence, even if there are performance + penalties for doing so. + +The question then is whether the performance hit is worth the extra hardware +involving byte-shuffling/shifting the data by an arbitrary offset. On +balance given that there are two reasonable instruction-based options, the +hardware-offset option should be left out for the initial version of SV, +with the option to consider it in an "advanced" version of the specification. + # Impementing V on top of Simple-V With Simple-V converting the original RVV draft concept-for-concept @@ -1620,7 +1900,7 @@ To illustrate how this works, here is some example code from FreeRTOS ... STORE x30, 29 * REGBYTES(sp) STORE x31, 30 * REGBYTES(sp) - + /* Store current stackpointer in task control block (TCB) */ LOAD t0, pxCurrentTCB //pointer STORE sp, 0x0(t0) @@ -1681,11 +1961,11 @@ bank of registers is to be loaded/saved: .macroVectorSetup MVECTORCSRx1 = 31, defaultlen MVECTORCSRx4 = 28, defaultlen - + /* Save Context */ SETVL x0, x0, 31 /* x0 ignored silently */ - STORE x1, 0x0(sp) // x1 marked as 31-long vector of default bitwidth - + STORE x1, 0x0(sp) // x1 marked as 31-long vector of default bitwidth + /* Restore registers, Skip global pointer because that does not change */ LOAD x1, 0x0(sp) @@ -1891,7 +2171,7 @@ or may not require an additional pipeline stage) >> FIFO). > Those bits cannot be known until after the registers are decoded from the -> instruction and a lookup in the "vector length table" has completed. +> instruction and a lookup in the "vector length table" has completed. > Considering that one of the reasons RISC-V keeps registers in invariant > positions across all instructions is to simplify register decoding, I expect > that inserting an SRAM read would lengthen the critical path in most @@ -1917,6 +2197,62 @@ reply: > non-predicated vector-compare, followed by vector gather-scatter on the > result? +## element width conversion: restrict or remove? + +summary: don't restrict / remove. it's fine. + +> > it has virtually no cost/overhead as long as you specify +> > that inputs can only upconvert, and operations are always done at the +> > largest size, and downconversion only happens at the output. +> +> okaaay.  so that's a really good piece of implementation advice. +> algorithms do require data size conversion, so at some point you need to +> introduce the feature of upconverting and downconverting. +> +> > for int and uint, this is dead simple and fits well within the RVV pipeline +> > without any critical path, pipeline depth, or area implications. + + + +## Under review / discussion: remove CSR vector length, use VSETVL + +**DECISION: 11jun2018 - CSR vector length removed, VSETVL determines +length on all regs**. This section kept for historical reasons. + +So the issue is as follows: + +* CSRs are used to set the "span" of a vector (how many of the standard + register file to contiguously use) +* VSETVL in RVV works as follows: it sets the vector length (copy of which + is placed in a dest register), and if the "required" length is longer + than the *available* length, the dest reg is set to the MIN of those + two. +* **HOWEVER**... in SV, *EVERY* vector register has its own separate + length and thus there is no way (at the time that VSETVL is called) to + know what to set the vector length *to*. +* At first glance it seems that it would be perfectly fine to just limit + the vector operation to the length specified in the destination + register's CSR, at the time that each instruction is issued... + except that that cannot possibly be guaranteed to match + with the value *already loaded into the target register from VSETVL*. + +Therefore a different approach is needed. + +Possible options include: + +* Removing the CSR "Vector Length" and always using the value from + VSETVL. "VSETVL destreg, counterreg, #lenimmed" will set VL *and* + destreg equal to MIN(counterreg, lenimmed), with register-based + variant "VSETVL destreg, counterreg, lenreg" doing the same. +* Keeping the CSR "Vector Length" and having the lenreg version have + a "twist": "if lengreg is vectorised, read the length from the CSR" +* Other (TBD) + +The first option (of the ones brainstormed so far) is a lot simpler. +It does however mean that the length set in VSETVL will apply across-the-board +to all src1, src2 and dest vectorised registers until it is otherwise changed +(by another VSETVL call). This is probably desirable behaviour. + ## Implementation Paradigms TODO: assess various implementation paradigms. These are listed roughly @@ -2002,7 +2338,7 @@ TBD: floating-point compare and other exception handling * Dot Product Vector * RVV slides 2017 -* Wavefront skipping using BRAMS +* Wavefront skipping using BRAMS * Streaming Pipelines * Barcelona SIMD Presentation *