100% found this document useful (1 vote)
22 views

RISC-V Assembly Language Programming Stephen Smith instant download

The document promotes the book 'RISC-V Assembly Language Programming' by Stephen Smith, which focuses on programming RISC-V CPUs using Assembly Language. It highlights the advantages of RISC-V's open-source instruction set and provides resources for learning through practical projects and tools. The book aims to equip readers with the skills to understand and program low-cost microcontrollers and single-board computers effectively.

Uploaded by

axadnane
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (1 vote)
22 views

RISC-V Assembly Language Programming Stephen Smith instant download

The document promotes the book 'RISC-V Assembly Language Programming' by Stephen Smith, which focuses on programming RISC-V CPUs using Assembly Language. It highlights the advantages of RISC-V's open-source instruction set and provides resources for learning through practical projects and tools. The book aims to equip readers with the skills to understand and program low-cost microcontrollers and single-board computers effectively.

Uploaded by

axadnane
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 78

Download the full version and explore a variety of ebooks

or textbooks at https://ebookmass.com

RISC-V Assembly Language Programming Stephen Smith

_____ Tap the link below to start your download _____

https://ebookmass.com/product/risc-v-assembly-language-
programming-stephen-smith/

Find ebooks or textbooks at ebookmass.com today!


We believe these products will be a great fit for you. Click
the link to download now, or visit ebookmass.com
to discover even more!

RISC-V Assembly Language Programming: Unlock the Power of


the RISC-V Instruction Set (Maker Innovations Series) 1st
Edition Stephen Smith
https://ebookmass.com/product/risc-v-assembly-language-programming-
unlock-the-power-of-the-risc-v-instruction-set-maker-innovations-
series-1st-edition-stephen-smith/

RISC-V Assembly Language Programming: Unlock the Power of


the RISC-V Instruction Set (Maker Innovations Series) 1st
Edition Smith
https://ebookmass.com/product/risc-v-assembly-language-programming-
unlock-the-power-of-the-risc-v-instruction-set-maker-innovations-
series-1st-edition-smith/

MIPS assembly language programming Robert Britton

https://ebookmass.com/product/mips-assembly-language-programming-
robert-britton/

The Art of Assembly Language Programming using PIC


Technology. Core Fundamentals Theresa Schousek

https://ebookmass.com/product/the-art-of-assembly-language-
programming-using-pic-technology-core-fundamentals-theresa-schousek-2/
The Art of Assembly Language Programming Using PIC®
Technology : Core Fundamentals. Theresa Schousek

https://ebookmass.com/product/the-art-of-assembly-language-
programming-using-pic-technology-core-fundamentals-theresa-schousek/

Modern X86 Assembly Language Programming: Covers X86


64-bit, AVX, AVX2, and AVX-512, 3rd Edition Daniel
Kusswurm
https://ebookmass.com/product/modern-x86-assembly-language-
programming-covers-x86-64-bit-avx-avx2-and-avx-512-3rd-edition-daniel-
kusswurm/

Digital Design and Computer Architecture: RISC-V Edition


Sarah Harris & David Harris

https://ebookmass.com/product/digital-design-and-computer-
architecture-risc-v-edition-sarah-harris-david-harris/

Programming for Absolute Beginners: Using the JavaScript


Programming Language 1st Edition Jonathan Bartlett

https://ebookmass.com/product/programming-for-absolute-beginners-
using-the-javascript-programming-language-1st-edition-jonathan-
bartlett/

The AWK Programming Language, 2nd Edition Aho

https://ebookmass.com/product/the-awk-programming-language-2nd-
edition-aho/
Maker Innovations Series

Jump start your path to discovery with the Apress Maker Innovations
series! From the basics of electricity and components through to the
most advanced options in robotics and Machine Learning, you’ll forge a
path to building ingenious hardware and controlling it with cutting-
edge software. All while gaining new skills and experience with
common toolsets you can take to new projects or even into a whole new
career.
The Apress Maker Innovations series offers projects-based learning,
while keeping theory and best processes front and center. So you get
hands-on experience while also learning the terms of the trade and how
entrepreneurs, inventors, and engineers think through creating and
executing hardware projects. You can learn to design circuits, program
AI, create IoT systems for your home or even city, and so much more!
Whether you’re a beginning hobbyist or a seasoned entrepreneur
working out of your basement or garage, you’ll scale up your skillset to
become a hardware design and engineering pro. And often using low-
cost and open-source software such as the Raspberry Pi, Arduino, PIC
microcontroller, and Robot Operating System (ROS). Programmers and
software engineers have great opportunities to learn, too, as many
projects and control environments are based in popular languages and
operating systems, such as Python and Linux.
If you want to build a robot, set up a smart home, tackle assembling
a weather-ready meteorology system, or create a brand-new circuit
using breadboards and circuit design software, this series has all that
and more! Written by creative and seasoned Makers, every book in the
series tackles both tested and leading-edge approaches and
technologies for bringing your visions and projects to life.
More information about this series at
https://link.springer.com/bookseries/17311.
Stephen Smith

RISC-V Assembly Language


Programming
Unlock the Power of the RISC-V Instruction Set
Stephen Smith
Gibsons, BC, Canada

ISSN 2948-2542 e-ISSN 2948-2550


Maker Innovations Series
ISBN 979-8-8688-0136-5 e-ISBN 979-8-8688-0137-2
https://doi.org/10.1007/979-8-8688-0137-2

© Stephen Smith 2024

This work is subject to copyright. All rights are solely and exclusively
licensed by the Publisher, whether the whole or part of the material is
concerned, specifically the rights of translation, reprinting, reuse of
illustrations, recitation, broadcasting, reproduction on microfilms or in
any other physical way, and transmission or information storage and
retrieval, electronic adaptation, computer software, or by similar or
dissimilar methodology now known or hereafter developed.

The use of general descriptive names, registered names, trademarks,


service marks, etc. in this publication does not imply, even in the
absence of a specific statement, that such names are exempt from the
relevant protective laws and regulations and therefore free for general
use.

The publisher, the authors, and the editors are safe to assume that the
advice and information in this book are believed to be true and accurate
at the date of publication. Neither the publisher nor the authors or the
editors give a warranty, expressed or implied, with respect to the
material contained herein or for any errors or omissions that may have
been made. The publisher remains neutral with regard to jurisdictional
claims in published maps and institutional affiliations.
This Apress imprint is published by the registered company APress
Media, LLC, part of Springer Nature.
The registered company address is: 1 New York Plaza, New York, NY
10004, U.S.A.
This book is dedicated to my beloved wife and editor Cathalynn Labonté-
Smith.
Introduction
The heart of every computer and smart device is a Central Processing
Unit (CPU). Historically, each CPU has been produced by a single
company which tightly controls the instruction set and internal
workings of the CPU. RISC-V (pronounced Risk Five) is a new approach
based on open-source principles. Anyone can implement a RISC-V CPU
using a standardized instruction set without requiring any special
licensing or royalty payments. As a result, RISC-V CPUs can be at a
much lower cost than proprietary CPUs and have exploded in the low-
cost microcontroller space. Now that more powerful RISC-V CPUs are
appearing, leading to Single Board Computers (SBCs) similar to the
Raspberry Pi, along with inexpensive low-end laptops and tablets, this
book presents the inner workings of typical RISC-V CPUs and details
the open-source Assembly Language instruction set they all implement.
Assembly Language is the native, lowest level way to program a
computer. Each processing chip has its own Assembly Language. This
book teaches programming RISC-V CPUs either running 32- or 64-bits.
Learning how a computer works, and mastering Assembly
Language, is an excellent way to get into the nitty-gritty details. These
low-cost microcontrollers and SBCs provide ideal platforms to learn
advanced concepts in computing.
Even though all of these devices are low-powered and compact,
they’re still sophisticated computers, many of which are capable of
running the full Linux operating system. Due to the RISC-V instruction
set standard, learning to program on one RISC-V processor is directly
applicable to all RISC-V processors.
In this book, we cover how to program RISC-V processors at the
lowest level, operating as close to the hardware as possible. Readers
will learn the following:
How to format instructions and combine them into programs, as well
as details of the operative binary data formats
How to program RISC-V instruction set extensions, such as floating-
point instructions
How to control integrated hardware devices by reading and writing
to the hardware control registers directly
How to interact with the Linux operating system and microcontroller
SDKs
The simplest way to learn these tasks is with a RISC-V SBC such as
the Starfive Visionfive 2. The book also details how to emulate the RISC-
V CPU on an Intel/AMD computer using the QEMU emulator, as well as
how to program the Espressif ESP32-C3 microcontroller. All the tools
needed to learn Assembly Language programming are open source and
readily available.
This book contains many working programs to play with, use as a
starting point, or study. The only way to learn programming is by doing
it, so do not be afraid to experiment, as it is the only way to learn.
Even if Assembly Language programming isn’t used in your day-to-
day life, knowing how the processor works at the Assembly Language
level and knowing the low-level binary data structures will make you a
better programmer in all other areas. Knowing how the processor
works will let you write more efficient C code and can even help with
Python programming.
Enjoy this introduction to Assembly Language. Learning it for one
processor family helps with learning and using any other processor
architectures encountered throughout a programmer’s career.
Any source code or other supplementary material referenced by the
author in this book is available to readers on GitHub
(https://github.com/Apress). For more detailed information, please
visit https://www.apress.com/gp/services/source-code.
Acknowledgments
No book is ever written in isolation. I want to especially thank my wife
Cathalynn Labonté-Smith for her support, encouragement, and expert
editing.
I want to thank all the good folks at Apress who made the whole
process easy and enjoyable. A special shout-out to Nirmal Selvaraj, my
production editor, who kept the whole project moving quickly and
smoothly. Thanks to Miriam Haidara, the acquisitions editor, for mobile
tech and maker programs, who got the project started. Thanks to
Stewart Watkiss, my technical reviewer, who helped make this a far
better book.
Table of Contents
Chapter 1:​Getting Started
History and Evolution of the RISC-V CPU
What You Will Learn
Ten Reasons to Learn Assembly Language Programming
Running Programs on RISC-V Systems
Coding a Simple “Hello World” Program
Hello World on the Starfive Visionfive 2
Programming Hello World in the QEMU Emulator
About Hello World on the ESP32-C3 Microcontroller
Summary
Exercises
Chapter 2:​Loading and Adding
Computers and Numbers
Negative Numbers
About Two’s Complement
RISC-V Assembly Instructions
CPU Registers
RISC-V Instruction Format
About the GCC Assembler
Adding Registers
32-bits in a 64-bit World
Moving Registers
About Pseudoinstructio​ns
About Immediate Values
Loading the Top
Shifting the Bits
Loading Larger Numbers into Registers
More Shift Instructions
About Subtraction
Summary
Exercises
Chapter 3:​Tooling Up
GNU Make
Rebuild a Project
Rule for Building .​S files
Define Variables
Build with CMake
Debugging with GDB
Preparation to Debug
Setup for Linux
Set Up gdb for the ESP32-C3
Debugging with GDB
Summary
Exercises
Chapter 4:​Controlling Program Flow
Creating Unconditional Jumps
Understanding Conditional Branches
Using Branch Pseudoinstructio​ns
Constructing Loops
Create FOR Loops
Code While Loops
Coding If/​Then/​Else
Manipulating Logical Operators
Using AND
Using XOR
Using OR
Adopting Design Patterns
Converting Integers to ASCII
Using Expressions in Immediate Constants
Storing a Register to Memory
Why Not Print in Decimal?​
Performance of Branch Instructions
Using Comparison Instructions
Summary
Exercises
Chapter 5:​Thanks for the Memories
Defining Memory Contents
Aligning Data
About Program Sections
Big vs.​Little Endian
Pros of Little Endian
About Memory Addresses
Loading a Register with an Address
PC Relative Addressing
Loading Data from Memory
Combining Loading Addresses and Memory
Storing a Register
Optimizing Through Relaxing
Converting to Uppercase
Summary
Exercises
Chapter 6:​Functions and the Stack
About Stacks
Jump and Link
Nesting Function Calls
Function Parameters and Return Values
Managing the Registers
Summary of the Function Call Algorithm
Uppercase Revisited
Stack Frames
Stack Frame Example
Macros
Include Directive
Macro Definition
Labels
Why Macros?​
Using Macros to Improve Code
Summary
Exercises
Chapter 7:​Linux Operating System Services
So Many Services
Calling Convention
Finding Linux System Call Numbers
Return Codes
Structures
About Wrappers
Converting a File to Uppercase
Building .​S Files
Opening a File
Error Checking
Looping
Summary
Exercises
Chapter 8:​Programming GPIO Pins
GPIO Overview
In Linux, Everything is a File
Flashing LEDs
Moving Closer to the Metal
Virtual Memory
In Devices, Everything is Memory
Registers in Bits
GPIO Enable Registers
GPIO Output Set Registers
More Flashing LEDs
GPIOTurnOn in Detail
Root Access
Summary
Exercises
Chapter 9:​Interacting with C and Python
Calling C Routines
Printing Debug Information
Register Masking Revisited
Calling Assembly Routines from C
Packaging the Code
Static Library
Shared Library
Embedding Assembly Language Code inside C Code
Calling Assembly from Python
Summary
Exercises
Chapter 10:​Multiply and Divide
Multiplication
Examples
Division
Division by Zero and Overflow
Example
Example:​Matrix Multiplication
Vectors and Matrices
Multiplying 3x3 Integer Matrices
Summary
Exercises
Chapter 11:​Floating-Point Operations
About Floating Point Numbers
About Normalization and NaNs
Recognizing Rounding Errors
Defining Floating Point Numbers
About Floating Point Registers
The Status and Control Register
Defining the Function Call Protocol
Loading and Saving FPU Registers
Performing Basic Arithmetic
Calculating Distance Between Points
Performing Floating-Point Conversions
Floating-Point Sign Injection
Comparing Floating-Point Numbers
Example
Summary
Exercises
Chapter 12:​Optimizing Code
Optimizing the Uppercase Routine
Simplifying the Range Comparison
Restricting the Problem Domain
Tips for Optimizing Code
Avoiding Branch Instructions
Moving Code Out of Loops
Avoiding Expensive Instructions
Use Macros
Loop Unrolling
Delay Preserving Registers in Functions
Keeping Data Small
Beware of Overheating
Summary
Exercises
Chapter 13:​Reading and Understanding Code
Browsing Linux &​GCC Code
Comparing Strings
Code Created by GCC
Reverse Engineering and Ghidra
Summary
Exercises
Chapter 14:​Hacking Code
Buffer Overrun Hack
Causes of Buffer Overrun
Stealing Credit Card Numbers
Stepping Through the Stack
Mitigating Buffer Overrun Vulnerabilities
Do Not Use strcpy
PIE Is Good
Poor Stack Canaries Are the First to Go
Preventing Code Running on the Stack
Tradeoffs of Buffer Overflow Mitigation Techniques
Summary
Exercises
Appendix A:​The RISC-V Instruction Set
Appendix B:​Binary Formats
Appendix C:​Assembler Directives
Appendix D:​ASCII Character Set
Appendix E:​Answers to Exercises
Index
About the Author
Stephen Smith
is a software architect, located in
Gibsons, BC, Canada. He’s been
developing software since high school, or
way too many years to record. He is an
expert in Artificial Intelligence and
Assembly Language programming,
earned his Advanced HAM Radio License,
and enjoys mountain biking, hiking, and
nature photography. He volunteers for
Sunshine Coast Search and Rescue. He is
the author of Raspberry Pi Assembly
Language Programming: ARM Processor
Coding, Programming with 64-Bit ARM
Assembly Language: Single Board
Computer Development for Raspberry Pi
and Mobile Devices, and RP2040 Assembly
Language Programming: ARM Cortex-M0+ on the Raspberry Pi Pico, all
published by Apress. Also, he writes his popular technology blog, at
smist08.wordpress.com.
About the Technical Reviewer
Stewart Watkiss
is a keen maker who has created
numerous physical computing projects
using a variety of computers and
microcontrollers. He is author of the
Apress titles Learn Electronics with
Raspberry Pi and Beginning Game
Programming with Pygame Zero. He
studied at the University of Hull, where
he earned a master’s degree in
electronics engineering, and more
recently at Georgia Institute of
Technology, where he earned a master’s
degree in computer science.
Stewart also volunteers as a STEM ambassador, helping teach
programming and physical computing to schoolchildren and at
Raspberry Pi events. He has created numerous resources for those
wanting to learn more about electronics and computing which are
available on his website (www.penguintutor.com).
© The Author(s), under exclusive license to APress Media, LLC, part of Springer
Nature 2024
S. Smith, RISC-V Assembly Language Programming, Maker Innovations Series
https://doi.org/10.1007/979-8-8688-0137-2_1

1. Getting Started
Stephen Smith1
(1) Gibsons, BC, Canada

Most people are familiar with Intel or AMD microprocessors lying at the
heart of their desktop, laptop, or server, and, similarly, most cell phones
and tablets use ARM microprocessors. RISC-V is the new entry built
around open-source concepts. Before getting into the details of RISC-V,
let’s first look at the history and evolution of modern microprocessors.

History and Evolution of the RISC-V CPU


At the heart of every computer, there is at least one Central Processing
Unit (CPU) that executes the programs that you run. Most modern
computers have several CPUs, the main one to run the operating system
and user programs, then several helper CPUs to offload tasks like
network communications. Solid-state drives (SSDs) and hard drives
(HDDs) each contain a CPU to read and store the data. Nearly every
household appliance from microwaves to thermostats contains a CPU
that interprets button pushes and performs the appliance’s function. A
modern automobile typically contains 1000 to 3000 CPU chips to
control everything from the infotainment system to the power steering
to the braking systems. CPUs range in price from a few cents to
thousands of dollars, and each runs its own special machine code to
execute programs.
Early CPUs, such as the IBM 360 mainframe, were large,
complicated, and comprised of thousands of discrete components. In
1971, Intel debuted the Intel 4004, a single chip 4-bit CPU that allowed
the proliferation of handheld calculators. This led to more advanced
CPUs such as the MOS Technology 6502 which had the right
combination of being both affordable yet powerful enough to create
both the personal computer and video game industries with products
like the Apple II and Nintendo Entertainment System.
IBM entered the PC market in 1981 using the more complex Intel
8088 CPU. This CPU was powerful and flexible with 16-bit addressing
and variable length machine code instructions. This started a trend of
single-chip CPUs adding complexity with the goal of becoming
mainframes on the desktop. Intel quickly introduced new chips with
more and more advanced functionalities to empower 32-bit and then
64-bit processing, vector operations, memory protection, multi-tasking,
and more. This process added to the chip's complexity along with the
underlying instruction set.
Competitors saw this and thought they could compete with Intel’s
Complex Instruction Set Computers (CISC) by introducing simplified
Reduced Instruction Set Computers (RISC). These computers wouldn’t
have the baggage of maintaining compatibility with older chips, they
would start out at 32- or 64-bits. Their instructions would be a fixed
width and there would be fewer of them to allow faster instruction
decoding. Each instruction would run in 1 clock cycle. Early RISC
processors were typically used in high-end UNIX workstations, so
consequently they never achieved the shipment volume and economies
of scale to effectively compete with Intel.
In 1982, Acorn Computers, which produced the BBC
Microcomputer, was looking to upgrade their line of computers from
using the now aging 6502. The engineers at Acorn did not want to move
to the Intel architecture and become another PC clone. Instead, they
wanted the simplicity of the 6502, but modernized. They took the
daring step of designing their own CPU, called the ARM CPU based on
the RISC philosophy.
If the ARM was only used in Acorn Computers, it probably would
have died out. However, towards the end of the 1990s, Steve Jobs
decided to use the ARM processor in the Apple iPod. The reason being
that because of its simplified instruction set, an ARM processor didn’t
use much power, and this allowed longer battery life in a portable
device.
With the success of the iPod, ARM reached a reasonable production
volume for more research and development (R&D), then with the
introduction of the iPhone, ARM usage exploded as ARM processors are
used in nearly every phone and tablet in production. Intel finally had
competition from a RISC processor.
Today, most Intel processors implement a RISC core that executes
the Intel machine code using multiple RISC instructions. ARM has
become more complex as it supports both 32- and 64-bit instructions
using different formats. A major complaint against both is that to use
compatible machine code with either ARM or Intel processors requires
expensive licensing agreements with the parent companies.
Enter RISC-V, an open standards RISC-based machine, where anyone
can produce a compatible CPU without paying anyone licensing fees.
Creating a new CPU machine language is expensive as you need high-
level language compilers and operating support. If you create a CPU
that executes RISC-V instructions, then you can use the standard RISC-V
version of Linux and the standard GNU language toolchain for
compiling high-level languages. The RISC-V project started in 2010 at
the University of California, Berkeley, and RISC-V is the fifth version of
the architecture. It supports 32-, 64-, and 128-bit processors. It is
designed to run on everything from microcontrollers costing pennies to
superscalar supercomputers costing millions. We’ve reached a point
where many RISC-V CPUs and computers are appearing. This book will
explore the architecture and describe how to program these RISC-V
CPUs at the lowest machine code level.

What You Will Learn


You will learn Assembly Language programming for RISC-V CPUs
running in 32- or 64-bit mode. Everything you will learn is directly
applicable to all RISC-V devices from the smallest microcontrollers to
the largest superscalar supercomputers. Learning Assembly Language
for one processor gives you the tools to learn it for another processor,
such as ARM processors typically used in mobile devices.
In all devices, the RISC-V processor isn’t just a CPU, it’s a system on a
chip. This means that most of the computers are all on one chip. When a
company is designing a device, they can select various modular
components to include on their chip. Typically, this contains a RISC-V
processor with multiple cores, meaning that it can process instructions
for multiple programs running at once. It likely contains several co-
processors for things like floating-point calculations, a Graphics
Processing Unit (GPU), and various communications protocols.
The RISC-V instruction set is modular. Each RISC-V CPU will
implement a set of modules to provide the functionality its users
require. First there is a base module consisting of the basic integer
instructions. There are three common base instruction sets—one for
32-bit, one for 64-bit, and a slimmed down subset of the 32-bit one for
minimal embedded processors.
The base set provides sufficient functionality to perform integer
arithmetic, logic, comparisons, and branches. Then additional
instructions are added in optional modules, for instance, integer
multiplication and division are in the “M” module. Low-cost embedded
processors will only implement a few basic modules, whereas a more
powerful CPU intended to be used in a full Linux-based computer might
implement a dozen modules. Table 1-1 contains the three main base
versions of the instruction sets that are covered in this book.

Table 1-1 Base Instruction Sets

Name Description
RV32I Base Integer Instruction Set, 32-bit
RV32E Base Integer Instruction Set (embedded), 32-bit
RV64I Base Integer Instruction Set, 64-bit

Table 1-2 contains the main instruction set extensions. For a


complete list, consult the specifications posted at riscv.org.

Table 1-2 Instruction Set Extensions

Name Description
M Standard Extension for Integer Multiplication and Division
A Standard Extension for Atomic Instructions
F Standard Extension for Single-Precision Floating-Point
Name Description
D Standard Extension for Double-Precision Floating-Point
Zicsr Control and Status Register (CSR) Instructions
Zifencei Instruction-Fetch Fence
G Shorthand for the IMAFDZicsr_Zifencei base and extensions
C Standard Extension for Compressed Instructions
Working at this low level is technical and time-consuming, so why
would a programmer want to write Assembly Language code?

Ten Reasons to Learn Assembly Language


Programming
Most programmers write in a high-level programming language like
Python, C#, Java, JavaScript, Go, Julia, Scratch, Ruby, Swift, or C. These
highly productive languages are used to write major programs from the
Linux operating system to websites like Facebook, to productivity
software like LibreOffice. If you learn to be a good programmer in a
couple of these, you can potentially find a well-paying interesting job
and write some great programs. If you create a program in one of these
languages, you can easily get it working on numerous operating
systems on multiple hardware architectures. You never have to learn
the details of all the bits and bytes, and these can remain safely under
the covers.
When you program in Assembly Language, you are tightly coupled
to a given CPU and moving your program to another requires a
complete rewrite of your program. Each Assembly Language
instruction does only a fraction of the amount of work, so to do
anything takes a lot of Assembly statements. Therefore, to do the same
work as, say, a Python program takes an order of magnitude larger
amount of effort, for the programmer. Writing in Assembly Language is
harder, as you must solve problems with memory addressing and CPU
registers that are all handled transparently by high-level languages. So
why would you want to learn Assembly Language programming?
Here are 10 reasons to learn and use Assembly Language:
1. To write more efficient code: Even if you don’t write Assembly
Language code, knowing how the computer works internally
allows you to write more streamlined code. You can make your
data structures easier to access and write code in a style that
allows the compiler to generate more effective code. You can make
better use of computer resources, like co-processors, and use the
given computer to its fullest potential.

2. To write your own operating system: The core of the operating


system that initializes the CPU handles hardware security and
multi-threading/multi-tasking requires Assembly code.

3. To create a new programming language: If it is a compiled


language, then you need to generate the Assembly code to execute.
The quality and speed of your language is largely dependent on
the quality and speed of the Assembly Language code it generates.

4. To make computers run faster: The best way to make Linux


faster is to improve the GNU C Compiler. If you improve the RISC-V
Assembly Language code produced by GNU C, then every program
compiled by GCC benefits.

5. To interface a computer to a hardware device: When


interfacing a computer through USB or GPIO ports, the speed of
data transfer is highly sensitive as to how fast a program can
process the data. Perhaps, there are a lot of bit-level
manipulations that are easier to program in Assembly Language.

6. To do faster machine learning or 3D graphics programming:


Both applications rely on fast matrix mathematics. If you can make
this faster with Assembly and/or using the co-processors, then
you can make AI-based robots or video games that much better.

7. To boost performance: Most large programs have components


written in different languages. If a program is 99% C++, the other
1% could be Assembly, perhaps giving a program a performance
boost or some other competitive advantage.
8. To create a new single-board computer: New boards have some
Assembly Language code to manage peripherals included with the
board. This code is usually called a BIOS (basic input/output
system).

9. To look for security vulnerabilities in a program or piece of


hardware: Look at the Assembly code to do this, otherwise you
may not know what is really going on, and hence where holes
might exist.

10. To look for Easter eggs in programs: These are hidden


messages, images, or inside jokes that programmers hide in their
programs. They are usually triggered by finding a secret keyboard
combination to pop them up. Finding them requires reverse
engineering the program and reading Assembly Language.

Now that we have some background on Assembly Language, we’ll


look at some choices for running RISC-V Assembly Language code.

Running Programs on RISC-V Systems


Due to the modular nature of RISC-V, there are a lot of possibilities for
running programs. This book will provide details on the following three
specific systems:
1. RISC-V-based Linux computer such as the Starfive Visionfive 2:
This is the easiest way to play with RISC-V Assembly Language,
since everything is done on the same computer. The CPU on the
Visionfive 2 board supports the RV64GC set of RISC-V extensions.

2. RISC-V simulator running on an Intel/AMD-based Windows or


Linux computer: Not everyone has a RISC-V computer yet, but
don’t let that stop you. You can simulate RISC-V running full Linux.

3. RISC-V microcontroller: There are many of these such as the


Espressif ESP32-C3 DevKit. First of all, write a program, then
compile it on another computer, such as a Linux laptop, and finally
download the compiled program to the microcontroller to run. This
is an important usage of RISC-V Assembly Language, but you will be
limited to a few basic instruction modules, namely, RV32IMC.
There are many other possibilities that also work, but you will need
to adapt the instructions for the supported systems. Next, we look at
how to set up each of these to assemble and run a simple Assembly
Language program to print out “Hello RISC-V World!”

Coding a Simple “Hello World” Program


Computers operate on binary data consisting of zeros and ones;
however, humans do not think this way. As a result, humans develop
tools to assist us in interacting with computers. One such tool is the
GNU Assembler which takes a human readable version of an Assembly
Language program and converts it to the zeroes and ones that the RISC-
V processor understands. In this section, we will present a simple
program without going into detail. The details will be filled in over the
following chapters. In this chapter, we’ll take the program and look at
several ways of Assembling and running it. First, we’ll look at running it
on the Starfive Visionfive 2 SBC under Linux.

Hello World on the Starfive Visionfive 2


The state of Linux on RISC-V computers is evolving quickly. At the time
of writing this book, the Debian Linux install image for the Starfive
Visionfive 2 computer is quite minimal and does not even include a web
browser. Please follow the instructions in its Quick Start Guide for the
most up-to-date information. These instructions will also give an idea
of what is required for other RISC-V-based Linux SBCs.
At the time of the writing of this book, these are the instructions to
set up the Visionfive 2. The steps are given in more detail in the
Visionfive’s Quick Start Guide. The Visionfive can run Linux from a
microSD card, an M.2 SSD drive, or from its internal firmware.
Before you start, you will need the following:
The Starfive Visionfive 2 board, USB keyboard, USB mouse, and HDMI
capable monitor
An microSD (Secure Digital) card
A burner program, like Balena Etcher
Secure Shell (SSH) program
Quick Start Guide
Internet connection
First, configure two dip switches on the motherboard to boot from
the preferred device. These instructions follow what is needed to boot
from a microSD card, but as long as the correct instructions are
followed, you can run Linux from any supported configuration.
1. Configure the two dip switches to eMMC.

2. Download the Debian Linux image for an SD card from Starfive’s


website: https://debian.starfivetech.com/. Next, burn it
onto an SD card using a program like Balena Etcher.

3. Place the SD card in the Visionfive 2 and turn the Visionfive on.

4. If you are using a hardwired Internet connection, you can skip this
step. When it boots to Linux, login, the password is “starfive.” Run
the Setup program and select your Wifi and enter the password.

5. Since the base image doesn’t contain a web browser, it is easiest to


perform the following steps using SSH from a host computer on the
same Wifi network. This way you can copy/paste commands from
the Quick Start Guide into the SSH program. Use “starfive.local” as
the host name.

ssh user@starfive.local

6. Next, the file system needs to be resized. If you don’t do this, you
can’t install any programs, since the base image has little free space.
This is a technical step and you should follow the instructions from
the Quick Start Guide carefully as you are deleting and recreating
disk partitions.

7. Now we perform the long step of installing the extra software that
includes web browsers and the development tools required for this
book. Use the scp command to copy the
“install_package_and_dependencies.sh” script to the computer and
run it. Enter the scp command from the host computer:

scp install_package_and_dependencies.sh
user@starfive.local:/home/user

8. Then the following commands from the SSH remote session.

chmod +x install_package_and_dependencies.sh
./install_package_and_dependencies.sh

Running this takes several hours, so it's a good time to go for


lunch. After this script completes, reboot the Visionfive 2.

9. Part of the previous shell script installs the GNU development tools.
If using a different Linux distribution of SBC, this may need to be
done manually using the command:

sudo apt install build-essential

10. After the computer reboots, login normally. There are now web
browsers, office tools, and software development tools.

11. Install a simple GUI text editor to use for programming. Use a
terminal-based text editor like vi if you wish. GEdit is a good
simple choice that can be installed with the following command:

sudo apt install gedit

With the computer setup, we are ready to write, assemble, and run a
simple “Hello World” program. In this chapter, we won’t worry about
the details of how this program works, rather we are ensuring we can
assemble, link, and run programs. We will examine how everything
works in detail in the following chapters.
Either download the source code from the Apress Github site or
type in the program in Listing 1-1 and save it as HelloWorld.S, where
capitalization is important.
#
# Risc-V Assembler program to print "Hello RISC-V
World!"
# to stdout.
#
# a0-a2 - parameters to linux function services
# a7 - linux function number
#

.global _start # Provide program starting


address to linker

# Setup the parameters to print hello world


# and then call Linux to do it.

_start: addi a0, x0, 1 # 1 = StdOut


la a1, helloworld # load address of
helloworld
addi a2, x0, 20 # length of our
string
addi a7, x0, 64 # linux write system
call
ecall # Call linux to
output the string

# Setup the parameters to exit the program


# and then call Linux to do it.

addi a0, x0, 0 # Use 0 return code


addi a7, x0, 93 # Service command code
93 terminates
ecall # Call linux to
terminate the program

.data
helloworld: .ascii "Hello RISC-V World!\n"
Listing 1-1 The Linux version of the Hello World program
Anything after a hash sign (#) is a comment. The code lines consist
of an optional label, followed by an opcode, possibly followed by several
parameters. To compile and run this program, create a file called build
that contains the contents of Listing 1-2:

as -o HelloWorld.o HelloWorld.S
ld -o HelloWorld HelloWorld.o
Listing 1-2 Build file for Hello World

After saving the build file, convert it to an executable file with the
following command:

chmod +x build

Now compile the program by running the build file and then the
resulting executable program with following commands:

./build
./HelloWorld

Figure 1-1 shows the result of running this. bash -x is used to show
the commands being executed.

Figure 1-1 Compiling and running the Hello World program

We’ve now written and executed our first Assembly Language


program. Next, let’s look at running Hello World on a RISC-V emulator.
Programming Hello World in the QEMU Emulator
Even without owning a genuine RISC-V processor, you can still emulate
a RISC-V-based computer using the QEMU emulator. QEMU is similar to
virtualization software like VMWare, except that the computer being
virtualized isn’t required to have the same CPU as the host system.
In this case, we’ll give instructions on how to install and run the
QEMU emulator on a Windows computer and emulate/virtualize a
RISC-V-based Linux system. Next, we will run exactly the same
programs that we run on the genuine RISC-V-based Starfive Visionfive
2.

Install QEMU on Windows


Here are the instructions to install QEMU on Windows and get a RISC-V
version of Linux up to the first login prompt. Before you start, you will
need the following:
QEMU software.
7-Zip or any uncompression program that understands xz format.
fw_jump.elf and uboot.elf. These are easy to install on Linux,
but a bit hard to find for Windows, so they are placed on the Apress
Github site under Chapter 1.
The QEMU documentation is extensive, but overwhelming. The
following are the steps required for this book:
1. Install QEMU. Download the Windows install image. Run the
downloaded program saying Yes to the Windows security prompts.

2. Add c:\Program Files\Qemu to the system PATH, assuming the


default installation folder.

3. Download the Ubuntu Server preinstalled image from


https://ubuntu.com/download/risc-v.

4. Use 7-Zip or any uncompression program that understands xz


format to extract the .img file from what you downloaded.
5. Rename the .img file as ubunturv.img, or change the name in the
commands that follow.

6. Add free space to the image with

qemu-img resize ubunturv.img +10G

This gives room to install programs once you start Linux.


7. To run the image, use fw_jump.elf and uboot.elf. These then
act as the emulated board’s BIOS.

8. Now we are ready to run QEMU with the rather long command:

qemu-system-riscv64 ^
-machine virt ^
-cpu rv64 ^
-m 2G ^
-device virtio-blk-device,drive=hd ^
-drive file=ubunturv.img,if=none,id=hd ^
-device virtio-net-device,netdev=net ^
-netdev
user,id=net,hostfwd=tcp::2222-:22 ^
-bios fw_jump.elf ^
-kernel uboot.elf ^
-append “root=LABEL=rootfs
console=ttyS0” ^
-nographic

In a Windows cmd prompt, at the time of this writing, not all


the cursor and other special characters translate properly. Rather
than logining in at the Linux prompt directly, use a ssh command to
login with

ssh -p 2222 ubuntu@localhost


Then in the ssh window, the cursor keys and other special
characters work properly.

Install QEMU on Linux


Now let’s do the same thing for Linux.
1. Install QEMU with

sudo apt install qemu-system-riscv64


sudo apt install u-boot-qemu opensbi

2. Download the Ubuntu Server preinstalled image from


https://ubuntu.com/download/risc-v.

3. From the downloaded Ubuntu image, extract the img file from the
xz file with

unxz < downloaded_file_name.img.xz > ubunturv.img

4. Expand the image, so there is some disk space to play around with

sudo qemu-img resize ubunturv.img +10G

5. Now we are ready to run with the rather long qemu command:

qemu-system-riscv64 \
-machine virt \
-cpu rv64 \
-m 2G \
-device virtio-blk-device,drive=hd \
-drive file=ubunturv.img,if=none,id=hd \
-device virtio-net-device,netdev=net \
-netdev
user,id=net,hostfwd=tcp::2222-:22 \
-bios /usr/lib/riscv64-linux-
gnu/opensbi/generic/fw_jump.elf \
-kernel /usr/lib/u-boot/qemu-
riscv64_smode/uboot.elf \
-object rng-
random,filename=/dev/urandom,id=rng \
-device virtio-rng-device,rng=rng \
-append “root=LABEL=rootfs
console=ttyS0” \
-nographic

Compiling in Emulated Linux


Whether using Windows or Linux to host QEMU, booting Linux under
the emulator will eventually lead you to a login prompt. The initial user
id/password is ubuntu/ubuntu. During the first time logging in, Linux
will force to change the password. The computer name is also ubuntu
which can be changed if needed. Once logged in, we can add the GNU
Compiler Collection with

sudo apt update


sudo apt install build-essential

This will also install GDB and a few other GCC-related tools.
Next, either type the HelloWorld.S program from the previous
section into vi to save it to the new image or copy the program to this
image using scp:

scp -P 2222 HelloWorld.S


ubuntu@localhost:/home/ubuntu

Then compile and run the program:

as -o HelloWorld.o HelloWorld.S
ld -o HelloWorld HelloWorld.o
./HelloWorld

Figure 1-2 shows the output of this process.


Figure 1-2 Compiling and running Hello World in the QEMU emulator
Now, let’s look at running Hello World on a Espressif ESP32-C3
Devkit microcontroller.

About Hello World on the ESP32-C3


Microcontroller
The Espressif ESP32-C3 Devkit is a RISC-V-based microcontroller
similar to a Raspberry Pi Pico. There are many RISC-V microcontrollers
on the market, but this one has an extensive SDK allowing
programming in MicroPython, C/C++, or Assembly Language. The
documentation is extensive and Espressif has a lot of experience in the
microcontroller world.
When developing for a microcontroller, you need a host computer
where you write the code and compile your program. You then
download the resulting program to run, usually through a USB cable.
The host computer can be a Windows, MacOS, or Linux-based
computer. The online Espressif Getting Started Guide is recommended
to install the Espressif SDK on your computer. The examples in this
book will be developed on a Windows-based computer, but this doesn’t
really matter. To install the Espressif SDK on Windows is quite simple,
just requiring that a standard Windows installation program be run.
Projects in the Espressif SDK use the CMake system for building and
are more complex than what we have seen so far. The easiest way to get
a project running quickly is to take one of the SDK’s example projects,
then transform it into what is required. The Espressif SDK contains a
Hello World program written in C. Let’s convert it to a Hello World
program written entirely in Assembly Language. Take the example
program located at the following link: C:\Espressif\frameworks\esp-
idf-v5.0.2\examples\get-started\hello_world, to create our hello_world
project:
1. Copy the SDK example hello_world project to a new folder, say
hello_world_asm.

2. Change main\CMakeLists.txt to replace the source file


hello_world_main.c with hello_world_main.S.

3. Delete main\hello_world_main.c and create


main\hello_world_main.S from Listing 1-2.

The “Hello World” program is a little different than the Linux


version and contained in Listing 1-3:

#
# Risc-V Assembler program to print "Hello RISC-V
World!"
# to the Espressif SDK monitor program through the
microUSB.
#
# a0 - address of helloworld string
#

.global app_main # Provide program starting


address to linker

# Setup the parameter to print hello world


# and then call SDK to do it.

app_main:
la a0, helloworld # load address of
helloworld
call puts # Call sdk to output
the string
j app_main # loop forever

.data
helloworld: .asciz "Hello RISC-V World!"
Listing 1-3 Espressif version of the Hello World program
We build the program with:

idf.py build

Note Remember to use the special version of the Command (CMD)


prompt on your desktop that the Espressif SDK install added. This
CMD contains all the correct environment variables required for a
successful build.

The first time CMD runs, it takes a long time since it needs to build the
entire SDK as part of the process. On subsequent builds, it is much
quicker as it only needs to build the files that have changed. Figure 1-3
shows the output of the build process on one of these following builds.
Figure 1-3 Building the Espressif version of the Hello World program
And then run it with the following:

idf.py -p com4 flash monitor

Figure 1-4 shows the end of the download process, followed by the
start of the program.
Figure 1-4 Running Hello World on the Espressif RISC-V microcontroller

Note The required com port might not be com4, to check the
correct port, run the Windows Device Manager, open the “Ports
(COM & LPT)” section, and note which COM port is associated with
the “Silicon Labs CP210x USB to UART Bridge.”

In the Linux version of the program, the program terminated after


printing the “Hello RISC-V World!” string once. Microcontrollers don’t
have an operating system to exit to, so they are usually implemented as
an infinite loop that runs forever, as was done in this simple example.
To exit the monitor, use Control-].

Summary
In this chapter, we introduced the RISC-V processor and Assembly
Language programming along with why Assembly Language is used. We
covered how to set up three possible development environments for
Assembly Language programming using RISC-V.
In Chapter 2, “Loading and Adding,” we will start to understand the
details of the programs presented, including the basics of Assembly
Language instructions and CPU registers. We’ll learn how numbers are
represented in a computer and how basic integer addition and
subtraction work.

Exercises
1. Setup your RISC-V development environment. Whether it is on a
Linux Single Board Computer (SBC) such as the Starfive Visionfive
2, running in the QEMU emulator or using a microcontroller like the
Espressif ESP32-C3.

2. Modify the “Hello RISC-V World!” string to a different string. For the
Linux versions, remember to modify the string length as well.
© The Author(s), under exclusive license to APress Media, LLC, part of Springer
Nature 2024
S. Smith, RISC-V Assembly Language Programming, Maker Innovations Series
https://doi.org/10.1007/979-8-8688-0137-2_2

2. Loading and Adding


Stephen Smith1
(1) Gibsons, BC, Canada

The goal of this chapter is to load various integer values into the CPU
and perform basic addition and subtraction operations on these values.
This sounds simple, but at the low level of Assembly Language, this can
get complicated. In this chapter, we will go step-by-step through the
various forms of the add, sub, and Shift instructions, to lay the
groundwork on how all instructions work, especially in the way they
handle parameters. This is so that in subsequent chapters, we can
proceed at a faster pace as we encounter the rest of the RISC-V
instruction set.
Before getting into the various Assembly Language instructions, we
will discuss how the RISC-V CPU represents positive and negative
numbers.

Computers and Numbers


We typically represent numbers using base ten. The common theory is
we do this because we have ten fingers to count with. This means a
number like 387 is really a representation for

387 = 3 ∗ 102 + 8 ∗ 101 + 7 ∗ 100


= 3 ∗ 100 + 8 ∗ 10 + 7
= 300 + 80 + 7
There is nothing special about using ten as our base, and a fun
exercise in math class is to do arithmetic using other bases. In fact, the
Mayan culture used base 20, perhaps because we have 20 digits: ten
fingers and ten toes.
Computers don’t have fingers and toes, just switches that are either
on or off. As a result, it is natural for computers to use base two
arithmetic. Thus, to a computer, a number like 1011 is represented by
the following:

1011 = 1 ∗ 23 + 0 ∗ 22 + 1 ∗ 21 + 1 ∗ 20
= 1 ∗ 8 + 0 ∗ 4 + 1 ∗ 2 + 1
= 8 + 0 + 2 + 1
= 11 (decimal)

This is great for computers, but we use four digits for the decimal
number 11 rather than two digits. The big disadvantage for humans is
that writing out binary numbers is tiring, because they take up so many
digits.
Computers are incredibly structured, so all their numbers are the
same size. When designing computers, it doesn’t make sense to have a
variety of differently sized numbers, so a few common sizes have
become standard as described in the following paragraph.
A byte is eight binary bits or digits. In our preceding example with
four bits, there are 16 possible combinations of 0s and 1s. This means
four bits can represent the numbers 0 to 15. This means it can be
represented by one base 16 digit. Base 16 digits are represented by the
numbers 0 to 9 and then the letters A–F for 10–15. We can then
represent a byte (eight bits) as two base 16 digits. We refer to base 16
numbers as hexadecimal (Figure 2-1).

Figure 2-1 Representing hexadecimal digits

Since a byte holds eight bits, it can represent 28 (256) numbers.


Thus, the byte E6 represents

E6 = e ∗ 161 + 6 ∗ 160
= 14 ∗ 16 + 6
= 230 (decimal)
= 1110 0110 (binary).
We call a 32-bit quantity a word, and it is represented by four bytes.
You might see a string like B6 A4 44 04 as a representation of 32 bits of
memory, or one word of memory, or perhaps the contents of one
register. If we use a 64-bit RISC-V processor, then a word is still 32-bits,
and 64-bit quantities are referred to as doublewords, and we would see
a string of eight bytes to represent one doubleword of data.
If this is confusing or scary, do not worry. The tools will do all the
conversions for you. It’s just a matter of understanding what is
presented to you on the screen. Also, if an exact binary number needs to
be specified, that is usually done in hexadecimal, although all the tools
accept all the formats.
A handy tool is the Linux Gnome Calculator (Figure 2-2). The Gnome
Calculator has a Programming Mode which shows a numbers
representation in multiple bases at once. This calculator is included
with Ubuntu Linux with the Gnome desktop. To install it on the
Visionfive 2, enter the following command line:

sudo apt-get install gnome-calculator

In “Programmer Mode,” conversions can be done with numbers


shown in several formats at once.
This is how data is represented on a computer. There is more
complexity in how signed integers are represented and how arithmetic
works.
Other documents randomly have
different content
back
back
back
back
back
back
back
back
back
back
back
back
back
back
back
back
back
back
back
back
back
back
back
back
back
Welcome to our website – the perfect destination for book lovers and
knowledge seekers. We believe that every book holds a new world,
offering opportunities for learning, discovery, and personal growth.
That’s why we are dedicated to bringing you a diverse collection of
books, ranging from classic literature and specialized publications to
self-development guides and children's books.

More than just a book-buying platform, we strive to be a bridge


connecting you with timeless cultural and intellectual values. With an
elegant, user-friendly interface and a smart search system, you can
quickly find the books that best suit your interests. Additionally,
our special promotions and home delivery services help you save time
and fully enjoy the joy of reading.

Join us on a journey of knowledge exploration, passion nurturing, and


personal growth every day!

ebookmasss.com

You might also like