开发一个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.部分核心程序 .…...

【Spring】Spring 总览
一、简单介绍一下 Spring Spring是一个全面的、企业应用开发的一站式解决方案,贯穿表现层、业务层、持久层,可以轻松和其他框架整合,具有轻量级、控制反转、面向切面、容器等特征。 轻量级 : 空间开销和时间开销都很轻量 控制反…...

微软、OpenAI用上“数据永动机” 合成数据是晨曦还是暮光?
微软、OpenAI、Cohere等公司已经开始测试使用合成数据来训练AI模型。Cohere首席执行官Aiden Gomez表示,合成数据可以适用于很多训练场景,只是目前尚未全面推广。 已有的(通用)数据资源似乎接近效能极限,开发人员认为&a…...

简单认识Redis 数据库的高可用
文章目录 一、Redis 高可用:1.简介:2、在Redis中实现高可用的技术 二、Redis持久化:1.持久化的功能:2.Redis 提供两种方式进行持久化: 三、RDB 持久化:1.简介:2.触发条件:4.启动时加…...

超级实用!,掌握这9个鲜为人知的CSS属性
微信搜索 【大迁世界】, 我会第一时间和你分享前端行业趋势,学习途径等等。 本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试完整考点、资料以及我的系列文章。 快来免费体验ChatGpt plus版本的,我们出的钱 体验地…...

深圳国际新能源及智能网联汽车全产业博览会今年10月举办
7月25日,深圳市工业和信息化局与励展博览集团共同在深圳举办Automotive World China 2023深圳国际新能源及智能网联汽车全产业博览会(简称“AWC 2023”)全球推介启动大会,该博览会将于2023年10月11日-13日在深圳国际会展中心盛大举…...

【具有非线性反馈的LTI系统识别】针对反馈非线性的LTI系统,提供非线性辨识方案(SimulinkMatlab代码实现)
目录 💥1 概述 📚2 运行结果 🎉3 参考文献 🌈4 Matlab代码、Simulink仿真实现 💥1 概述 本文为具有反馈非线性的LTI系统提供了一种非线性识别方案,这取决于输入和LTI系统输出。对于MEMS来说尤其如此&#…...

Stable diffusion 和 Midjourney 怎么选?
通过这段时间的摸索,我将和你探讨,对普通人来说,Stable diffusion 和 Midjourney 怎么选?最重要的是,学好影视后期制作对 AI 绘画创作有哪些帮助?反过来,AI 绘画对影视后期又有哪些帮助…...

c++网络编程
网络编程模型 c/s 模型:客户端服务器模型b/s 模型:浏览器服务器模型1.tcp网络流程 服务器流程: 1.创建套接字2.完善服务器网络信息结构体3.绑定服务器网络信息结构体4.让服务器处于监听状态5.accept阻塞等待客户端连接信号6.收发数据7.关闭套…...

【沁恒蓝牙mesh】数据收发接口与应用层模型传递
本文主要描述了沁恒蓝牙mesh SDK的蓝牙数据收发接口,以及应用层的回调函数解析以及模型传递 这里写目录标题 1. 数据收发接口1.1【发送数据】1.2 【数据接收】 2. 应用层模型分析 1. 数据收发接口 1.1【发送数据】 /*(1)接口1 */ /*接口一&…...

Java类关系之代理(代理模式)
在Java中,如果一个类需要使用另一个类的方法,我们可以使用继承的方式实现,那么问题来了,如果这个类恰恰在逻辑关系上不能使用继承怎么办呢?比如说,飞机和控制台这两个类,控制台的方法有上下左右…...