# OPF ISA WG External RFC ls005 v1: XLEN * RFC Author: Luke Kenneth Casson Leighton. * RFC Contributors/Ideas: Jacob Lifshay, Toshaan Bharvani * Funded by NLnet under the NGI Zero Entrust EU Horizon Europe Grant 101069594 **URLs**: * * * * **Severity**: Major **Status**: New **Date**: 22 Dec 2022 v2 TODO **Target** v3.2B **Books and Section affected**: ``` Everything (in a consistent, regular and systematic fashion) ``` **Summary** ``` Exactly as is already done in RISC-V, convert the entire use of 64-bit hard-coding to "XLEN". Exactly as is in RISC-V, options then include PowerISA-32, PowerISA-64 and PowerISA-128. Unlike in RISC-V, the concept of PowerISA-16 and PowerISA-8 is also floated, for Embedded, AI, Edge, Processing-in-Memory, Distributed Computing and other purposes. ``` **Submitter**: Luke Leighton (Libre-SOC) **Requester**: Libre-SOC **Impact on processor**: ``` Entirely new processors, entirely new markets. ``` **Impact on software**: ``` Massive but regular, consistent, and systematic. ``` **Keywords**: ``` XLEN ``` **Motivation** The Power ISA is far too massive, making it wholly unsuited for Embedded markets and adversely impacting its reach and potential. The RISC paradigm it is based on has gone too far into PackedSIMD (128-bit). Fixing this is relatively and conceptually straightforward: allow 32-bit and even 16-bit and 8-bit implementations, and use the opportunity to allow future Scalar 128-bit implementations in the exact same strategic way that RISC-V has RV128. Register files are redefined to XLEN width but are permitted to "group" registers together to create 16-bit, 32-bit and 64-bit addresses. In this way, the limitations of what would otherwise restrict the usefulness of a severely-targetted application-specific processor may be overcome in order to make it still possible to (at reduced performance) still run general-purpose applications. AI application-specific processing or other Processing-In-Memory or other specialist design therefore may for example focus a balance of raw computing power heavily onto 8-bit or 16-bit computation, but still gain the benefit of the Power ISA and everything it brings. Contrast this with the more "normal" approach of creating heavily-focussed specialist "AI" Engines incapable of Turing-completeness and the benefits are clear. Note 1: SVP64 **requires** this change as a 100% critical dependency. SIMD back-end ALUs process Vectors of "Elements" at 8, 16 and 32-bit (and 64-bit), read from, processed, and returned to, the standard **Scalar** Register Files, with byte-level write-enable lines. The proposal is therefore made as an opportunity for others interested in Scalar ISA 8/16/32-bit (and future 128-bit variants of Scalar Power ISA) to take **and complete** that work in an incremental fashion, without having to be faced with a massive bulk and body of work as a prerequisite. Examples include that whilst an SVP64 Prefixed '''lbz''' instruction ('''sv.lbz''') is well-defined and has strict well-defined behaviour, a pure **Scalar-only** (non-SVP64) over-ridden '''lbz''' instruction has not been so well-defined, and would require a Stakeholder interested in 8/16/32-bit (and future 128-bit) to think through the implications and incrementally submit further OPF ISA RFCs. With RISC-V **already having done this type of work** it is not technically difficult: it just requires another Stakeholder to do it. Note 2: one alternative to this proposal, as far as SVP64 is concerned, is to literally duplicate the entirety of Chapters 3 and 4 Book III, and to create - and then maintain - multiple identical copies of the instructions including identical copies of the pseudocode except for substitution of occurrences of "64" with a "32" variant, "16" variant, "8" variant (and future "128" variant), and so on. This would add over 700 additional pages to the Power ISA Specification and it should be clear that it would become a maintenance nightmare. Another alternative is to poison and irredemably damage the Power ISA (as a powerful and lean RISC ISA) by adding several hundred (close to 1,000) additional specific 8-bit, 16-bit and 32-bit (and in future 128-bit) Scalar instructions. Given that the 32-bit Opcode Allocation Space is already under pressure such a move would be extremely unwise for that reason alone. **Changes** For all pseudocode right across the board in all Scalar operations, replace hard-coded "64" with "XLEN". **This work is already underway as sponsored by NLnet in the Libre-SOC Power ISA Pseudocode**. The default is obviously recommended to be "XLEN=64" in order to create zero disruption. Definitions of the Register File(s) for GPR and FPR are then changed to be "XLEN" wide. However, for Embedded purposes (XLEN=32/16/8), an SPR controls whether (and how many) sequentially-grouped registers are taken together to create 16-bit, 32-bit and 64-bit addresses (depending on application need). GPR is obvious, FPR is quirky. SVP64 redefines FP ops (those not ending in "s") to be "full width" and all ops ending in "s" to be "half of the full width". * XLEN=64 keeps FPR "full width" exactly as presently defined, and "half width" exactly as presently defined. * XLEN=32 overrides FPR "full width" operations to full BFP32, and "half width" to be "BFP16 stored in an BFP32" * XLEN=16 redefines FPR "full width" operations to full [IEEE BFP16](https://en.wikipedia.org/wiki/Half-precision_floating-point_format) and leaves "half width" RESERVED (there is no IEEE version of [FP8](https://web.archive.org/web/20221223085833/https://wccftech.com/nvidia-intel-arm-bet-their-ai-future-on-fp8-whitepaper-for-8-bit-fp-published/)). * XLEN=8 redefines FPR "full width" operations to [bfloat16](https://en.wikipedia.org/wiki/Bfloat16_floating-point_format) and leaves "half width" RESERVED. ---------------- # Examples ## pseudocode examples demonstrating modification. before for popcntb: ``` do i = 0 to 7 n <- 0 do j = 0 to 7 if (RS)[(i*8)+j] = 1 then n <- n+1 RA[(i*8):(i*8)+7] <- n ``` after: ``` do i = 0 to ((XLEN/8)-1) n <- 0 do j = 0 to 7 if (RS)[(i*8)+j] = 1 then n <- n+1 RA[(i*8):(i*8)+7] <- n ``` Here as the instruction's intent is to count bytes, and RA contains on a per-byte basis a SIMD-style count of each byte's 1s, it becomes possible to simply count less bytes. Should it be more useful to redefine popcntb in terms of always returning eight results? For example `sv.popcntb/w=16` to return 8 2-bit counts of the number of bits in each 2-bit group in RS? ## no modification needed, but function changes For the `addi` instruction there is no apparent change: ``` RT <- (RA|0) + EXTS(SI) ``` However behind the scenes, RA is XLEN bits wide, therefore EXTS performs an increase in bitlength not to exactly 64 but to XLEN. Obviousy for XLEN=16 there is no sign-extension, and for XLEN=8 truncation of `SI` will occur. Illustrates that there are subtle quirks involved, requiring some thought. The reason for keeping as many bits of the Immediate as possible should be clear. ## Compare Ranged Byte (cmprb BF,L,RA,RB) ``` src1 <- EXTZ((RA)[XLEN-8:XLEN-1]) src21hi <- EXTZ((RB)[XLEN-32:XLEN-23]) src21lo <- EXTZ((RB)[XLEN-24:XLEN-17]) src22hi <- EXTZ((RB)[XLEN-16:XLEN-9]) src22lo <- EXTZ((RB)[XLEN-8:XLEN-1]) if L=0 then in_range <- (src22lo <= src1) & (src1 <= src22hi) else in_range <- (((src21lo <= src1) & (src1 <= src21hi)) | ((src22lo <= src1) & (src1 <= src22hi))) CR[4*BF+32] <- 0b0 CR[4*BF+33] <- in_range CR[4*BF+34] <- 0b0 CR[4*BF+35] <- 0b0 ``` Compare Ranged Byte takes either one or two ranges from RB as individual bytes, thus requiring a minimum 16-bit (32-bit when L=1) operand RB. src1 on the other hand is only 8-bit long: the first byte of RA. Therefore a little more thought is required. Should this simply be UNDEFINED behaviour when XLEN=8/16 and L=1? When XLEN=16, L=0 the instruction is still valid. Would it be costly at the Decoder? ## Trap Word Immediate Like FP Single operations there also exist operations at "half of regfile width" in the Integer realm. They are discernable with the designation "Word" in their title, such as "Trap WORD Immediate". ``` a <- EXTS((RA)[XLEN/2:XLEN-1]) if (a < EXTS(SI)) & TO[0] then TRAP if (a > EXTS(SI)) & TO[1] then TRAP if (a = EXTS(SI)) & TO[2] then TRAP if (a u EXTS(SI)) & TO[4] then TRAP ``` Here, EXTS receives **half** of the bits of its input register operand, RA. Note this is **not** "32 bit because a Word is 32-bit". The definition "Trap Word Immediate" has to be replaced with "Trap Half-register-width Immediate" but this is very clumsy. When XLEN=8 "half register width" is clearly 4 bit, thus the LSB nibble is tested, but still sign-extended for comparison against the 16-bit signed immediate. ## Extend Sign byte/half/word This instruction can be redefined again in terms of: * "Word" meaning "Half of register width" * "Half-word" meaning "Quarter of register width" * "Byte" meaning "One-eighth of register width" And a table results as follows: ``` XLEN=8: extsb: 1-bit -> 8-bit sign extension extsh: 2-bit -> 8-bit sign extension extsw: 4-bit -> 8-bit sign extension XLEN=16: extsb: 2-bit -> 16-bit sign extension extsh: 4-bit -> 16-bit sign extension extsw: 8-bit -> 16-bit sign extension XLEN=32: extsb: 4-bit -> 32-bit sign extension extsh: 8-bit -> 32-bit sign extension extsw: 16-bit -> 32-bit sign extension XLEN=64: extsb: 8-bit -> 64-bit sign extension extsh: 16-bit -> 64-bit sign extension extsw: 32-bit -> 64-bit sign extension ``` If the instructions were kept as presently defined then there is a loss of functionality and opportunity: ``` XLEN=8: # completely wasted opportunity extsb: 8-bit -> 8-bit does nothing extsh: 16-bit -> 8-bit truncates extsw: 32-bit -> 8-bit truncates XLEN=16: # wasted 2/3 of encoding extsb: 8-bit -> 16-bit sign extension extsh: 16-bit -> 16-bit does nothing extsw: 32-bit -> 16-bit truncates XLEN=32: # wasted 1/3 of encoding extsb: 8-bit -> 32-bit sign extension extsh: 16-bit -> 32-bit sign extension extsw: 32-bit -> 32-bit does nothing XLEN=64: # unchanged (default) behaviour extsb: 8-bit -> 64-bit sign extension extsh: 16-bit -> 64-bit sign extension extsw: 32-bit -> 64-bit sign extension ``` The RTL for `extsb` becomes: ``` in <- (RA)[XLEN-8:XLEN-1] # extract first byte if XLEN = 8 then RT <- in[7] * 8 # 1->8 if XLEN = 16 then RT <- in[6] * 15 || in[7] # 2->16 if XLEN = 32 then RT <- in[4] * 29 || in[5:7] # 4->32 if XLEN = 64 then RT <- in[0] * 57 || in[1:7] # 8->64 ``` And `extsh` and `extsw` follow similar logic. Interestingly there is no loss of functionality compared to keeping `extsb` always as "byte sign-extending" and ironically the loss of opportunity *is* to keep `extsb` the same (extend *byte* regardless of XLEN). [[!tag opf_rfc]] \newpage{}