开发一个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 交流~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/10481.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器学习深度学习——多层感知机

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——感知机 &#x1f4da;订阅专栏&#xff1a;机器学习&&深度学习 希望文章对你们有所帮助 上一节…

Python代码实现题型

1.写出print()输出值 a = [1, 2, 3, 4, 5] # [1, 3, 5] print(a[::2]) # [4, 5] print(a[-2:]) 2.写出调用函数的输出结果 def fn(x, lst=[]):for i in range(x):lst.append(i * i)print(lst)# [0, 1] fn(2)# [3, 2, 1, 0, 1, 4] fn(3, [3,2,1])# [0, 1, 0, 1, 4] fn(3) 3.…

Git时间:版本控制工具进阶

Git时间&#xff1a;版本控制工具进阶 忽略文件 Git允许用户将指定的文件或目录排除在版本控制之外&#xff0c;它会检查代码仓库的目录下是否存在一个名为.gitignore的文件&#xff0c;如果存在&#xff0c;就去一行行读取这个文件中的内容&#xff0c;并把每一行指定的文件…

瀚高数据库中,对象不存在问题的处理思路及解决方案

**瀚高数据库 目录 环境 文档用途 详细信息 环境 系统平台&#xff1a;N/A 版本&#xff1a;6.0,4.5 文档用途 使用瀚高数据库可能会遇到应用系统中报对象等不存在的问题&#xff0c;可参照本文处理。 详细信息 1、常见报错信息展示&#xff1a; msgid “table “%s” does …

MySQL 读写分离

目录 一、什么是读写分离&#xff1f; 二、为什么要读写分离呢&#xff1f; 三、什么时候要读写分离&#xff1f; 四、主从复制与读写分离 五、MySQL 读写分离原理 六、企业 使用MySQL 读写分离场景 1&#xff09;基于程序代码内部实现 2&#xff09;基于中间代理层实现…

你说你会Java手动锁,但你会这道题吗???

按照这个格式输出你会吗&#xff1f;&#xff1f;&#xff1f; 你说你不会&#xff0c;接下来认真看认真学了。 1.首先引入原子类。AtomicInteger num new AtomicInteger(0); 什么是原子类&#xff1f; 就是可以保证线程安全的原子操作的数据类型。 有什么作用&#xff1f;…

在Debian 12 上安装 PHP 5.6, 7.4

环境&#xff1a;Debian 12 Debian 12 默认的PHP版本为 8.2 如果直接安装php7.4就出现下面的报错&#xff1a; sudo apt-get install libapache2-mod-php7.4 php7.4 php7.4-gd php7.4-opcache php7.4-mbstring php7.4-xml php7.4-json php7.4-zip php7.4-curl php7.4-imap p…

postgresql备份和恢复

实际工作中会对数据库进行备份和还原&#xff0c;备份主要有三种格式 .bak 即压缩的二进制 .sql 即明文存储 .tar 即tarball压缩格式 数据库备份分单数据库备份&#xff0c;使用 pg_dump 命令&#xff1b;所有数据库备份&#xff0c;使用 pg_dumpall 命令 pg_dump 常用选项…

Unity TextMeshPro 富文本-文本水平对齐

资料 文档 文本水平对齐 对齐方式&#xff1a;左对齐&#xff0c;右对齐&#xff0c;居中,Justified,Flush 使用&#xff1a;<alignleft>左对齐</align> &#xff0c;可赋值left,right,center,flush,justified 该标签会覆盖默认的对齐方式。标签范围内的文本受影…

Java简化MongoDB编解码器的两种方法

介绍&#xff1a; 在与MongoDB进行数据交互时&#xff0c;有时候会遇到找不到类的编解码器&#xff08;codec&#xff09;的错误。为了解决这个问题&#xff0c;一种常见的方法是创建自定义编解码器来处理特定的类。然而&#xff0c;对于一些开发者来说&#xff0c;这样的方法…

导出为PDF加封面且分页处理dom元素分割

文章目录 正常展示页面导出后效果代码 正常展示页面 导出后效果 代码 组件内 <template><div><div><div class"content" id"content" style"padding: 0px 20px"><div class"item"><divstyle"…

Ubuntu Server版 之 mysql 系列

Ubuntu 分 桌面版 和 服务版 桌面版 &#xff1a;有额外的简易界面 服务版&#xff1a;是纯黑框的。没有任何UI界面的可言 安装mysql 安装位置 一般按照的位置存放在 /usr/bin 中 sudo apt-get install mysql-server查看mysql的状态 service mysql status mysql 安全设置…

使用xtcp映射穿透指定服务

使用xtcp映射穿透指定服务 管理员Ubuntu配置公网服务端frps配置service自启(可选) 配置内网服务端frpc配置service自启(可选) 使用者配置service自启(可选) 通过frp实现内网client访问另外一个内网服务器 管理员 1&#xff09;配置公网服务端frps2&#xff09;配置内网服务端…

FS32K144官方提供串口Bootloader对接Matlab串口烧写程序

​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ 前言 Bootloader升级工具&#xff1a;可用TTL、232、485&#xff08;硬件收发模式&#xff09;,其中的一种&#x…

CertGetCertificateChain trust error CERT_TRUST_REVOCATION_STATUS_UNKNOWN

执行命令&#xff1a; curl --cacert http_ca.crt -u elastic https://localhost:9200 结果报错了 直接访问https://localhost:9200/ &#xff0c;正常 解决办法&#xff1a; curl --cacert http_ca.crt -u elastic https://localhost:9200 --insecure

2_Apollo4BlueLite中断控制器NVIC

1.概述 Apollo4BlueLite 的中断控制器是采用 ARM Cortex-M4 内核&#xff0c;并集成了 NVIC&#xff08;Nested Vectored Interrupt Controller&#xff0c;嵌套向量中断控制器&#xff09;作为其中断控制器。 NVIC 是 ARM Cortex-M 系列处理器中常用的中断控制器&#xff0c…

「前缀和以及差分数组」

文章目录 1 前缀和数组1.1 题解1.2 Code1.3 结果 2 二维矩阵的前缀和数组2.1 题解2.2 Code2.3 结果 3 差分数组 1 前缀和数组 适用于快速频繁的计算一个索引区间内的元素之和&#xff0c;核心思想就是使用一个前缀和数组&#xff0c;然后使用前缀和数组的两个元素之差&#xf…

论文笔记--FEDERATED LEARNING: STRATEGIES FOR IMPROVING COMMUNICATION EFFICIENCY

论文笔记--FEDERATED LEARNING: STRATEGIES FOR IMPROVING COMMUNICATION EFFICIENCY 1. 文章简介2. 文章概括3 文章重点技术3.1 联邦学习(federated learning, FL)3.2 Structured updates3.3 Sketched Update 4. 文章亮点5. 原文传送门 1. 文章简介 标题&#xff1a;FEDERATE…

React之内置的高阶组件

React之内置的高阶组件 React内置了一些高阶组件&#xff0c;以便对一些组件做特殊处理&#xff0c;从而提高代码性能。例如React.memo、React.forwardRef 注意&#xff1a;高阶组件不是Reacts视图组件 什么是高阶组件 高阶组件本质是高阶函数。高阶组件接收一个组件作为参数&…

QListWidget设置QWidget作为QListWidgetItem

1、实现QListWidget按照N像素进行滑动&#xff1b; 2、实现自定义QWidget作为QListWidgetItem&#xff1b; 代码实现&#xff1a; from PyQt5.QtWidgets import QWidget, QLabel, QVBoxLayout, QHBoxLayout, QTextEdit, QListWidget, QListWidgetItem, QScrollArea from PyQt5…