开发一个RISC-V上的操作系统(一)—— 环境搭建

目录

一、开发环境配置

二、测试

三、Makefile

四、开发前需要了解的知识


在前面我们使用Verilog实现了一个简易的RISC-V处理器,并且能烧录到板子上跑一些简单C程序,传送门:

RISC-V处理器的设计与实现(一)—— 基本指令集_risc_v处理器_Patarw_Li的博客-CSDN博客

RISC-V处理器的设计与实现(二)—— CPU框架设计_Patarw_Li的博客-CSDN博客

RISC-V处理器的设计与实现(三)—— 上板验证_Patarw_Li的博客-CSDN博客

接下来我会开始编写一个riscv上的简易操作系统,然后放到我们做的riscv处理器上运行,参考的资料和视频链接如下:

[完结] 循序渐进,学习开发一个RISC-V上的操作系统 - 汪辰 - 2021春 

riscv-operating-system-mooc: 开放课程《循序渐进,学习开发一个 RISC-V 上的操作系统》配套教材代码仓库。mirror to https://github.com/plctlab/riscv-operating-system-mooc

本人自己做的 riscv-cpu 项目仓库(如果觉得对你有帮助请一定一定点个 star!): 

riscv-cpu: 一个基于RISC-V指令集的CPU实现(成功移植到野火征途PRO开发板)

一、开发环境配置

使用的开发环境如下:

$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 20.04.2 LTS
Release:	20.04
Codename:	focal

$ uname -r
5.15.0-76-generic

安装Ubuntu 20.04官方提供的 GNU工具链和 QEMU 模拟器:

sudo apt update
sudo apt install build-essential gcc make perl dkms git gcc-riscv64-unknown-elf gdb-multiarch qemu-system-misc

其中 gcc-riscv64-unknown-elf 就是我们的交叉编译工具,可以把程序编译成riscv上的可执行文件; gdb为debug工具;qemu是一个模拟器,可以模拟出riscv系统。

二、测试

在配置好开发环境后,我们可以用一个程序来测试一下,下面是前面文章用到的led程序,我们会在Ubuntu上把这个C程序利用交叉编译工具链编译成二进制.bin文件,然后通过串口将.bin文件烧录到处理器的memory中:

int main(){
    int n = 5;
    int sum = 0;
    for (int i = 1; i <= n; ++i) {
        sum = sum + i;
    }
 
    int* gpio_data = (int*) 0x30000004; // gpio_data寄存器的地址
    *gpio_data = sum; // 将gpio_data寄存器的内容改为sum值
    return 0;
}

然后我们需要编译我们的led.c程序: 

riscv64-unknown-elf-gcc -c -nostdlib -march=rv32i -mabi=ilp32 led.c -o main.o
  • -c 选项是编译、汇编到目标代码,但不进行链接。
  • -nostdlib 告诉编译器不要把标准库编译进去。
  • -march=rv32i -mabi=ilp32 用于指定指令集架构和 ABI,因为我们只实现了部分整数指令,所以使用rv32i。

编译后,我们可以得到 main.o,它是一个 ELF 文件。我们只需要 .text 部分的机器指令,所以用 objcopy 对它进行处理:

riscv64-unknown-elf-objcopy -O binary -j .text main.o main.bin
  • -O binary 选项用于输出纯二进制文件。
  • -j .text 是告诉它只保留 .text 部分。

经过处理,生成的二进制文件只含有机器指令。但此时的程序并不能直接运行。我们用 objdump 进行反编译,看看生成的汇编是怎么样的: 

riscv64-unknown-elf-objdump -D -b binary main.bin -mriscv

可以看到第一行直接将sp(堆栈指针寄存器,stack pointor)减去了32,此时sp寄存器为负值,肯定无法正常执行,所以我们还需要一个初始化文件init.s来帮助我们初始化sp寄存器,内容如下:

li sp, 0x10000000
addi sp, sp, 256

我们处理器的ram地址是从0x10000000开始, 利用li sp, 0x10000000指令将sp寄存器内容初始化为0x10000000,然后将栈的大小设置为256(因为这里栈是向下递减的)。

然后将init.s编译成init.o文件:

riscv64-unknown-elf-gcc -c -nostdlib -march=rv32i -mabi=ilp32 init.s -o init.o

然后我们再把init.o和main.o链接起来,生成final.o:

riscv64-unknown-elf-ld -melf32lriscv -o final.o init.o main.o

最后生成二进制文件final.bin:

riscv64-unknown-elf-objcopy -O binary -j .text final.o final.bin

用 objdump 进行反编译,看看生成的汇编是怎么样的: 

riscv64-unknown-elf-objdump -D -b binary final.bin -mriscv

可以看到前面已经变成初始化文件init.s里面的内容了,这样也就不用担心sp为负的问题了。

然后编写串口上位机发送Python程序(串口发送程序已经更新至gitee仓库:cpu_prj: 一个基于RISC-V指令集的CPU实现):

import serial
 
try:
    ser = serial.Serial("COM3", 19200, timeout=0.5)
    if ser.is_open:
        print("COM3" + " open success!")
        with open('./final.bin', 'rb') as f:
            a = f.read()
        print("sending bin file")
        count = ser.write(a)
        print("send over, the number of byte: ", count)
 
except Exception as e:
    print("---error---: ", e)

# 如果报错ModuleNotFoundError: No module named 'serial',则执行 pip install pyserial

将编译好的final.bin文件移动到和串口发送程序同目录下,记得修改串口设置为你自己的,我这里用的串口为COM3,波特率为19200。

将移植了riscv处理器的开发板与pc连接,按住key1不动后,然后运行python串口程序,程序发送完毕后松开key1,然后点击板子上的复位按键,即可看到四个led灯全亮:

知道如何烧录程序到处理器上的话,之后操作系统的开发和验证都会方便很多。

三、Makefile

既然生成二进制文件要这么多步骤,我们可以使用Makefile来帮助我们自动化构建,这样我们就不需要每次修改代码都要执行这么多指令,工程结构如下 :

main.c为用户编写的代码,init.s为上面的初始化文文件,Makefile脚本代码如下:

CROSS_COMPILE = riscv64-unknown-elf-
CFLAGS = -c -nostdlib -march=rv32i -mabi=ilp32

CC = ${CROSS_COMPILE}gcc
LD = ${CROSS_COMPILE}ld
OBJCOPY = ${CROSS_COMPILE}objcopy
OBJDUMP = ${CROSS_COMPILE}objdump

all: final.bin
final.bin: 
        ${CC} ${CFLAGS} -o init.o init.s
        ${CC} ${CFLAGS} -o main.o main.c
        ${LD} -melf32lriscv -o final.o init.o main.o    
        ${OBJCOPY} -O binary  -j .text final.o final.bin
code: all
        @${OBJDUMP} -D -b binary final.bin -mriscv | less

clean:
        rm -fr *.o *.bin

1. 生成二进制.bin文件,执行make即可:

make

之后将final.bin文件烧录到板子上即可。

2. 查看二进制文件的final.bin的汇编代码:

make code

3. 清除所有生成的文件:

make clean

如果对Makefile不熟悉的可以看这篇文章:Makefile教程(绝对经典,所有问题看这一篇足够了)_GUYUEZHICHENG的博客-CSDN博客 

四、开发前需要了解的知识

在进行操作系统的开发前,你应该要熟悉如下知识:

  1. 交叉编译
  2. 调试器GDB
  3. 模拟器QEMU
  4. 项目构造工具Make

如果遇到问题也欢迎加群 892873718 交流~ 

  • 5
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值