开发一个RISC-V上的操作系统(二)—— 系统引导程序(Bootloader)
目录
文章传送门
一、什么是Bootloader
二、简单的启动程序
三、上板测试
文章传送门
开发一个RISC-V上的操作系统(一)—— 环境搭建_riscv开发环境_Patarw_Li的博客-CSDN博客
开发一个RISC-V上的操作系统(二)—— 系统引导程序(Bootloader)_Patarw_Li的博客-CSDN博客
开发一个RISC-V上的操作系统(三)—— 串口驱动程序(UART)_Patarw_Li的博客-CSDN博客
一、什么是Bootloader
Bootloader是cpu在上电后执行的第一段代码,用于初始化各类资源,并且跳转到主程序上执行,比如初始化sp寄存器,将rom中的数据搬运到ram上,清零bss段等等。
百度百科的词条中,这样解释Bootloader:“Bootloader是嵌入式系统在加电后执行的第一段代码,在它完成CPU和相关硬件的初始化之后,再将操作系统映像或固化的嵌入式应用程序装载到内存中然后跳转到操作系统所在的空间,启动操作系统运行”。
一般系统引导程序都是固化在flash中(因为ram断电即失),上电后先执行引导程序再跳转到主程序上执行:

引导程序大多都是使用汇编语言编写(毕竟涉及到一些寄存器操作),下面我会写一个简单的启动程序来帮助我们初始化栈指针sp、并且跳转到主程序执行。
二、简单的启动程序
可以先去我的gitee仓库下载代码,本节代码在 00_START 目录下下:
riscv_os: 一个RISC-V上的简易操作系统
代码结构如下:

inc目录下存放头文件;kernel.c为主程序,引导程序最终会跳转到这里执行;start.S为引导程序;Makefile为自动化构建脚本。
先来看看start.S里的内容:
#include "inc/platform.h"# size of stack is 256 bytes.equ STACK_SIZE, 256.global _start.text
_start:la sp, RAM + STACK_SIZE # set the initial stack pointer to 0x00001100 (0x00001000 + 256)j start_kernel # jump to kernel.end # end of file
- .equ类似于C语言里面的宏,将STACK_SIZE设置成256。
- .global关键字用来让一个符号对链接器可见,可以供其他链接对象模块使用;告诉编译器后续跟的是一个全局可见的名字(变量/函数名)。
- .text指定后续内容为代码段。
- _start是一个符号,是汇编程序默认入口标号。也是编译、链接后程序的起始地址。 由于程序是通过加载器来加载的,必然要找到 _start名字的函数,因此 _start必须定义成全局的,以便存在于编译后的全局符号表中,供其他程序(如加载器)寻找到。
- la sp, RAM + STACK_SIZE 将栈指针寄存器sp的值初始化为RAM + STACK_SIZE(0x00001000 + 256)。
- j start_kernel 跳转到start_kernal 主程序中执行。
为什么用大写的.S后缀而不用小写的.s呢?因为使用GCC(准确说是GCC调用了as汇编器)处理汇编代码时,汇编文件的后缀有两种:.s与.S。这两种文件都是汇编代码,其区别在于:
.s格式的汇编文件中,只能包含纯粹的汇编代码,汇编器只对其进行汇编操作,没有预处理操作;.S格式的汇编文件中,还可以使用预处理命令,汇编器会先进行预处理,然后再进行汇编。
而我们的启动代码包含了头文件,所以就需要用大写的.S结尾的汇编文件了。
然后是Makefile里面的内容:
CROSS_COMPILE = riscv64-unknown-elf-
CFLAGS = -nostdlib -fno-builtin -march=rv32im -mabi=ilp32 -g -WallCC = ${CROSS_COMPILE}gcc
OBJCOPY = ${CROSS_COMPILE}objcopy
OBJDUMP = ${CROSS_COMPILE}objdumpSRCS_ASM = \start.S \SRCS_C = \kernel.c \OBJS = $(SRCS_ASM:.S=.o)
OBJS += $(SRCS_C:.c=.o).DEFAULT_GOAL := all
all: os.elf# start.o must be the first in dependency!
os.elf: ${OBJS}${CC} ${CFLAGS} -o os.elf $^${OBJCOPY} -O binary os.elf os.bin%.o : %.c${CC} ${CFLAGS} -c -o $@ $<%.o : %.S${CC} ${CFLAGS} -c -o $@ $<.PHONY : code
code: all@${OBJDUMP} -S os.elf | lessclean:rm -fr *.o *.bin *.elf
该脚本的工作是先把start.S和kernel.c编译成start.o和kernel.o目标文件,然后再将start.o和kernel.o目标文件链接成os.elf文件,最后再通过objcopy将os.elf文件变成二进制os.bin文件,os.bin文件就是最后我们要放到板子上跑的程序。
可能有人会问为什么不直接把elf文件放到处理器上去运行,下面对elf格式的文件做一些简单的介绍:

下面是elf文件的格式,可以看到除了中间一部分正文段和数据段以外,还有一些其他的段,比如ELF Header,里面描述了体系结构和操作系统等基本信息,并指出Section Header Table和Program Header Table在文件中的什么位置;Program Header Table在汇编和链接过程中没有用到,所以是可有可无的,Section Header Table中保存了所有Section的描述信息。

但是cpu并不能识别这些信息,只有一些特定的操作系统才能识别这些信息,所以这些信息对处理器来说是没有用的,而objcopy指令正是帮我们去掉这些处理器无法识别的内容,留下的内容即为处理器可以识别的内容。
Makefile脚本的用法:
1. 生成二进制.bin文件,执行make即可:
make

生成的os.bin即为我们要烧录到板子上运行的程序。
2. 查看二进制文件的os.elf的汇编代码:
make code

使用这个指令可以查看每条C语句对应的汇编代码以及每条指令的地址。
3. 清除所有生成的文件:
make clean
最后是kernel.c里面的内容,这里面即可存放我们要运行的内容,还是以我们的流水灯程序为例子:
void start_kernel(void){uint8_t *gpio_data = (uint8_t *)0x20000004;while(1){// 第一个灯亮起*gpio_data = 1;for(int i = 0; i < 1000000; i++); // delay// 第二个灯亮起*gpio_data = 2;for(int i = 0; i < 1000000; i++); // delay// 第三个灯亮起*gpio_data = 4;for(int i = 0; i < 1000000; i++); // delay// 第四个灯亮起*gpio_data = 8;for(int i = 0; i < 1000000; i++); // delay}while(1){}; // stop here!
}
这样引导程序和主程序都准备完毕了,我们接下来就可以上板实验了。
三、上板测试
要进行上板测试,首先得按照我前面的文章烧录riscv处理器程序到板子上:
RISC-V处理器的设计与实现(三)—— 上板验证(基于野火征途Pro开发板)_Patarw_Li的博客-CSDN博客
项目仓库地址:cpu_prj: 一个基于RISC-V指令集的CPU实现
然后执行make生成os.bin文件后,通过python串口发送程序(serial_utils目录下)将os.bin文件烧录到处理器的memory上(按住key1不动,烧录完后松开),烧录后即可看到流水灯现象。
遇到问题欢迎加群 892873718 交流~
相关文章:
开发一个RISC-V上的操作系统(二)—— 系统引导程序(Bootloader)
目录 文章传送门 一、什么是Bootloader 二、简单的启动程序 三、上板测试 文章传送门 开发一个RISC-V上的操作系统(一)—— 环境搭建_riscv开发环境_Patarw_Li的博客-CSDN博客 开发一个RISC-V上的操作系统(二)—— 系统引导…...
Git安装与学习
Git学习网站 Git安装教程 镜像网站 https://registry.npmmirror.com/binary.html 镜像下载是网站对服务器的一个保护措施之一,就是A站点下载的数据同 B站点下载的数据完全一样,b站点就是A站点的一面镜子。 所以镜像下载下来和原站点一摸一样。...
【Docker】docker中容器之间通信方式
文章目录 1. Docker容器之间通信的主要方式1.1 通过容器ip访问1.2. 通过宿主机的ip:port访问1.3. 通过link建立连接(官方不推荐使用)1.4. 通过 User-defined networks(推荐) 2. 参考资料 1. Docker容器之间通信的主要方式 1.1 通…...
算法-归并排序-JAVA
下面是Java实现归并排序的示例代码: public class MergeSort {public void mergeSort(int[] arr) {if (arr null || arr.length < 1) {return;}int[] temp new int[arr.length];mergeSort(arr, temp, 0, arr.length - 1);}private void mergeSort(int[] arr, …...
Flask 进阶
Flask 如何访问项目以外的文件 在工作中, 要在项目里展示一些额外的文件, 包括但不限于静态的html。图片, log, 其他的都还好说, 但是当html的时候我就开始犯难了, 因为数量过多 我并不想把它塞进我项目的t…...
home-assistant整合sso
其他软件都可以通过nginx直接做代理添加鉴权,但是这个hass果然是用户安全隐私很强,做代理需要配置白名单,而且支持的三方鉴权都不太适合我的需求,非要改源码才行,后来我发现不用改源码的折中方式 参考文章 External …...
Ip-Limit: 轻量级注解式IP限流组件(二)
author: van , ggfanwentaogmail.comIp-Limit-Example: 轻量级注解式IP限流组件使用样例 项目简介 该项目为ip-limiter的使用示例项目。 ip-limiter地址: https://github.com/DDAaTao/ip-limiter 示例项目文件树 └─example├─handler│ └─BaseException…...
【C++】开源:Redis数据库配置与使用
😏★,:.☆( ̄▽ ̄)/$:.★ 😏 这篇文章主要介绍Redis数据库配置与使用。 无专精则不能成,无涉猎则不能通。。——梁启超 欢迎来到我的博客,一起学习,共同进步。 喜欢的朋友可以关注一下,…...
TCP/IP网络编程 第二十四章:制作HTTP服务器端
实现简单的Web服务器端 现在开始在HTTP协议的基础上编写Web服务器端。先给出Windows平台下的示例,再给出Linux下的示例。在这里我假设各位都有了有关HTTP的知识,如果不知道HTTP协议的具体内容可以参考的往期博客,有了这些基础就不难分析源代…...
React 前端应用中快速实践 OpenTelemetry 云原生可观测性(SigNoz/K8S)
OpenTelemetry 可用于跟踪 React 应用程序的性能问题和错误。您可以跟踪从前端 web 应用程序到下游服务的用户请求。OpenTelemetry 是云原生计算基金会(CNCF)下的一个开源项目,旨在标准化遥测数据的生成和收集。已成为下一代可观测平台的事实标准。 React(也称为 Re…...
Linux 多线程并发Socket服务端的实现( 11 ) -【Linux通信架构系列 】
系列文章目录 C技能系列 Linux通信架构系列 C高性能优化编程系列 深入理解软件架构设计系列 高级C并发线程编程 设计模式系列 期待你的关注哦!!! 现在的一切都是为将来的梦想编织翅膀,让梦想在现实中展翅高飞。 Now everythi…...
2.7. Java 泛型了解么?什么是类型擦除?介绍一下常用的通配符?
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。 Java 的泛型是伪泛型&am…...
单例模式与构造器模式
单例模式 1、是什么 单例模式(Singleton Pattern):创建型模式,提供了一种创建对象的最佳方式,这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建 在应用程序运…...
SQL力扣练习(七)
1.行程和用户(262) 表:Trips ----------------------- | Column Name | Type | ----------------------- | id | int | | client_id | int | | driver_id | int | | city_id | int | | status | enum | | reques…...
C语言假期作业 DAY 05
题目 一、选择题 1、如下程序的功能是( ) #include <stdio.h> int main() { char ch[80] "123abcdEFG*&"; int j; puts(ch); for(j 0; ch[j] ! \0; j) if(ch[j] > A && ch[j] < Z) ch[j] ch[j] e - E; puts(ch)…...
php-golang-rpc使用roadrunner-server/goridge/v3/pkg/rpc和php的spiral/goridge3.2实践
golang代码: go get github.com/roadrunner-server/goridge/v3 package main import ( "fmt" "net" "net/rpc" goridgeRpc "github.com/roadrunner-server/goridge/v3/pkg/rpc" ) type App struct{} func (s *App) Hi(na…...
API常用签名验证方法(PHP实现)
使用场景 现在越来越多的项目使用的前后端分离的模式进行开发,后端开发人员使用API接口传递数据给到前端开发进行处理展示,在一些比较重要的修改数据接口,涉及金钱,用户信息等修改的接口如果不做防护验证,经常容易被人…...
kotlin高阶函数
kotlin高阶函数 函数式API:一个函数的入参数为Lambda表达式的函数就是函数式api 例子: public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {return filterTo(ArrayList<T>(), predicate) }上面这段函数: 首先这个函…...
kotlin list集合树
kotlin list集合树 记录一下 data class AreaSchemaManageDto(var id: Long? null,var pid: Long? null,var label: String? null,var children: MutableList<AreaSchemaManageDto>? null ) : Serializable { }逻辑 fun getAll(): List<AreaSchemaManageDto&g…...
基于Autoencoder自编码的64QAM星座图整形调制解调通信系统性能matlab仿真
目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1星座图整形 4.2自编码器 4.3基于Autoencoder的星座图整形调制解调模型 4.4 实现过程 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 .…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...
【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
基于SpringBoot在线拍卖系统的设计和实现
摘 要 随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统,主要的模块包括管理员;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...
接口 RESTful 中的超媒体:REST 架构的灵魂驱动
在 RESTful 架构中,** 超媒体(Hypermedia)** 是一个核心概念,它体现了 REST 的 “表述性状态转移(Representational State Transfer)” 的本质,也是区分 “真 RESTful API” 与 “伪 RESTful AP…...
Spring Boot 与 Kafka 的深度集成实践(二)
3. 生产者实现 3.1 生产者配置 在 Spring Boot 项目中,配置 Kafka 生产者主要是配置生产者工厂(ProducerFactory)和 KafkaTemplate 。生产者工厂负责创建 Kafka 生产者实例,而 KafkaTemplate 则是用于发送消息的核心组件&#x…...
河北对口计算机高考MySQL笔记(完结版)(2026高考)持续更新~~~~
MySQL 基础概念 数据(Data):文本,数字,图片,视频,音频等多种表现形式,能够被计算机存储和处理。 **数据库(Data Base—简称DB):**存储数据的仓库…...
在MobaXterm 打开图形工具firefox
目录 1.安装 X 服务器软件 2.服务器端配置 3.客户端配置 4.安装并打开 Firefox 1.安装 X 服务器软件 Centos系统 # CentOS/RHEL 7 及之前(YUM) sudo yum install xorg-x11-server-Xorg xorg-x11-xinit xorg-x11-utils mesa-libEGL mesa-libGL mesa-…...
