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

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

全部换成

/* */

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

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

首先声明全局 .global_start

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

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

第一步:设置中断向量表

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

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

;中断向量表ldr pc,=Reset_Handlerldr pc,=Undefined_Handlerldr pc,=SVC_Handlerldr pc,=PrefAbort_Handlerldr pc,=DataAbort_Handlerldr pc,=NotUsed_Handlerldr pc,=IRQ_Handlerldr 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,=0x87800000dsbisbmcr p15,0,r0,c12,c0,0dsbisb

其中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.mzph.cn/pingmian/45596.shtml

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

相关文章

pdf工具

iLovePDF | 为PDF爱好者提供的PDF文件在线处理工具 https://www.ilovepdf.com/zh-cn 图片 pdf 合并成一个pdf也可以拆分

近期matlab学习笔记,学习是一个记录,反复的过程

近期matlab学习笔记&#xff0c;学习是一个记录&#xff0c;反复的过程 matlab的mlx文件在运行的时候&#xff0c;不需要在文件夹路径下&#xff0c;也能运行&#xff0c;但是需要调用子函数时&#xff0c;就需要在文件所在路径下运行 那就先运行子函数&#xff0c;把路径换过来…

YOLOv10: Real-Time End-to-End Object Detection

双重标签分配 与一对一多分配不同&#xff0c;一对一匹配只为每个地面真相分配一个预测&#xff0c;避免了NMS后处理。然而&#xff0c;这导致了较弱的监督&#xff0c;导致次优的准确性和收敛速度。幸运的是&#xff0c;这种缺陷可以通过一对一多分配来弥补。为此&#xff0c…

Redis的配置优化、数据类型、消息队列

文章目录 一、Redis的配置优化redis主要配置项CONFIG 动态修改配置慢查询持久化RDB模式AOF模式 Redis多实例Redis命令相关 二、Redis数据类型字符串string列表list集合 set有序集合sorted set哈希hash 三、消息队列生产者消费者模式发布者订阅者模式 一、Redis的配置优化 redi…

Androidstudio安卓开发,SharedPreferences实现登录注册

1. 项目涉及到的技术点 SharedPreferences的使用 2. 效果图 3. 实现过程 注册布局文件&#xff1a;activity_register.xml <?xml version"1.0" encoding"utf-8"?> <androidx.appcompat.widget.LinearLayoutCompat xmlns:android"http:…

mindspore打卡第24天之LSTM+CRF序列标注

LSTMCRF序列标注 概述 序列标注指给定输入序列&#xff0c;给序列中每个Token进行标注标签的过程。序列标注问题通常用于从文本中进行信息抽取&#xff0c;包括分词(Word Segmentation)、词性标注(Position Tagging)、命名实体识别(Named Entity Recognition, NER)等。以命名实…

细说MCU用定时器控制ADC采样频率的实现方法

目录 一、工程依赖的硬件及背景 二、设计目的 三、 建立工程 1.选择时钟源和Debug模式 2.配置系统时钟和ADC时钟 3.配置串口 4.配置ADC 5.设置TIM3 6.设置TIM4 7.配置中断 8.GPIO 四、代码修改 1.重新定义ADC回调函数 2.在主程序中编写数据发送代码 3.使能ADC和…

json-server服务使用教程

目录标题 安装 json-server启动 json-server 本地服务 安装 json-server npm install -g json-server0.17.4json-server -v报错请参考&#xff1a;执行json-server -v报错 因为在此系统上禁止运行脚本。 启动 json-server 本地服务 查看本机IP&#xff1a;ipconfig Shift右…

220.贪心算法:根据身高重建队列(力扣)

代码解决 class Solution { public:// 定义排序规则&#xff1a;首先按身高降序排序&#xff0c;如果身高相同则按k值升序排序static bool cmp(const vector<int>&a, const vector<int>&b){if (a[0] b[0]) return a[1] < b[1]; // 如果身高相同&#…

51单片机-第三节-LCD1602调试工具,矩阵键盘

一、LCD调试工具函数&#xff1a; 使用&#xff1a; 所有函数&#xff0c;前两个参数&#xff0c;均为指定显示位置。 四个参数的&#xff0c;第四个参数&#xff0c;为保留位数&#xff0c;少的保留后面&#xff08;123,2 -> 23&#xff09;&#xff0c;多的前面补零。 …

Web开发 —— 放大镜效果(HTML、CSS、JavaScript)

目录 一、需求描述 二、实现效果 三、完整代码 四、实现过程 1、HTML 页面结构 2、CSS 元素样式 3、JavaScript动态控制 &#xff08;1&#xff09;获取元素 &#xff08;2&#xff09;控制大图和遮罩层的显隐性 &#xff08;3&#xff09;遮罩层跟随鼠标移动 &…

k8s核心操作_k8s中的存储抽象_基本概念与NFS搭建_Deployment使用NFS进行挂载---分布式云原生部署架构搭建028

然后我们继续开始看 如果我们使用容器部署,比如我们有三个节点,一个是master,一个node1 一个是node2 那么pod 中我们可以看到,容器中的 /data 等各个目录都映射了出来了,但是 如果比如上面红色的部分,有个pod,原来在node2上,最右边那个,但是这个pod宕机了 那么,k8s会在node…

永磁同步电机谐波抑制算法(7)——基于自适应陷波(adaptive notch filter,ANF)的精确谐波电流抑制策略

1.前言 1.1经典谐波抑制策略存在的问题 在之前的谐波抑制专题中&#xff0c;主要介绍了两种谐波抑制策略——基于多同步旋转坐标系的谐波抑制策略以及基于比例积分谐振PIR调节器的谐波抑制策略&#xff0c;同时还介绍了这两种策略的改进办法&#xff0c;进而使得这两种策略在…

Go:基本变量与数据类型

目录 前言 前期准备 Hello World! 一、基本变量 1.1 声明变量 1.2 初始化变量 1.3 变量声明到初始化的过程 1.4 变量值交换 1.5 匿名变量 1.6 变量的作用域 二、数据类型 1.1 整型 1.2 浮点型 1.3 字符串 1.4 布尔类型 1.5 数据类型判断 1.6 数据类型转换 1.…

NewStarCTF 2023 week5--web

目录 Unserialize Again 法一:(非预期) 法二: Final Yes Pickle pppython? 4-复盘 Unserialize Again f12告诉了我们cookie, 查看一下,可以发现 pairing.php <?php highlight_file(__FILE__); error_reporting(0); class story{private $useradmin;public $p…

Centos7 新增yum源

背景&#xff1a;原来的yum源&#xff0c;无法下载yum包了。新增一个阿里云的&#xff08;网易163的源失效了&#xff0c;无法使用&#xff09; Could not retrieve mirrorlist http://mirrorlist.centos.org/?release7&archx86_64&repoos&infrastock error was …

three.js官方案例webgpu_reflection.html学习记录

目录 ​1 判断浏览器是否支持 2 THREE.DirectionalLight 2.1DirectionalLightShadow 3 Texture 3.1 .wrapS 3.2 .wrapT 3.3 .colorSpace 4 创建地面 5 WebGPURenderer 6 OrbitControls 控制器 7 屏幕后处理 import * as THREE from three;import { MeshPhongNodeMa…

简析“请求头”——可以用“头部字典”按需定制请求头

请求头是HTTP请求的重要部分&#xff0c;可以用“头部字典”按需定制请求头。 (笔记模板由python脚本于2024年07月12日 19:28:44创建&#xff0c;本篇笔记适合喜欢钻研web知识点的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free…

RSA算法(C++)

RSA加解密过程 RSA为非对称加密算法&#xff0c;由一对公钥和一对私钥构成&#xff0c;私钥加密公钥解密&#xff0c;公钥加密私钥解密 如下图,D为私密的&#xff0c;假设传输英文字母&#xff0c;我们给英文字母编号A1,B2,C3… RSA加解密过程 两对密钥产生方法如下 C Op…

【RHCE】基于密钥的身份验证(Win-Linux)

目的&#xff1a;要提⾼系统安全性&#xff0c;通过在 OpenSSH 服务器上禁⽤密码⾝份验证来强制进⾏基于密钥的⾝份验证。 1、一台虚拟机无需密码连接另一台虚拟机 .ssh目录 > 保存了ssh相关的key和一些记录文件 &#xff08;1&#xff09;生成密钥对 使⽤这个流程在本地…