关于正点原子的alpha开发板的启动函数(汇编,自己的认识)

news2024/9/23 3:27:24

我傻逼了,这里的注释还是不要用;

全部换成

/* */

这里就分为两块,一部分是复位中断部分,第二部分就是IRQ部分(中断部分最重要)

我就围绕着两部分来展开我的认识

首先声明全局 .global_start

在 ARM 架构的程序中,_start 常常是 C 程序的 main 函数之前的汇编代码,它负责设置程序的初始状态,比如初始化堆栈指针、设置 BSS 段、调用 C/C++ 运行时初始化代码等,然后将控制权传递给 main 函数

不知道为什么视频里的清楚和设置bss段,后面的代码没有了

第一步:设置中断向量表

这里设置中断向量表的顺序是固定的,依照架构技术手册设置。

发生了哪个中断就把其地址加载给pc指针

;中断向量表
	ldr pc,=Reset_Handler
	ldr pc,=Undefined_Handler
	ldr pc,=SVC_Handler
	ldr pc,=PrefAbort_Handler
	ldr pc,=DataAbort_Handler
	ldr pc,=NotUsed_Handler
	ldr pc,=IRQ_Handler
	ldr pc,=FIQ_Handler

一共有8种中断类型,我们最关心的两种一个是系统复位中断,第二种是外设中断IRQ中断,其他的目前是不太关心。所以其实现也是一个死循环,如下

SVC_Handler:
ldr r0,=SVC_Handler
bx r0

第二步:实现复位中断函数和IRQ中断函数

关闭C1寄存器里的几个功能,主要是操作SCTLR寄存器

	mrc p15,0,r0,c1,c0,0 /* 读取CP15的C1寄存器到R0中   */
	bic r0,r0,#(1<<12)/*清除C1寄存器的bit12位(I位),关闭I Cache */
	bic r0,r0,#(1<<11) /*清除C1寄存器的bit11(Z位),关闭分支预测 */
	bic r0,r0,#(1<<2)/*清除C1寄存器的bit2(C位),关闭D Cache */
	bic r0,r0,#(1<<1)/*清除C1寄存器的bit1(A位),关闭对齐 */
	bic r0,r0,#(1<<0)/*清除C1寄存器的bit0(M位),关闭MMU */
	mcr p15,0,r0,c1,c0,0 /* 将r0寄存器中的值写入到CP15的C1寄存器中*/

配置完该寄存器后就开始设置中断向量表基地址,这几个中断的偏移是在基地址为0x0的基础上的,但是我们存放其位置是在0x87800000

		/* 中断向量表偏移*/
	ldr r0,=0x87800000
	dsb
	isb
	mcr p15,0,r0,c12,c0,0
	dsb
	isb

其中dsb和isb是为了保障操作完成。

mcr p15,0,r0,c12,c0,0

该段代码意思是从CP15协处理器c12存入这个偏移地址

而c12里的VBAR是一个用来存放中断向量偏移基地址的寄存器

初始化这八种中断情况下的pc堆栈指针了。

当然可以全部实现,也可以只实现我们需要的

/* 设置各个模式下的堆栈指针*/

	/* IRQ模式*/
	mrs r0,spsr;
	bic r0,r0,#(0x1f)/*将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */
	orr r0,r0,#(0x12)/*r0或上0x12,表示使用IRQ模式 */
	msr cpsr,r0 /*将r0 的数据写入到cpsr_c中 */
	ldr sp,=0x80600000/* 设置IRQ模式下的栈首地址为0X80600000,大小为2MB*/
	/* SYS模式*/
	mrs r0,spsr;
	bic r0,r0,#(0x1f) /*将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */
	orr r0,r0,#(0x1f)/* r0或上0x1f,表示使用SYS模式*/
	msr cpsr,r0/*将r0 的数据写入到cpsr_c中 */
	ldr sp,=0x80400000/* 设置IRQ模式下的栈首地址为0X80400000,大小为2MB*/
	/* IRQ模式*/
	mrs r0,spsr;
	bic r0,r0,#(0x1f)/*将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */
	orr r0,r0,#(0x13)/* r0或上0x13,表示使用SVC模式*/
	msr cpsr,r0/*将r0 的数据写入到cpsr_c中 */
	ldr sp,=0x80200000/*设置SVC模式下的栈首地址为0X80200000,大小为2MB */

在 ARM 架构中,状态寄存器 `SPSR`(Saved Program Status Register)用于保存当前的程序状态信息,包括处理器模式。

以下是切换处理器模式的几种常见方法:

1. **使用 `CPS` 指令**:ARM 架构提供了 `CPS`(Change Processor State)指令,用于切换处理器模式。例如,`CPS #0x13` 将切换到 SVC(Supervisor)模式,其中 `0x13` 是模式值加上一个优先级级别。

2. **修改 `CPSR` 寄存器**:直接修改 `CPSR` 寄存器可以改变处理器模式,但这通常不是安全的编程实践,因为它可能影响当前的程序状态。

3. **使用 `MSR` 指令**:`MSR`(Move to Status Register)指令可以用来修改 `CPSR` 或 `SPSR` 的某些字段。例如,`MSR spsr_cxsf, r0` 可以将寄存器 `r0` 的值写入 `SPSR` 的控制和状态字段。

4. **异常和中断处理**:当异常或中断发生时,处理器会自动切换到相应的模式,并保存当前的 `CPSR` 到相应的 `SPSR`。处理完异常或中断后,处理器会从 `SPSR` 恢复 `CPSR` 的值,从而切换回原来的模式。

5. **使用 `BX` 指令**:在某些情况下,使用 `BX` 指令跳转到一个具有不同模式的代码位置也可以实现模式切换。被跳转的目标代码需要设置正确的模式。

`SPSR` 主要用于异常和中断处理中保存和恢复程序状态,而不是直接用来切换模式。在编写程序时,应该使用 `CPS` 指令或 `MSR` 指令来安全地切换处理器模式,并确保程序状态的正确性。直接修改 `SPSR` 可能会导致不可预测的行为,因为 `SPSR` 也包含了其他状态信息,如中断屏蔽位等。

SPSR寄存器用来改变状态的就是低5位,当然我们也可以直接用cps 加上那5位切换到对应模式

cps #0x13/*切换到SVC模式*/
ldr sp,=0x80600000/*设置堆栈指针*/

这样也许更简洁。

设置完堆栈指针就该跳转到 main函数了,但是前面的设置过程为保证安全会关闭全局中断,设置完后再打开,类似于freertos里的进入临界区

2.1 具体的复位函数实现

Reset_Handler:

	cpsid i				/* 关闭全局中断*/
	mrc p15,0,r0,c1,c0,0 /* 读取CP15的C1寄存器到R0中   */
	bic r0,r0,#(1<<12)/*清除C1寄存器的bit12位(I位),关闭I Cache */
	bic r0,r0,#(1<<11) /*清除C1寄存器的bit11(Z位),关闭分支预测 */
	bic r0,r0,#(1<<2)/*清除C1寄存器的bit2(C位),关闭D Cache */
	bic r0,r0,#(1<<1)/*清除C1寄存器的bit1(A位),关闭对齐 */
	bic r0,r0,#(1<<0)/*清除C1寄存器的bit0(M位),关闭MMU */
	mcr p15,0,r0,c1,c0,0 /* 将r0寄存器中的值写入到CP15的C1寄存器中*/

	/* 设置各个模式下的堆栈指针*/

	/* IRQ模式*/
	mrs r0,spsr;
	bic r0,r0,#(0x1f)/*将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */
	orr r0,r0,#(0x12)/*r0或上0x12,表示使用IRQ模式 */
	msr cpsr,r0 /*将r0 的数据写入到cpsr_c中 */
	ldr sp,=0x80600000/* 设置IRQ模式下的栈首地址为0X80600000,大小为2MB*/
	/* SYS模式*/
	mrs r0,spsr;
	bic r0,r0,#(0x1f) /*将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */
	orr r0,r0,#(0x1f)/* r0或上0x1f,表示使用SYS模式*/
	msr cpsr,r0/*将r0 的数据写入到cpsr_c中 */
	ldr sp,=0x80400000/* 设置IRQ模式下的栈首地址为0X80400000,大小为2MB*/
	/* IRQ模式*/
	mrs r0,spsr;
	bic r0,r0,#(0x1f)/*将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */
	orr r0,r0,#(0x13)/* r0或上0x13,表示使用SVC模式*/
	msr cpsr,r0/*将r0 的数据写入到cpsr_c中 */
	ldr sp,=0x80200000/*设置SVC模式下的栈首地址为0X80200000,大小为2MB */

	cpsie i	/*打开全局中断 */
	b main/*跳转到main函数 */

然后就是最重要的IRQ中断函数的编写

2.2 具体IRQ函数的实现

首先保存lr寄存器的值,也就是保存现场,也就是入栈操作,把寄存器中的值保存进内存中去。

入栈 r0-r3,r12,因为这几个寄存器不会自动保存,要手动保存

然后就使spsr状态寄存器,它也不会自动保存

好了这几个寄存器全部压入栈内了,等于中断前的现场已经保存完整

进入协处理器CP15的C15经过操作数4的选择后最终我们选择读取CBAR寄存器的值

这个寄存器全名叫做配置基地址寄存器

我们为什么要得到该寄存器的值呢,因为我们要进一步得到GIC控制器的基地址。GIC的基地址存在CBAR里,这点是最重要的。没有CBAR的读取就找不到GIC的基地址

通过找到GIC的基地址,再经过偏移0x2000我们就得到GIC-CPU接口的基地址再偏移0xc我们得到了GIC-IAR寄存器的地址。这个GIC-IAR寄存器就是我们干这麽久最关键的地方。如果是知道这个偏移,那我们这样就更便捷,但是可读性就差了一些。

add r1,r1,#(0x200C);

 找到IAR寄存器后读取寄存器里的值我们就得到了CPUID和中断号,他们被存入r0寄存器

 得到中断号后我们的目的就得到了,就能找到对应的中断函数地址了。入栈r0,r1,把中断号和GIC-CPU接口的地址保存下来

切换到SVC模式,保存当前模式下的lr寄存器

r0,r1都用了,根据system_irqhandler得到入口地址存到r2寄存器内

blx跳到对应函数。

出栈SVC模式下的lr寄存器,切换到IRQ模式。出栈r0,r1。在不同的处理模式下出入栈lr都是成对的,它们拥有不完全公用的堆栈空间。这里的出栈入栈都是针对内存而言的,出栈就是内存到寄存器,入栈就使寄存器到内存。此刻r0存的是中断号,r1存的是GIC-CPU的基地址。把中断号存入GIC-CPU的基地址偏移0x10处的寄存器内表示中断执行完成,写EOIR 出栈r0恢复状态寄存器也就是

当前堆栈里还存有r0,r0-r3,r12的值,其中第一个r0存的是spsr寄存器的值

pop {r0}
msr spsr, r0

 这个与那个应该是等价的,都是接受栈内原本存入的spsr寄存器的值

然后恢复现场,还IRQ中断发生前的r0-r3,r12寄存器的内容

弹出lr-4的值给pc

这里我开始困惑的地方就是为什么没有-8,后来我才知道运行那段会被强制执行完,所以-4就可以了。

IRQ_Handler:
	push {lr}	/*保存当前运行地址 */
	push {r0-r3, r12}  /* 其他寄存器会自动保存,这几个要手动*/
	
	mrs r0 ,spsr  /*读取状态寄存器 */
	push {r0}  /*保存 */

	mrc p15,4,r1,c15,c0,0 /*读取CP15的CBAR寄存器 */
	add r1,r1,#(0x2000) /*基地址偏移0x2000得到GIC-CPU接口的基地址 */
	ldr r0,[r1,#(0xc)] /* 将GIC-CPU基地址再偏移0xc得到GICC-IAR寄存器的基地址,取该地址的值*/
	/*得到中断号及CPUID */
	push {r0,r1}
	cps #(0x13) /* 切换到SVC模式*/
	push {lr} /*保存svc模式下的lr寄存器 */
	ldr r2,=system_irqhandler  /* 加载C语言的IRQ中断处理函数*/
	blx r2 /*跳转到对应的IRQ中断函数 */

	pop {lr}
	cps #0x12 /* 进入IRQ模式*/
	pop {r0,r1}
	str r0,[r1,#(0x10)] /*将其中断ID号写入r0保存的地址中,也就是IAR基地址 */
	pop {r0}
	msr spsr_cxsf, r0 /*恢复状态寄存器 */

	pop {r0-r3,r12}
	pop {lr}
	subs pc,lr,#4
	

经过我的测试,把中断向量基地址的偏移放在 _start:下也是没有问题的。
 

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

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

相关文章

基于SpringBoot+VueJS+微信小程序技术的图书森林共享小程序设计与实现:7000字论文+源代码参考

博主介绍&#xff1a;硕士研究生&#xff0c;专注于信息化技术领域开发与管理&#xff0c;会使用java、标准c/c等开发语言&#xff0c;以及毕业项目实战✌ 从事基于java BS架构、CS架构、c/c 编程工作近16年&#xff0c;拥有近12年的管理工作经验&#xff0c;拥有较丰富的技术架…

4.4 文件管理

大纲 文件结构 真题 树形文件 真题 空间存储 一般考位示图法 真题

vue3使用Echarts图表生成项目进度甘特图

先看效果 代码展示 <template><h1>项目进度甘特图</h1><div id"app"><!-- Echarts 图表 --><div ref"progressChart" class"progressChart"></div></div> </template><script setup&…

The Web3 社区 Web3 产品经理课程

概述 / 深耕区块链行业 11 年&#xff0c;和很多产品经理都打过交道&#xff1b;遇到过优秀的产品经理&#xff0c;也遇到过比较拉垮的产品经理。多年工作中&#xff0c;曾在某些团队&#xff0c;承载技术兼产品经理的角色&#xff1b;也参与过很多 Web3 外包项目&#xff0c;包…

CSS技巧专栏:一日一例 5-纯CSS实现背景色从四周向中心填充的按钮特效

特此说明 本专题专注于讲解如何使用CSS制作按钮特效。前置的准备工作和按钮的基本样式,都在本专栏第一篇文章中又详细说明。自本专栏第四篇文章起,本专栏都将直接跳过前面的内容,不再重复复制,需要了解按钮基础样式的同学,请移步:《CSS技巧 - 一日一例 (1):会讨好的热…

物联网可编程中央控制主机

物联网可编程中央控制主机&#xff08;Programmable Central Control Host for IoT&#xff0c;如GF-MAXCC&#xff09;在多个领域都有广泛的应用。这些应用领域包括但不限于&#xff1a; 1. 智能家居 GEFFEN在智能家居系统中&#xff0c;物联网可编程中央控制主机充当着家庭…

CT金属伪影去除的去噪扩散概率模型| 文献速递-基于深度学习的多模态数据分析与生存分析

Title 题目 A denoising diffusion probabilistic model for metal artifact reduction in CT CT金属伪影去除的去噪扩散概率模型 01 文献速递介绍 CT图像中的金属伪影是在CT扫描视野内存在金属物体&#xff08;如牙科填充物、骨科假体、支架、手术器械等&#xff09;时出…

DP(3) | 0-1背包 | Java | 卡码 46 LeetCode 416 做题总结

代码随想录笔记 AcWing-背包九讲专题 一道例题 dd大牛背包9讲 背包笔记 对于面试的话&#xff0c;其实掌握01背包&#xff0c;和完全背包&#xff0c;就够用了&#xff0c;最多可以再来一个多重背包。 01背包&#xff1a;n种物品&#xff0c;每种物品只有 1 个&#xff0c;每…

Linux 内核编译安装 - Deepin,Debian系

过程 下载 网站下载linux内核源码[^1] [^2]&#xff0c;并解压&#xff0c;进入源码目录&#xff1b; https://www.kernel.org/ https://mirrors.tuna.tsinghua.edu.cn/kernel/v6.x/ 安装依赖 参考脚本安装依赖[^3]&#xff1b; sudo apt install git wget fakeroot build…

观察者模式的实现

引言&#xff1a;观察者模式——程序中的“通信兵” 在现代战争中&#xff0c;通信是胜利的关键。信息力以网络、数据、算法、算力等为底层支撑&#xff0c;在现代战争中不断推动感知、决策、指控等各环节产生量变与质变。在软件架构中&#xff0c;观察者模式扮演着类似的角色…

Vue和Element UI 路由跳转,侧边导航的路由跳转,侧边栏拖拽

首先看布局&#xff0c;因为我的用于页面显示的 <router-view> 是通过重定向定位到登陆页的&#xff0c;然后通过登陆页跳转到主页。项目中用到了点击侧边栏的跳转&#xff0c;所以记录下来&#xff0c;方便有需要的人用到~ 阐述 &#xff08;1&#xff09;.content{ di…

openharmony上传图片,并获取返回路径

适用条件&#xff1a; openharmony开发 4.0 release版本&#xff0c;对应能力API10 一直不断尝试&#xff0c;一会用官方提供的上传文件&#xff0c;一会用第三方库的axios都不行&#xff0c; 一会报错‘没权限&#xff0c;一会报错’路径错误&#xff0c;还有报错‘401参数错…

探索Java网络编程精髓:UDP与TCP的实战魔法!

Java 中提供了专门的网络编程程序包 java.net&#xff0c;提供了两种通信协议&#xff1a;UDP&#xff08;数据报协议&#xff09;和 TCP&#xff08;传输控制协议&#xff09;&#xff0c;本文对两种通信协议的开发进行详细介绍。 1 UDP 介绍 UDP&#xff1a;User Datagram Pr…

压缩pdf文件的大小,pdf档怎么压缩为最小内存

在现代工作和学习中&#xff0c;pdf文件已经成为了一种不可或缺的文件格式。它跨平台、保持格式不变的优势使其在文件传输和分享中占据了重要位置。然而&#xff0c;pdf文件往往因为包含大量图像和文本而体积较大&#xff0c;这给文件的传输和存储带来了不少困扰。本文将为你介…

不会编程怎么办?量化交易不会编程可以使用吗?

量化交易使用计算机模型程序代替人工进行交易&#xff0c;一般需要投资者自己编写程序建模&#xff0c;然后回测无误之后再进行实盘交易&#xff0c;那么不会编程的投资者能使用量化软件进行量化交易吗&#xff1f; 不会编程使用量化软件有两种方法 一种是请人代写代码&#x…

Java高频面试基础知识点整理8

干货分享&#xff0c;感谢您的阅读&#xff01;背景​​​​​​高频面试题基本总结回顾&#xff08;含笔试高频算法整理&#xff09; 最全文章见&#xff1a;Java高频面试基础知识点整理 &#xff08;一&#xff09;Java基础高频知识考点 针对人员&#xff1a; 1.全部人员都…

【三维AIGC】扩散模型LDM辅助3D Gaussian重建三维场景

标题&#xff1a;《Sampling 3D Gaussian Scenes in Seconds with Latent Diffusion Models》 来源&#xff1a;Glasgow大学&#xff1b;爱丁堡大学 连接&#xff1a;https://arxiv.org/abs/2406.13099 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何…

Guava LocalCache源码分析:LocalCache生成

Guava LocalCache源码分析&#xff1a;Cache生成 版本LocalCache参数说明Cache构建过程LocalCache介绍LocalCache实例化将builder中的属性赋值到LocalCache中分段 LocalCache为guava本地缓存的解决方案&#xff0c;提供了基于容量&#xff0c;时间和引用的缓存回收方式&#xf…

Spring MVC入门3

看完这篇博客你能学到什么 理解JSON的使用理解注解PathVariable理解解注解RequestPart理解cookie和Session的基本概念理解cookie和Session的区别 如果想真正掌握&#xff0c;还需要自己勤加练习。 正文 JSON JSON概念 JSON&#xff1a;JavaScript Object Notation 【JavaS…

免费长效IP在业务场景中的深度应用解析

在数字化竞赛的跑道上&#xff0c;每一秒的都至关重要&#xff0c;而免费长效IP&#xff0c;不仅为企业减少了运营成本&#xff0c;更在数据安全与访问效率上筑起了一道坚实的保护线。然而&#xff0c;面对市场上琳琅满目的代理服务&#xff0c;如何挑选出能应对各种业务场景的…