Skip to content

Commit 81770b2

Browse files
committed
Use new riscv-from-scratch repo setup instructions
This allows us to remove all assembly and linker files from the 'assets' folder. This commit includes other various small fixes.
1 parent 79e6410 commit 81770b2

File tree

4 files changed

+58
-299
lines changed

4 files changed

+58
-299
lines changed

_posts/2019-04-27-riscv-from-scratch-2.markdown

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,31 @@ The `freedom-e-sdk` made it trivial for us to compile, debug, and run any C prog
1919

2020
In this post, we'll break free from the `freedom-e-sdk`. We'll write and attempt to debug a simple C program of our own, unveil the magic hidden behind `main`, and examine the hardware layout of a `qemu` virtual machine. We'll then examine and modify a linker script, write our own C runtime to get our program set up and running, and finally invoke GDB and step through our program.
2121

22+
### Setup
23+
2224
If you missed the previous post in this series and don't have `riscv-qemu` and the RISC-V toolchain installed and were hoping to follow along, jump to the ["QEMU and RISC-V toolchain setup"](/riscv-from-scratch/2019/03/10/riscv-from-scratch-1.html#qemu-and-risc-v-toolchain-setup) section (or in RISC-V assembly, `jal x0, qemu_and_toolchain_setup`) and complete that before moving on.
2325

26+
Next, let's set up a workspace for the code we will write today (and in future posts).
27+
28+
{% highlight bash %}
29+
git clone git@github.com:twilco/riscv-from-scratch.git
30+
# or `git clone https://github.com/twilco/riscv-from-scratch.git` to clone
31+
# via HTTPS rather than SSH
32+
# alternatively, if you are a GitHub user, you can fork this repo.
33+
# https://help.github.com/en/articles/fork-a-repo
34+
35+
cd riscv-from-scratch/work
36+
{% endhighlight %}
37+
38+
As the name suggests, the `work` directory will serve as our working directory for this and future posts.
39+
2440
### The naive approach
2541

26-
Let's start our journey with a simple C program that infinitely adds two numbers together.
42+
Let's start our journey by using the text editor of your choice to create a simple C program called `add.c` that infinitely adds two numbers together.
43+
44+
{% highlight c %}
45+
// file: riscv-from-scratch/work/add.c
2746

28-
{% highlight bash %}
29-
cat add.c
3047
int main() {
3148
int a = 4;
3249
int b = 12;
@@ -133,6 +150,7 @@ To figure out what's going on here, we need to take a detour and talk about how
133150
To answer these questions, let's re-run our GCC command with the `-v` flag to get a more verbose output of what it is actually doing.
134151

135152
{% highlight bash %}
153+
# In the `riscv-from-scratch/work` directory...
136154
riscv64-unknown-elf-gcc add.c -O0 -g -v
137155
{% endhighlight %}
138156

@@ -174,28 +192,25 @@ As we saw above, `gcc` links a default `crt0` unless told to do otherwise. This
174192

175193
This may work fine in a general case, but is undoubtedly not going to work for every RISC-V processor. As mentioned previously, one of `crt0`s jobs is to set up the stack, but how can it do that if it doesn't know _where_ the stack should be for the CPU (`-machine`) we're running against? Answer: it can't, at least not without us giving it a bit of assistance.
176194

177-
Circling back to the `qemu` command we ran at the beginning of this post (`qemu-system-riscv64 -machine virt -m 128M -gdb tcp::1234 -kernel a.out`), recall we were using the `virt` machine. Fortunately for us, `qemu` exposes a simple way to dump information about a machine in `dtb` (device tree blob) format.
195+
Circling back to the `qemu` command we ran at the beginning of this post (`qemu-system-riscv64 -machine virt -m 128M -gdb tcp::1234 -kernel a.out`), recall we were using the `virt` machine. Fortunately for us, `qemu` exposes a simple way to dump information about a machine in `dtb` (devicetree blob) format.
178196

179197
{% highlight bash %}
180-
# Go to the ~/usys/riscv folder we created before and create a new dir
181-
# for our machine information.
182-
cd ~/usys/riscv && mkdir machines
183-
cd machines
198+
# In the `riscv-from-scratch/work` directory...
184199

185-
# Use qemu to dump info about the 'virt' machine in dtb (device tree blob)
200+
# Use qemu to dump info about the 'virt' machine in dtb (devicetree blob)
186201
# format.
187202
# The data in this file represents hardware components of a given
188203
# machine / device / board.
189204
qemu-system-riscv64 -machine virt -machine dumpdtb=riscv64-virt.dtb
190205
{% endhighlight %}
191206

192-
Data in `dtb` format is difficult to read considering it's mostly binary, but there is a command-line tool called `dtc` (device tree compiler) that can convert it into something more human-readable.
207+
Data in `dtb` format is difficult to read considering it's mostly binary, but there is a command-line tool called `dtc` (devicetree compiler) that can convert it into something more human-readable.
193208

194209
{% highlight bash %}
195210
# I'm running MacOS, so I use Homebrew to install this. If you're
196211
# running another OS you may need to do something else.
197212
brew install dtc
198-
# Convert our .dtb into a human-readable .dts (device tree source) file.
213+
# Convert our .dtb into a human-readable .dts (devicetree source) file.
199214
dtc -I dtb -O dts -o riscv64-virt.dts riscv64-virt.dtb
200215
{% endhighlight %}
201216

@@ -243,9 +258,7 @@ Rather than writing our own linker file from scratch, it is going to make more s
243258
Knowing this, let's copy the default linker script `riscv64-unknown-elf-ld` uses into a new file:
244259

245260
{% highlight bash %}
246-
cd ~/usys/riscv
247-
# Make a new dir for custom linker scripts out RISC-V CPUs may require.
248-
mkdir ld && cd ld
261+
# In the `riscv-from-scratch/work` directory...
249262
# Copy the default linker script into riscv64-virt.ld
250263
riscv64-unknown-elf-ld --verbose > riscv64-virt.ld
251264
{% endhighlight %}
@@ -307,12 +320,10 @@ SECTIONS
307320

308321
As you can see, we use the [PROVIDE command](https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/4/html/Using_ld_the_GNU_Linker/assignments.html#PROVIDE) to define a symbol called `__stack_top`. `__stack_top` will be accessible from any program linked with this script (assuming the program itself does not also define something named `__stack_top`). We set the value of `__stack_top` to be `ORIGIN(RAM)`, which we know is `0x80000000`, plus `LENGTH(RAM)`, which we know is 128 megabytes (`0x8000000` bytes). This means our `__stack_top` is set to `0x88000000`.
309322

310-
For brevity's sake I won't include the entire linker file here, but if you want the final product you can view/download it here: [{{ site.url }}{% link /assets/ld/riscv64-virt.ld %}](/assets/ld/riscv64-virt.ld)
311-
312323
### Stop! <s>Hammertime</s> Runtime!
313324
<div style="margin-top: -30px; margin-bottom: 10px;"><sub><sup><sub><sup><a href="https://www.youtube.com/watch?v=otCpCn0l4Wo">https://www.youtube.com/watch?v=otCpCn0l4Wo</a></sup></sub></sup></sub></div>
314325

315-
We finally have all we need to create a custom C runtime that works for us, so let's get started. What we need is actually very simple - here is `crt0.s` in its entirety:
326+
We finally have all we need to create a custom C runtime that works for us, so let's get started. Create a file called `crt0.s` in the `riscv-from-scratch/work/` directory and insert the following:
316327

317328
{% highlight nasm %}
318329
.section .init, "ax"
@@ -427,10 +438,9 @@ Our very last line is an assembler directive, `.end`, which simply marks the end
427438

428439
To recap, we've worked through many problems in our quest of debugging a simple C program on a RISC-V processor. We first used `qemu` and `dtc` to find where our memory was located in the `virt` virtual RISC-V machine. We then used this information to take manual control of the memory layout in our customized version of the default `riscv64-unknown-elf-ld` linker script, which then enabled us to accurately define a `__stack_top` symbol. We finished by using this symbol in our own custom `crt0.s` that set up our stack and global pointers and finally called the `main` function. Let's make use of all this work to complete our original goal of debugging our simple C program in GDB.
429440

430-
As a reminder, here was our program:
441+
As a reminder, here was our `add.c` program:
431442

432443
{% highlight c %}
433-
cat add.c
434444
int main() {
435445
int a = 4;
436446
int b = 12;
@@ -446,7 +456,7 @@ And now to compile and link:
446456
{% highlight bash %}
447457
riscv64-unknown-elf-gcc -g -ffreestanding -O0 -Wl,--gc-sections \
448458
-nostartfiles -nostdlib -nodefaultlibs -Wl,-T,riscv64-virt.ld \
449-
crt0.s ns16550a.s
459+
crt0.s add.c
450460
{% endhighlight %}
451461

452462
You'll notice we have specified _a lot_ more flags than we did last time, so let's walk through all the ones we didn't cover in the first section.

_posts/2019-07-08-riscv-from-scratch-3.markdown

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,23 +41,34 @@ UARTs and USARTs are all around you, even if you may not realize it. They are b
4141

4242
### Setup
4343

44-
Before we get down to writing our driver, we'll need a few things set up to ensure we can properly compile and link. If you've worked through the previous two posts in this series, you shouldn't have to do anything here, although you may want to make a copy of our linker script and runtime files in the new directory we create.
44+
Before we get down to writing our driver, we'll need a few things set up to ensure we can properly compile and link. If you've worked through the previous two posts in this series you shouldn't have to do anything here beyond a `cd some/path/to/riscv-from-scratch/work`.
4545

46-
In the [previous post]({% post_url 2019-04-27-riscv-from-scratch-2 %}), we customized the default linker script to expose a `__stack_top` symbol and created a minimal C runtime to perform basic initialization tasks, namely setting up the stack and global pointers and calling into `main`. That post details why these things are important, but that information is unnecessary to continue on in this post, so feel free to simply download the necessary files here:
46+
However, if you missed the previous posts in this series:
4747

48-
[crt0.s](/assets/crt0.s)
49-
50-
[riscv64-virt.ld](/assets/ld/riscv64-virt.ld)
51-
52-
Now that you have downloaded these files (or if you have them from the previous post), move or copy them into a new directory which will serve as the workspace for today's post.
48+
1. Ensure you have `riscv-qemu` and the RISC-V toolchain installed. You can follow [these instructions](/riscv-from-scratch/2019/03/10/riscv-from-scratch-1.html#qemu-and-risc-v-toolchain-setup) from the first post to complete this.
49+
2. Clone or fork the [riscv-from-scratch repo](https://github.com/twilco/riscv-from-scratch):
50+
{% highlight bash %}
51+
git clone git@github.com:twilco/riscv-from-scratch.git
52+
# or `git clone https://github.com/twilco/riscv-from-scratch.git` to clone
53+
# via HTTPS rather than SSH
54+
# alternatively, if you are a GitHub user, you can fork this repo.
55+
# https://help.github.com/en/articles/fork-a-repo
5356

57+
cd riscv-from-scratch/work
58+
{% endhighlight %}
59+
{:start="3"}
60+
3. Check out the branch that contains the code prerequisites, located in `src`, for this post: <br/>
61+
{% highlight bash %}
62+
git checkout pre-uart-driver-skeleton
63+
{% endhighlight %}
64+
{:start="4"}
65+
4. Copy the customized linker script `riscv64-virt.ld` and minimal C runtime `crt0.s` to our working directory: <br/>
5466
{% highlight bash %}
55-
mkdir -p ~/projects/riscv-uart
56-
mv ~/Downloads/crt0.s ~/projects/riscv-uart
57-
mv ~/Downloads/riscv64-virt.ld ~/projects/riscv-uart
67+
# note: this will overwrite any existing files you may have in `work`
68+
cp -a src/. work
5869
{% endhighlight %}
5970

60-
You'll also need to have the GNU RISC-V toolchain and QEMU installed to facilitate compilation and emulation. Follow [these instructions](/riscv-from-scratch/2019/03/10/riscv-from-scratch-1.html#qemu-and-risc-v-toolchain-setup) from the first post in this series to complete this.
71+
If you're curious to know more about this customized linker script and minimal C runtime, check out the [previous post]({% post_url 2019-04-27-riscv-from-scratch-2 %}).
6172

6273
### Hardware layout in review
6374

@@ -114,10 +125,10 @@ This brings us to the last property in our `uart` node, `compatible = "ns16550a"
114125

115126
### Creating the basic skeleton of our driver
116127

117-
We have all we need to begin writing our driver, so let's begin. Start by ensuring you're in the directory we created before with our C runtime and linker script:
128+
We have all we need to begin writing our driver, so let's begin. Start by ensuring you're in the `riscv-from-scratch/work` directory we created in the setup section:
118129

119130
{% highlight bash %}
120-
cd ~/projects/riscv-uart
131+
cd some/path/to/riscv-from-scratch/work
121132
{% endhighlight %}
122133

123134
Now create a file called `ns16550a.s`, which will contain the code for our NS16550A UART driver. In this file, let's start with a basic skeleton containing the functions we want to expose. For now, we'll limit this driver to simply reading and writing chars, or bytes, without worrying about other available capabilities of the NS16550A, such as interrupts.
@@ -154,15 +165,15 @@ riscv64-unknown-elf-gcc -g -ffreestanding -O0 -Wl,--gc-sections \
154165
This should result in the following error:
155166

156167
{% highlight bash %}
157-
/Users/twilcock/usys/riscv/riscv64-unknown-elf-gcc-8.2.0-2019.02.0-x86_64-apple-darwin/bin/../lib/gcc/riscv64-unknown-elf/8.2.0/../../../../riscv64-unknown-elf/bin/ld: /var/folders/rg/hbr8vy7d13z9k7pdn0l_n9z51y1g13/T//ccjYQiJc.o: in function `.L0 ':
158-
/Users/twilcock/projects/riscv-uart/crt0.s:12: undefined reference to `main'
168+
/Users/twilco/usys/riscv/riscv64-unknown-elf-gcc-8.2.0-2019.02.0-x86_64-apple-darwin/bin/../lib/gcc/riscv64-unknown-elf/8.2.0/../../../../riscv64-unknown-elf/bin/ld: /var/folders/rg/hbr8vy7d13z9k7pdn0l_n9z51y1g13/T//ccjYQiJc.o: in function `.L0 ':
169+
/Users/twilco/projects/riscv-from-scratch/work/crt0.s:12: undefined reference to `main'
159170
collect2: error: ld returned 1 exit status
160171
{% endhighlight %}
161172

162173
An utter disaster! Taking a closer look at the error, this line tells us what we need to do:
163174

164175
{% highlight bash %}
165-
/Users/twilcock/projects/riscv-uart/crt0.s:12: undefined reference to `main`
176+
/Users/twilco/projects/riscv-from-scratch/work/crt0.s:12: undefined reference to `main`
166177
{% endhighlight %}
167178

168179
Taking a look at our `crt0.s` file, we do indeed see a reference to a symbol called `main`:
@@ -184,7 +195,7 @@ _start:
184195
.end
185196
{% endhighlight %}
186197

187-
This is an easy fix - we simply need to link a file that defines the `main` symbol. We would've wanted to do this anyways at some point, as we need some way to exercise our UART driver, and we can easily do so from `main`. Create a new file called `main.c` in our working directory (`~/projects/riscv-uart`) and define a main function. We'll also call `uart_put_char` to ensure that `main` is able to find our definition of it in `ns16550a.s`.
198+
This is an easy fix - we simply need to link a file that defines the `main` symbol. We would've wanted to do this anyways at some point, as we need some way to exercise our UART driver, and we can easily do so from `main`. Create a new file called `main.c` in our working directory (`riscv-from-scratch/work`) and define a main function. We'll also call `uart_put_char` to ensure that `main` is able to find our definition of it in `ns16550a.s`.
188199

189200
{% highlight c %}
190201
int main() {

assets/crt0.s

Lines changed: 0 additions & 14 deletions
This file was deleted.

0 commit comments

Comments
 (0)