【裸机开发】中断系统 —— IRQ 中断服务函数(汇编部分)

news2024/12/26 23:47:11

IRQ 和前面的Reset 函数不大一样,当一个IRQ中断产生时,我们也不知道这个IRQ中断来自哪个外设,因此,需要先获取到中断ID,随后才会跳转到真正的中断服务函数执行处理逻辑。

整个 IRQ 中断处理可以看做是包含了两个部分:

  • 汇编部分(环境准备):获取中断 ID,跳转中断处理函数
  • C 语言部分:执行中断逻辑处理

目录

一、IRQ 环境准备

二、环境准备汇编实现

1、保存现场

2、获取中断ID

3、切换 SVC 模式

4、跳转通用中断服务函数

5、 恢复现场

三、完整写法


一、IRQ 环境准备

中断处理的整体思路是,先保存现场,切换到指定模式,随后再跳转中断处理函数,执行完处理逻辑以后再恢复到原状态继续向下执行。

这里要做的工作也是类似,唯独可能会略有不同的就是新增了和中断处理逻辑相关的内容:

  • 保存现场(保存 LR、CPSR寄存器以及其他寄存器)
  • 获取中断ID
  • 切换 SVC 模式
  • 跳转中断处理函数
  • 恢复现场(恢复SPSR、回到中断发生前的下一个位置)

二、环境准备汇编实现

1、保存现场

因为即将跳转到中断服务函数,我们需要保存如下内容:

  • 上下文内容:下面要用r0-r3 寄存器的内容,
  • 下一条指令的地址:当中断发生时,LR 寄存器会自动保存下一条指令的地址
  • SPSR 寄存器:用于保存CPSR寄存器的状态,以便于中断处理完毕后恢复到原状态

push {lr}             /* 保存 lr 地址 */
push {r0-r3, r12}     /* 保存 r0-r3,r12 寄存器 */ 

mrs r0, spsr          /* 读取 spsr 寄存器 */
push {r0}             /* 保存 spsr 寄存器 */

2、获取中断ID

(1)  获取寄存器地址

中断控制器 GIC 下包含了一系列寄存器(寄存器组),如记录寄存器状态、记录寄存器中断ID的寄存器。我们只要获得了这个寄存器组的首地址,后续就可以根据偏移拿到每一个寄存器的地址。

如下便是 CBAR寄存器,其中的 PERIPHBASE 便记录了 GIC 控制器的首地址。我们可以通过如下命令获得 GIC 的寄存器组首地址。现将其保存到 r1 寄存器。

mrc p15, 4, r1, c15, c0, 0        /* 将CP15协处理器的C0内的值保存到 r1 寄存器中 */

现在拿到首地址以后,还需要拿到偏移定位到指定寄存器。我们要关注的是 CPU Interface 这个寄存器组。我们先拿到 CPU Interface 这个寄存器组的首地址,相对于GIC 的首地址是 0x2000

add r1, r1, #0x2000        /* r1中本就保存了 GIC 基地址,现在需要加上偏移 0x2000 */
                           /* 于是就得到了CPU Interface 寄存器组的基地址 */

我们看到 GICC_IAR 寄存器,这个寄存器中保存了中断ID,这个寄存器相对于CPU Interface 寄存器组的偏移量为 0x000C

add r1, r1, #0x000C     

(2) 获取中断ID

下面是 GICC_IAR 的字段分布,我们只需要获取到其中的 9-0 bit 即可。

  • 先将 r1 寄存器指向的地址内容加载到 r0 寄存器
  • 然后 r0 寄存器取出低10位

str r1, [r0]            /* 将 r1 寄存器指向的内容保存到 r0 */ 
and r0, r0, #0x3ff      /* 取出低10位 */ 

(3) 保存中断 ID

后续会跳转到中断服务函数,我们需要给函数传参,中断服务函数在调用的时候默认从r0-r3 寄存器中获取参数内容。第一个参数从 r0 获取,第二个参数从r1 获取,... 以此类推。

调用C函数传递参数时,参数个数小于 4 个,可以使用 r0 - r3 传递;如果大于 4 个,需要使用堆栈传递。

push {r0, r1}             /* 保存 r0,r1 */

3、切换 SVC 模式

发生 IRQ 中断时,系统会自动进入 IRQ 模式,但是我们执行中断处理逻辑的时候,可能会产生其他中断,所以在处理中断逻辑的时候,我们可以先进入SVC模式,等处理完了再切换回 IRQ 模式。

下面我们使用 cps 命令直接切换到 SVC 模式

cps #0x13         /* 进入 SVC 模式,允许其他中断再次进去 */

4、跳转通用中断服务函数

万事具备,只等跳转。system_irqhandler  是我们在 C 语言中定义好的一个函数。

push {lr}                     /* 保存 SVC 模式的 lr 寄存器 */
ldr r2, =system_irqhandler    /* 加载 C 语言中断处理函数到 r2 寄存器中*/
blx r2                        /* 运行 C 语言中断处理函数,带有一个参数 */
pop {lr}                      /* 执行完 C 语言中断服务函数,lr 出栈 */
/*
 * 参数 giccIar 是中断ID
 */
void system_irqhandler(unsigned int giccIar)
{
    /* 中断处理逻辑 */
}

5、 恢复现场

剩下的就是一些出栈操作了。

  • 切换到 IRQ 模式。原本切换到 SVC 模式下是为了在执行中断服务函数时,允许其他中断进入,现在中断服务函数执行完毕,需要切换回 IRQ 模式
  • 每次从 GICC_IAR 中读取中断ID以后,需要将中断ID值写入到 GICC_EOIR 寄存器。
  • 恢复 SPSR、r0 - r3、r12 寄存器
  • subs pc, lr, #4
cps #0x12             /* 切换回 IRQ 模式 */
pop {r0, r1}          /* 对应之前的 push {r0, r1} */

str r0, [r1, #0x10]   /* 中断执行完成,写 EOIR */

pop {r0}
msr spsr_cxsf, r0     /* 恢复 spsr */
pop {r0-r3, r12}      /* r0-r3,r12 出栈 */
pop {lr}              /* lr 出栈 */

subs pc, lr, #4       /* 将 lr-4 赋给 pc */

解析:subs pc, lr, #4

这里涉及到指令流水线的问题,指令执行的时候,译码器和PC计数器并不会闲着,而是先一步获取下一次要用到的指令。这就造成了PC 计数器中保存的指令地址 和 实际在执行的指令地址相差 8 个字节。

假设在执行指令1 的时候产生了一个中断,执行完手上的指令后会调转到中断向量表,然后去调用对应的中断服务函数,但是等到回来的时候,要执行 PC 指向的指令,所以我们发现这里直接跳过了指令2。

因此在中断服务函数执行完毕后,我们需要让 PC 寄存器的值减4,即回到上一条指令继续运行。

 

三、完整写法

push {lr}             /* 保存 lr 地址 */
push {r0-r3, r12}     /* 保存 r0-r3,r12 寄存器 */ 
mrs r0, spsr          /* 读取 spsr 寄存器 */
push {r0}             /* 保存 spsr 寄存器 */

mrc p15, 4, r1, c15, c0, 0         /* 将CP15协处理器的C0内的值保存到 r1 寄存器中 */
add r1, r1, #0x2000                /* r1中本就保存了 GIC 基地址,现在需要加上偏移 0x2000 */
                                   /* 于是就得到了CPU Interface 寄存器组的基地址 */
add r1, r1, #0x000C  
str r1, [r0]                       /* 将 r1 寄存器指向的内容保存到 r0 */ 
and r0, r0, #0x3ff                 /* 取出低10位(获取中断ID) */ 
push {r0, r1}                      /* 保存 r0,r1 */
​
cps #0x13                     /* 进入 SVC 模式,允许其他中断再次进去 */
push {lr}                     /* 保存 SVC 模式的 lr 寄存器 */
ldr r2, =system_irqhandler    /* 加载 C 语言中断处理函数到 r2 寄存器中*/
blx r2                        /* 运行 C 语言中断处理函数,带有一个参数 */
pop {lr}                      /* 执行完 C 语言中断服务函数,lr 出栈 */

​cps #0x12             /* 切换回 IRQ 模式 */
pop {r0, r1}          /* 对应之前的 push {r0, r1} */
str r0, [r1, #0x10]   /* 中断执行完成,写 EOIR */
pop {r0}
msr spsr_cxsf, r0     /* 恢复 spsr */
pop {r0-r3, r12}      /* r0-r3,r12 出栈 */
pop {lr}              /* lr 出栈 */

subs pc, lr, #4       /* 将 lr-4 赋给 pc */

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

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

相关文章

CSS查缺补漏之选择器

最近在复盘CSS基础知识,发现很多CSS选择器里面还是大有学问,需要详细总结一番,以备差缺补漏~ 作为CSS基础的一大类别,选择器又分为多种类别,本篇内容默认读者已了解并掌握基础选择器【通配符选择器】、【元素选择器】…

springboot+vue项目之MOBA类游戏攻略分享平台(java项目源码+文档)

风定落花生,歌声逐流水,大家好我是风歌,混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的MOBA类游戏攻略分享平台。项目源码以及部署相关请联系风歌,文末附上联系信息 。 💕💕作者&#xf…

自动化测试框架Playwright安装以及使用

最近,微软开源了一个非常强大的自动化项目叫 playwright-python 它支持主流的浏览器,包含:Chrome、Firefox、Safari、Microsoft Edge 等,同时支持以无头模式、有头模式运行,并提供了同步、异步的 API,可以…

C++学习——第二节课-输入输出

大家好,我是涵子。今天我们来学习C中的输入输出。 一、电脑中的输入输出 日常生活中常见的电脑、手机、电视机外部接口,也就是I/O(输入/输出)接口部分,其样式、种类较多,不同的接口配置也体现了设备的档次…

SpringBoot整合jwt+redis+随机验证码+Vue的登录功能

一、运行效果展示 !注意:前端的Vue项目中要引入element-ui和axios # npm安装element-ui、axios npm insatll element-ui -S npm install axios -S # 在main中引入 // 引入ElementUI import ElementUI from element-ui import element-ui/lib/theme-chalk…

springboot动态加载json文件

resources下面的配置文件,application文件修改启动会实时加载新的内容 其他的文件属于静态文件,打包后会把文件打入jar里面,修改静态文件启动不会加载新的内容 Resource areacode nre FileSystemResource("config" File.separa…

技术分享——数据安全之数据分类方法小集

背景 2021年6月10日,《中华人民共和国数据安全法》(以下简称“《数安法》”)通过了第十三届全国人民代表大会常务委员会第二十九次会议并予以发布,标志着我国数据安全工作进入到有法可依的新阶段。 本文通过梳理现有的部分法规、…

Fiddler Orchestra从安装到实战演练

上次谈到了Fiddler Orchestra用户指南,这次笔者把自己的实战演练分享大家,闲话少说,步骤如下: 1、根据前面文章《Fiddler Orchestra用户指南》,Fiddler Orchestra客户端和控制器只能运行在至少支持.NET Standard 2.0的…

Redis入门(一)

第1章 NoSQL 1.1 NoSQL数据库 1.1.1 NoSQL是什么 (1)NoSQL(Not Only SQL ),意即“不仅仅是SQL”,泛指非关系型的数据库。 (2)NoSQL不拘泥于关系型数据库的设计范式,放弃了通用的技术标准&…

MyAQL事务

目录 ----------------------MySQL 事务-------------------------------- 1.事务的概念 2.事务的ACID特点 ●原子性 ●一致性 ●隔离性 事务隔离级别的作用范围分为两种: ●持久性 3.事务控制语句 案例: 4…

【Elacticsearch】 倒排索引的查增删改原理

关联文章:【Elacticsearch】 原理/数据结构/面试经典问题整理_东方鲤鱼的博客-CSDN博客 建立索引的原理 当向协调节点发送请求以索引新文档时,将执行以下操作: 所有在Elasticsearch集群中的节点都包含:有关哪个分片存在于哪个节点…

深度学习入门笔记1--梯度下降之--为什么是负方向--为什么局部下降最快的是负梯度方向

本节目标理解梯度下降的原理,主要围绕以下几个问题展开: 梯度下降法的用途?什么是梯度?为什么是负的梯度为什么局部下降最快的方向就是梯度的负方向。 需要的知识储备:一级泰勒展开公式 向量内积计算公式 1. 梯度下…

Sui主网升级至V1.3.0版本

Sui主网现已升级至V1.3.0版本,升级要点如下所示: 将协议版本更新至12 开始在Narwhal中使用BatchV2,新增VersionedMetadata允许更精细的追踪Narwhal批处理延迟。有关详细信息,请参阅#12178和#12290。 将协议版本更新至13 弃用0…

wtmp日志读取

wtmp日志介绍 之前遇到一个AIX服务器登录不上,但是能ping通的事情。一开始我怀疑是sshd服务坏掉了,但是使用telnet也无法登录。好在这台机器所在的机房就在我隔壁,于是外接显示器,直接上机操作。好在直接通过物理介质还是能登录得…

全球企业KVM贡献榜公布,腾讯云再添1项核心突破

6月14日,在全球虚拟化顶级技术峰会 KVM Forum 上,2023年度全球企业 KVM 开源贡献榜正式发布。腾讯云成为中国唯一连续七年入围的云厂商。 作为云计算的关键底层技术,云厂商需要利用KVM对物理机进行虚拟化,提供云端的池化算力。作为…

如何「假装」自己做过性能测试?

简历: 熟练掌握后端性能、压力测试 面试官: 你们是怎么做性能测试的? 我: 主要是对后端服务模块进行性能测试,我们上一个项目是是一个群聊项目,类似于QQ群,大家可以在一个群里聊天&#xf…

视觉SLAM十四讲——ch10实践(后端2)

视觉SLAM十四讲——ch10的实践操作及避坑 0. 实践前小知识介绍1. 实践操作前的准备工作2. 实践过程2.1 g2o原生位姿图2.2 李代数上的位姿图优化 3. 遇到的问题及解决办法3.1 在运行pose_graph_g2o_lie时出现错误 0. 实践前小知识介绍 视觉SLAM(Simultaneous Locali…

基于Java菜匣子优选系统设计实现(源码+lw+部署文档+讲解等)

博主介绍: ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ 🍅 文末获取源码联系 🍅 👇🏻 精…

粒子群算法(Particle Swarm Optimization(PSO)附简单案例及详细matlab源码)

作者:非妃是公主 专栏:《智能优化算法》 博客地址:https://blog.csdn.net/myf_666 个性签:顺境不惰,逆境不馁,以心制境,万事可成。——曾国藩 文章目录 专栏推荐序一、概论二、粒子群算法原理…

【复杂网络建模】——使用PyTorch和DGL库实现图神经网络进行链路预测

🤵‍♂️ 个人主页:Lingxw_w的个人主页 ✍🏻作者简介:计算机科学与技术研究生在读 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 &#x1f4a…