A vast number of today’s electronics projects use a microprocessor chip because of the flexibility that software control affords. But which microprocessor do you choose? Which software programming languages should be utilized? These are important questions for every project designer. RISC-V microprocessors provide extra hardware flexibility. Manufacturer costs are reduced since they don’t have IP licenses and restrictions to comply with. Without these restrictions, vendors can add custom features to their CPU, offering specialized project advantages. The well-designed RISC-V specification is organized into feature categories (referred to as extensions). This permits the vendor to choose to implement only those features that the application requires. Reducing the number of transistors on a chip results in reduced power requirements and cost.

Hobbyists and vendors alike benefit from the availability of the free Gnu C/C++ compiler toolchain. What is sometimes overlooked is that the assembler is included in the toolchain. For the most part, C/C++ will continue to be the most productive programming language. However, there sometimes remains a need for highly optimized functions attainable only with assembly language programming. One prominent area for this is the development of short interrupt service routines.
 
RISC-V in action
RISC-V module on desk.

Why Choose RISC-V for Your Project?

Many existing instruction architectures have decades of being extended so that existing software remains compatible. This pushes the software developer into a steep learning curve to master the architecture. This is tedious and can lead to longer debug times and slipped deadlines. The RISC-V specification wiped the slate clean and designed a fresh instruction set.

The enthusiasm for RISC-V by vendors, including Intel, suggests that the architecture is a force to be reckoned with. This makes the architecture a good investment. As you take up RISC-V assembler language, you can develop optimized functions directly. Or you can check C/C++ builds at the instruction level to see what the optimizing compiler has produced for you. Sometimes a compiler’s high-level optimization results in erroneous code. Therefore, identifying problems like this saves time.

What Is This Book About?

The book, RISC-V Assembly Language Programming using ESP32-C3 and QEMU, is a tutorial to get you going. It is designed to be a gentle tutorial. Each chapter introduces a few minimal concepts so that you don’t need to learn too much at once. Later chapters then build on what you’ve already learned. Each project starts with a C language main program calling into one or more assembly language subprograms. This demonstrates the utility of using the C/C++ language where it makes sense to and exploiting assembly language at the same time.
RISC-V Book

The ESP32-C3 was chosen because of its low cost for the hobbyist and student alike. Yet, the unit is a highly functional, including USB, Wi-Fi, SPI, I2C and more. The ESP32-C3 is an excellent platform for learning about the RISC-V RV32 ISA (32-bit instruction set architecture). Espressif’s own ESP-IDF development system is used for the ESP32-C3. There are no Arduino software dependencies required.

The QEMU emulator allows the student to use their desktop computer to run Fedora Linux in the RISC-V RV64 ISA (64-bit architecture). Additionally, this environment offers the student practice at using the RISC-V floating-point extensions.
 
RISC-V
 
RISC-V
 

So, What Do You Get with a RISC-V CPU?

Table 1 outlines the 32 basic integer registers that the CPU offers the programmer. These can be referred to as x0 to x31 or by the friendlier ABI (application binary interface) names, such as a0 for argument zero. For RV32 platforms like the ESP32-C3, these are 32-bit registers. For RV64 platforms, the registers are 64 bits in width instead.

Table 1: RISC-V Basic Registers.
Register ABI Name Description
x0 zero Hardwired to return zero
x1 ra Return Address
x2 sp Stack pointer
x3 gp Global pointer
x4 tp Thread pointer
x5-x7 t0-t2 Temporary registers
x8 s0/fp Saved register / frame pointer
x9 s1 Saved register
x10-x11 a0-a1 Function arguments / return value
x12-x17 a2-a7 Function arguments (continued)
x18-x27 s2-s11 Saved registers
x28-x31 t3-t6 Temporary registers
 
With a large number of registers like this, a well-constructed function can avoid reaching out to memory entirely, resulting in faster execution. The registers offer immediate access to data, whereas memory access requires additional clock cycles. All RISC-V CPUs provide these 32 registers, with one exception. Extension E for RISC-V was developed to allow manufacturers to create a CPU with only 16 registers. These are targeted to very low-cost microcontroller roles.

All registers are equally capable, with the exception of x0, which has a special talent. When x0 is specified as a source register, it supplies the integer value zero. When specified as the destination for the result, that result is discarded. Even though the remaining registers are equal in function, the Gnu calling convention reserves groups of registers for specific purposes. Examples are a0 to a7, which are used to carry arguments, t0 to t6 to hold temporary values and register ra (x1) to receive a return address.

In addition to these registers, RISC-V extensions F, D or Q add the following floating-point registers as shown in Table 2.

Table 2: Floating-point registers and their ABI Names.
Register ABI Name Description
f0-f7 ft0-ft7 Floating-point temporaries
f8-f9 fs0-fs1 Floating-point saved registers
f10-f11 fa0-fa1 Floating-point arguments/return values
f12-f17 fa2-fa7 Floating-point arguments
f18-f27 fs2-fs11 Floating-point saved registers
f28-f31 ft8-ft11 Floating-point temporaries
 
The width of these registers depends upon the extensions supported. Extension F holds the C language float type, while extension D holds the double type. Finally, extension Q supports the quad floating-point format.

The observant reader may have noticed something missing. There are no status flag bits, such as Carry, Overflow, Negative, etc., and thus no combined status register. The designers of the RISC-V architecture decided that it was best to omit these. This reduces the interrupt service routine overhead because the status flags no longer require saving and restoring. RISC-V addresses that need in other ways, which is described in the book.

Final Note

The RISC-V CPU is like a well-oiled state machine. An easily understood instruction set permits you as the programmer to direct every step of the way.  There is great satisfaction in perfecting this level of control. Why not pursue some fun by following the gentle tutorial?