Monday 28 March 2022

RISC-V : Learning Assembly


Study Texts

I read a book RISC-V Assembly Language by Anthony J Dos Reis to learn RISC-V (RV).  The book assumes you don't have any prior experience with assembly language and some parts are rather slow and obvious to me.  However it pays to read through in detail as there is a lot of important information included.
One downside of the book is that the examples use an assembler "rv" written by the author, which runs on Windows.  Of necessity, time is devoted to the way it works.  As I am using a real RV computer I would prefer focussing on the real environment.  I agree that Reis's "rv" assembler makes it much easier for most people to get started, as I am rather unusual / fortunate in having a real linux RV system.

 I also used the definitive RISC-V Reader, written by the architects, David Paterson and Andrew Waterman.  This highlights the rational elegance of the language and compares it with older CISC (Intel) and RISC (ARM) architectures.  The language can be summarised in a very few pages - although this doesn't help a lot in learning it.

Machine Code

The Reis book focuses on RV32I 32-bit base instruction set.  There are cutdown versions for 16 bit as well as 64-bit and 128-bit varieties. My computer is 64-bit  but runs the 32-bit instruction set mostly.  There are a few extra instructions for 64-bit systems but they are just natural extensions.  For example the LW instruction loads a 32-bit word into a 32 or 64 bit register and LD loads a 64-bit word into a 64-bit register.  One of the strengths of RV is you should be able to program a wide variety of devices with, mostly, the same instruction set.

There are various standard fields in a machine code instruction.  Care has been taken to put the fields in the same place within all instructions wherever possible, in fact there are only 6 different formats.  This makes the hardware simpler and assists pipelining.



All instructions are 32 bits in length with the opcode in the right-hand 7 bits.  This helps quick identification of  an instructions purpose when looking at machine code.
There are 32 registers which are divided into groups for different purposes such as temporary, saved, function arguments.  Stack pointer, return address and global pointer registers have specific uses.
In total there are only about 60 instructions covering the expected operations such as load, store, branch, add, shift and logical operations.

Assembly Language

Using a small instruction set makes learning the basic operations of loading, storing, adding, shifting, looping and branching very simple.
As RV machines don't have a status word some things need to be processed somewhat differently.  The result of comparison operations is put into a register; the programmer needs to test for overflow conditions themselves.

A sample program is shown below:


The subroutine mechanism requires you to save the return address on the stack when you enter a subroutine (assuming you want to be able to call other subroutines) and in practice the stack can be used for most of the storage required in the program.
There is a convention that "callee" registers, s0-s11 need to be saved within your subroutine if you modify them.  Temporary registers and argument registers are assumed to be overwritten across a subroutine call, so if the calling program needs them it is responsible for saving them.
This is a nice mechanism to minimise unnecessary save / restore overhead for subroutines.

Pseudo Instructions

Pseudo instructions pad out the instruction set.  For example there is a BLT (branch if less than) but no  BGT (branch if greater than) instructions.  You can code BGT t0, t1, done-label and the assembler translates to BLT t1, t0, done-label.  A NOP (no operation) pseudo-instruction is translated to addi x0, x0,0 (add nothing to the read-only x0 register).

Load immediate (LI) and load address (LA) are particularly helpful allowing us to load 32 bit data and addresses.  LI basically uses two instructions: LUI (load unsigned immediate) loads the lower 12 bits and ADDI (add immediate) to add in the upper 20 bits.  There are some wrinkles which the assembler deals with for you.  Similarly LA uses LUI and AUIPC (Add Upper Immediate to PC) and sorts out the complexities for you.

Multiplication and divison

The RV32I instruction set doesn't include multiplication and division instructions but RV32M does.  So if your processor can do multiply and divide you can use the standard instructions, if not you need to implement suitable subroutines such as the shift-add algorithm provided in the study text.

Conclusion

I have just about covered the RV language.  The RISC-V Reader is even shorter than the Reis text book and provides a lot of comparisons with ARM and Intel architecture to justify RV design.  RV seems to have a bright future now that suitable hardware is being developed and sold.  Although not yet competitive with established players it seems likely that it will grow rapidly over the next few years.

No comments:

Post a Comment