1.start.s 位于flash的第一段代码

maskrom中 的代码会引导程序到pflash的零地址 即 0x20000000,所以程序从这个地址开始。

  1. 读取hard id
  2. 如果为0 就跳转_core0 执行打印
  3. _core0 函数 往flash 0x20000000 一个一个写字母

start.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
	.section .text             //定义数据段名为.text
.globl _start //定义全局符号_start
.type _start,@function //_start为函数

_start: //函数入口
csrr a0, mhartid //csr是riscv专有的内核私有寄存器,独立在12位地址
//mhartid寄存是定义了内核的hart id,这里读取到a0寄存器里
li t0, 0x0 //li是伪指令,加载立即数0到t0
beq a0, t0, _core0 //比较a0和t0,相等则跳转到_core0地址处,否则向下执行
_loop: //定义一个_loop符号
j _loop //跳转到_loop,此处形成循环,用意为如果当前cpu core不为
//hart 0则循环等待,为hart 0则继续向下执行
_core0: //定义一个core0才能执行到此处
li t0, 0x100 //t0 = 0x100
slli t0, t0, 20 //t0 左移20位 t0 = 0x10000000
li t1, 'H' //t1 = 'H' 字符的ASCII码值写入t1
sb t1, 0(t0) //s是store写入的意思,b是byte,这里指的是写入t1
//的值到t0指向的地址,即为写入0x10000000这个寄存器
//这个寄存器正是uart0的发送data寄存器,此时串口会输出"H"
li t1, 'e' //接下来都是重复内容
sb t1, 0(t0)
li t1, 'l'
sb t1, 0(t0)
li t1, 'l'
sb t1, 0(t0)
li t1, 'o'
sb t1, 0(t0)
li t1, ' '
sb t1, 0(t0)
li t1, 'Q'
sb t1, 0(t0)
li t1, 'u'
sb t1, 0(t0)
li t1, 'a'
sb t1, 0(t0)
li t1, 'r'
sb t1, 0(t0)
li t1, 'd'
sb t1, 0(t0)
li t1, ' '
sb t1, 0(t0)
li t1, 'S'
sb t1, 0(t0)
li t1, 't'
sb t1, 0(t0)
li t1, 'a'
sb t1, 0(t0)
li t1, 'r'
sb t1, 0(t0)
li t1, ' '
sb t1, 0(t0)
li t1, 'b'
sb t1, 0(t0)
li t1, 'o'
sb t1, 0(t0)
li t1, 'a'
sb t1, 0(t0)
li t1, 'r'
sb t1, 0(t0)
li t1, 'd'
sb t1, 0(t0)
li t1, '!'
sb t1, 0(t0)
li t1, '\n'
sb t1, 0(t0) //到这里就会输出"Hello Quard Star board!"
j _loop //完成后进入loop

.end //汇编文件结束符号

2.lds 链接文件

lds 将.s的代码 链接到flash处

boot.lds

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
OUTPUT_ARCH( "riscv" )  /*输出可执行文件平台*/

ENTRY( _start ) /*程序入口函数*/

MEMORY /*定义内存域*/
{
/*定义名为flash的内存域属性以及起始地址,大小等*/
flash (rxai!w) : ORIGIN = 0x20000000, LENGTH = 512k
}

SECTIONS /*定义段域*/
{
.text : /*.text段域*/
{
KEEP(*(.text)) /*将所有.text段链接在此域内,keep是保持防止优化,即无论如何都保留此段*/
} >flash /*段域的地址(LMA和VMA相同)位于名为flash内存域*/
}

build.sh

这里交叉编译器使用riscv64-unknown-elf-gcc 用于编译面向RISC-V 64位架构的ELF(可执行和可链接格式)目标文件。这个前缀表示,你正在使用的是针对RISC-V架构未知操作系统(ELF)的GCC编译器。

  • 制作固件,我们的pflash为32M,因此flash固件比较为32M,我们生成一个32文件,空余位置暂时先填充0,这样就得到了fw.bin固件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CROSS_PREFIX=riscv64-unknown-elf
if [ ! -d "$SHELL_FOLDER/output/lowlevelboot" ]; then
mkdir $SHELL_FOLDER/output/lowlevelboot
fi
cd boot
$CROSS_PREFIX-gcc -x assembler-with-cpp -c start.s -o $SHELL_FOLDER/output/lowlevelboot/start.o
$CROSS_PREFIX-gcc -nostartfiles -T./boot.lds -Wl,-Map=$SHELL_FOLDER/output/lowlevelboot/lowlevel_fw.map -Wl,--gc-sections $SHELL_FOLDER/output/lowlevelboot/start.o -o $SHELL_FOLDER/output/lowlevelboot/lowlevel_fw.elf
# 使用gnu工具生成原始的程序bin文件
$CROSS_PREFIX-objcopy -O binary -S $SHELL_FOLDER/output/lowlevelboot/lowlevel_fw.elf $SHELL_FOLDER/output/lowlevelboot/lowlevel_fw.bin
# 使用gnu工具生成反汇编文件,方便调试分析(当然我们这个代码太简单,不是很需要)
$CROSS_PREFIX-objdump --source --demangle --disassemble --reloc --wide $SHELL_FOLDER/output/lowlevelboot/lowlevel_fw.elf > $SHELL_FOLDER/output/lowlevelboot/lowlevel_fw.lst

cd $SHELL_FOLDER/output/lowlevelboot
rm -rf fw.bin
dd of=fw.bin bs=1k count=32k if=/dev/zero
dd of=fw.bin bs=1k conv=notrunc seek=0 if=lowlevel_fw.bin

run.sh

  • 加载固件运行qemu仿真,向之前run.sh脚本添加-drive if=pflash的参数就可以将固件配置到模拟器的固件加载位置,根据之前qemu内的路径就可以加载执行固件内的代码。
1
2
3
4
5
6
7
8
9
10
SHELL_FOLDER=$(cd "$(dirname "$0")";pwd)

$SHELL_FOLDER/output/qemu/bin/qemu-system-riscv64 \
-M quard-star \
-m 1G \
-smp 8 \
-bios none \
#-monitor stdio \
-drive if=pflash,bus=0,unit=0,format=raw,file=$SHELL_FOLDER/output/lowlevelboot/fw.bin \
-nographic --parallel none

sudo ./build.sh

sudo ./run.sh

image-20240605202837030

3.问题

注意安装riscv64交叉编译环境的时候汇报这个错误,可能要指明使用的lib路径

1
2
$CROSS_PREFIX-gcc -L/opt/riscv/riscv64-unknown-elf/lib -L/opt/riscv/lib -x assembler-with-cpp -c start.s -o $SHELL_FOLDER/output/lowlevelboot/start.o
$CROSS_PREFIX-gcc -L/opt/riscv/riscv64-unknown-elf/lib -L/opt/riscv/lib

image-20240604194042617

报错

./run.sh: 9: -drive: not found

原因是上面命令有注释然后命令断开了

1
2
3
4
5
6
7
8
9
SHELL_FOLDER=$(cd "$(dirname "$0")";pwd)

$SHELL_FOLDER/output/qemu/bin/qemu-system-riscv64 \
-M quard-star \
-m 1G \
-smp 8 \
-bios none \
-drive if=pflash,bus=0,unit=0,format=raw,file=$SHELL_FOLDER/output/lowlevelboot/fw.bin \
-nographic --parallel none