RISC-V, Part 2: Design

In my last blog post, we introduced the RISC-V processor architecture and talked about its history and development. In this post, we will take a deeper look into the design of the RISC-V architecture itself. However, before we do so it must be pointed out that my experience in processor design itself is limited, so most of this information is just a synopsis of the information from Waterman and the other designers of RISC-V[1]. As before, references will be provided at the bottom of the article for more information.

RISC-V Design

When Waterman and his associates at U.C., Berkeley decided to create their own processor instruction set architecture, they chose to build on the legacy of past designs. Four other RISC (Reduced Instruction Set Architecture) processors originated from Berkeley as well, so the fifth one was named RISC-V in kind. In an early research proposal, the lead developers pointed out that most ISAs fail not because of poor technical features, but because of a lack of funding[2]. Due to early support from DARPA and donations from companies like Google, Samsung, and Nvidia, research was able to rely on well-grounded techniques without concern for the future of development.

One of RISC-V's key design features is that it consists of a core ISA and several extensions. This helps keep implementation footprints small, as parts that are unneeded can be left out. The core of the architecture is broken into 4 parts: 32-bit instruction set, 32-bit embedded instruction set (with 16 registers), 64-bit instruction set, and 128-bit instruction set. These will be denoted as RV32I, RV32E, RV64I, and RV128I respectively when referenced individually. Of the 4, RV128I won't be talked about much because is currently only a future-proofing step, as 128-bit processors aren't in production, let alone commonplace, but the developers state that they expect the 64-bit address space to run out on supercomputers within the next 3 decades. Before we look at the extensions, we should take a glance at the design of the base instruction sets first. Much of this information will be summarized directly from the RISC-V Instruction Set Manual[3].

RV32I is currently the most common ISA to implement, as hardware capable of emulating 32-bit processors is easier to find and use than that capable of emulating 64-bit processors. Additionally, the RV32E (embedded) instruction set includes the same instructions as RV32I, but the number of integer registers has been cut in half to 16. Other than that, instructions for RV32I will also work on RV32E. In both cases, instructions match one of four formats, labelled R, I, S, and U. These formats do share several common traits - they must be aligned on a 4-byte boundary (which makes sense, 4 bytes = 32 bits) and they keep their source and destination registers at the same position so decoding instructions is easier to accomplish. There are also variants to the S and U formats, labelled B and J respectively. These categories are used to split instruction types: integer computations are R-type or I-type depending on parameters, jumps use the J-type format, conditional branches use the B-type format, memory loads use I-type, and memory stores use S-type. These categorizations aren't explained in the manual, but it's easy to guess that it helps ease design development.

The 64-bit instruction set, RS64I, is very similar to its 32-bit counterparts. The main difference is the wider integer registers and address space, extending maximum values for both from around 4.2 billion to over 18 quintillion. Also, instructions were added so the architecture can handle 32-bit values more accurately and efficiently without risking buffer overflows on 32-bit applications. RV128I acts in a similar manner by extending registers to 128 bits and adding new instructions to handle 64-bit integers. A new load instruction is also included to help work with 128-bit values in memory, although the original LOAD opcode is still included as well.

Extensions

Of the 14 different extensions to the base ISAs, only 6 are currently in a "frozen" state where they have been ratified. The other 8 are still open for modifications, and over half are still in an alpha state. This section will take a quick look at 4 of the 6 frozen extensions: Integer Multiplication and Division (M), Atomic Instructions (A), Single-Precision Floating-Point (F), and Double-Precision Floating-Point (D). Those 4 and the base are combined into the shorthand G, so RV64IMAFD is the same as RV64G. Any other extensions added to that are listed after the G, such as RV64GQ (Quad-Precision Floating-Point).

Given that computers were originally designed for mathematical operations, the ability to perform multiplication and division is fairly critical. The M extension covers those operations for both signed and unsigned integers. This extension includes 3 main functions and their variants: MUL to multiply two integers, DIV to divide two integers, and REM to divide two integers and return their remainder. DIV and REM both have U and W variants for unsigned and 64-bit integers respectively, as well as a UW variant to combine the two. MUL has those variants plus an H variant for the upper portion of the result rather than the lower portion, plus an SU variant for multiplying signed and unsigned integers.

The Atomic Instructions extension is a little less self-explanatory than the previous one. Atomic instructions are those that must complete all together in order to prevent unpredictable behavior. An example of this is reading an object while another process wants to write to it - the first process should finish reading first, otherwise the results could be altered. The A-extension for RISC-V adds methods for determining the order of instructions as well as determining if an instruction can "break" the atomic nature of another. For example, if two processes need to read the same object, they should be allowed to do so because no modifications will take place.

The last two "core" extensions are for single-precision and double-precision floating-point numbers. Both of these are required for non-integers, which is helpful when interacting with the real world. Single-precision floats allow for a greater range of numbers to be represented with the same word size, but they sacrifice precision to do so. Double-precision floating-point numbers (often referred to as doubles) are more precise but are slower to work with as a result. The F-extension adds 32 new registers for floating-point numbers as well as the instructions needed for basic arithmetic and number conversion. The D-extension follows suit by widening the floating-point registers to 64 bits each, thus allowing them to hold 32-bit or 64-bit values. It also adds instructions that mirror their single-precision counterparts.

Wrapping Up

Although the information in this post isn't the most technical, it is hopefully a good entry point into both RISC-V instructions and processor design in general. For more detailed and accurate information, it is recommended that readers look into both the RISC-V design and manual listed in the references. Of course, now that you know about how the design is put together, you can move on to the next step: contributing.

References

[1] Waterman, Andrew Shell. “Design of the RISC-V Instruction Set Architecture.” UC Berkeley, 2016. https://escholarship.org/uc/item/7zj0b3m7#main.
[2] Asanović, Krste, and David A. Patterson. “Instruction Sets Should Be Free: The Case For RISC-V | EECS at UC Berkeley.” Accessed November 15, 2019. https://www2.eecs.berkeley.edu/Pubs/TechRpts/2014/EECS-2014-146.html.
[3] Waterman, Andrew, and Krste Asanović, eds. “The RISC-V Instruction Set Manual.” RISC-V Foundation Volume I: Unprivileged ISA. Accessed November 15, 2019. https://riscv.org/specifications/.

blogroll

social