关于正点原子的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:下也是没有问题的。
相关文章:
关于正点原子的alpha开发板的启动函数(汇编,自己的认识)
我傻逼了,这里的注释还是不要用; 全部换成 /* */ 这里就分为两块,一部分是复位中断部分,第二部分就是IRQ部分(中断部分最重要) 我就围绕着两部分来展开我的认识 首先声明全局 .global_start 在 ARM 架…...
Deep Layer Aggregation【方法部分解读】
摘要: 视觉识别需要跨越从低到高的层次、从小到大的尺度以及从精细到粗略的分辨率的丰富表示。即使卷积网络的特征层次很深,单独的一层信息也不足够:复合和聚合这些表示可以改进对“是什么”和“在哪里”的推断。架构上的努力正在探索网络骨干的许多维度,设计更深或更宽的架…...
大数据面试SQL题-笔记01【运算符、条件查询、语法顺序、表连接】
大数据面试SQL题复习思路一网打尽!(文档见评论区)_哔哩哔哩_bilibiliHive SQL 大厂必考常用窗口函数及相关面试题 大数据面试SQL题-笔记01【运算符、条件查询、语法顺序、表连接】大数据面试SQL题-笔记02【...】 目录 01、力扣网-sql题 1、高频SQL50题(…...
零基础自学爬虫技术该从哪里开始入手?
零基础自学爬虫技术可以从以下几个方面入手: 一、学习基础编程语言 Python 是爬虫开发的首选语言,因此首先需要学习 Python 编程语言的基础知识。这包括: 语法基础:学习 Python 的基本语法,如变量定义、数据类型、控…...
CV11_模型部署pytorch转ONNX
如果自己的模型中的一些算子,ONNX内部没有,那么需要自己去实现。 1.1 配置环境 安装ONNX pip install onnx -i https://pypi.tuna.tsinghua.edu.cn/simple 安装推理引擎ONNX Runtime pip install onnxruntime -i https://pypi.tuna.tsinghua.edu.cn/si…...
Redis的使用(四)常见使用场景-缓存使用技巧
1.绪论 redis本质上就是一个缓存框架,所以我们需要研究如何使用redis来缓存数据,并且如何解决缓存中的常见问题,缓存穿透,缓存击穿,缓存雪崩,以及如何来解决缓存一致性问题。 2.缓存的优缺点 2.1 缓存的…...
BERT架构的深入解析
BERT(Bidirectional Encoder Representations from Transformers)是由Google在2018年提出的一种基于Transformer架构的预训练模型,迅速成为自然语言处理(NLP)领域的一个里程碑。BERT通过双向编码器表示和预训练策略&am…...
数字孪生技术如何助力低空经济飞跃式发展?
一、什么是低空经济? 低空经济,是一个以通用航空产业为主导的经济形态,它涵盖了低空飞行、航空旅游、航空物流、应急救援等多个领域。它以垂直起降型飞机和无人驾驶航空器为载体,通过载人、载货及其他作业等多场景低空飞行活动&a…...
HTTP背后的故事:理解现代网络如何工作的关键(二)
一.认识请求方法(method) 1.GET方法 请求体中的首行包括:方法,URL,版本号 方法描述的是这次请求,是具体去做什么 GET方法: 1.GET 是最常用的 HTTP 方法. 常用于获取服务器上的某个资源。 2.在浏览器中直接输入 UR…...
数据流通环节如何规避安全风险
由于参与数据流通与交易的数据要素资源通常是经过组织加工的高质量数据集,甚至可能涉及国家核心战略利益,一旦发生针对数据流通环节的恶意事件,将造成较大负面影响,对数据要素市场的价值激活造成潜在威胁。具体来说,数…...
部署k8s 1.28.9版本
继上篇通过vagrant与virtualBox实现虚拟机的安装。笔者已经将原有的vmware版本的虚拟机卸载掉了。这个场景下,需要重新安装k8s 相关组件。由于之前写的一篇文章本身也没有截图。只有命令。所以趁着现在。写一篇,完整版带截图的步骤。现在行业这么卷。离…...
实验二:图像灰度修正
目录 一、实验目的 二、实验原理 三、实验内容 四、源程序和结果 源程序(python): 结果: 五、结果分析 一、实验目的 掌握常用的图像灰度级修正方法,包括图象的线性和非线性灰度点运算和直方图均衡化法,加深对灰度直方图的理解。掌握对比度增强、直方图增强的原理,…...
bash: ip: command not found
输入: ip addr 报错: bash: ip: command not found 报错解释: 这个错误表明在Docker容器中尝试执行ip addr命令时,找不到ip命令。这通常意味着iproute2包没有在容器的Linux发行版中安装或者没有正确地设置在容器的环境变量PA…...
全开源TikTok跨境商城源码/TikTok内嵌商城/前端uniapp+后端+搭建教程
多语言跨境电商外贸商城 TikTok内嵌商城,商家入驻一键铺货一键提货 全开源完美运营 海外版抖音TikTok商城系统源码,TikToK内嵌商城,跨境商城系统源码 接在tiktok里面的商城。tiktok内嵌,也可单独分开出来当独立站运营 二十一种…...
云原生、Serverless、微服务概念
云原生(Cloud Native) 云原生是一种设计和构建应用程序的方法,旨在充分利用云计算的优势。云原生应用程序通常具有以下特征: 容器化:应用程序和其依赖项被打包在容器中,确保一致的运行环境。常用的容器技…...
Windows上LabVIEW编译生成可执行程序
LabVIEW项目浏览器(Project Explorer)中的"Build Specifications"就是用来配置项目发布方法的。在"Build Specifications"右键菜单中选取"New",可以看到程序有几种不同的发布方法:Application(EXE)、Installer、.Net Inte…...
ref 和 reactive 区别
在Vue 3中,ref和reactive都是用于创建响应式数据的API,但它们之间存在一些关键的区别。以下是ref和reactive的主要区别: 1. 数据类型处理 ref:主要用于定义基本类型的数据(如字符串、数字、布尔值等)以及…...
深度学习计算机视觉中, 多尺度特征和上下文特征的区别是?
在深度学习和计算机视觉中,多尺度特征和上下文特征都是用来捕捉和理解图像中复杂模式和关系的重要概念,但它们的侧重点有所不同。 多尺度特征 (Multi-scale Features) 多尺度特征是指在不同尺度上对图像进行特征提取,以捕捉不同尺度的物体特…...
Facebook未来展望:数字社交平台的进化之路
在信息技术日新月异的时代,社交媒体平台不仅是人们交流沟通的重要工具,更是推动社会进步和变革的重要力量。作为全球最大的社交媒体平台之一,Facebook在过去十多年里,不断创新和发展,改变了数十亿用户的社交方式。展望…...
uniapp-vue3-vite 搭建小程序、H5 项目模板
uniapp-vue3-vite 搭建小程序、H5 项目模板 特色准备拉取默认UniApp模板安装依赖启动项目测试结果 配置自动化导入安装依赖在vite.config.js中配置 引入 prerttier eslint stylelint.editorconfig.prettierrc.cjs.eslintrc.cjs.stylelintrc.cjs 引入 husky lint-staged com…...
docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...
用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
