【裸机开发】IRQ 中断服务函数(一) —— 汇编初始化

news2025/1/16 23:59:53

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位

ldr r0, [r1]            /* 将 r1 寄存器指向的地址对应内容保存到 r0 */ 
ldr r4, =0x3ff          /* 无法直接使用 #0x3ff,因为 0x3ff 不是立即数*/
and r0, r0, r4          /* 取出低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  
ldr r0, [r1]            /* 将 r1 寄存器指向的地址对应内容保存到 r0 */ 
ldr r4, =0x3ff          /* 无法直接使用 #0x3ff,因为 0x3ff 不是立即数*/
and r0, r0, r4          /* 取出低10位 */ 
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/678479.html

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

相关文章

MySQL 自增主键一定是连续的吗?

众所周知,自增主键可以让聚集索引尽量地保持递增顺序插入,避免了随机查询,从而提高了查询效率 但实际上,MySQL 的自增主键并不能保证一定是连续递增的。 下面举个例子来看下,如下所示创建一张表: 自增值保…

ORCA优化器浅析——GP数据库调用优化器流程

首先我们需要看CGPOptimizer类(src/include/gpopt/CGPOptimizer.h)为Greenplum数据库提供ORCA优化器export出来的函数的封装。Greenplum数据库主流程调用extern "C"中提供的函数,比如初始化ORCA优化器的函数InitGPOPT,优化查询树的函数GPOPTOp…

springboot+jsp农产品商城宣传网站设计与实现oo6e3

在该在线助农系统设计与实现中,idea能给用户提供更多的方便,其特点一是方便学习,方便快捷;二是有非常大的信息储存量,主要功能是用在对数据库中查询和编程。其功能有比较灵活的数据应用,只需利用小部分代码…

【Leetcode60天带刷】day30回溯算法——332.重新安排行程 , 51. N皇后 ,37. 解数独

​ 题目: 332. 重新安排行程 给你一份航线列表 tickets ,其中 tickets[i] [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。 所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,…

【从零开始学习JAVA | 第十四篇】继承

目录 前言: 引入: 继承: 小拓展: 优点: 成员方法的继承问题: 总结: 前言: 继承是面向对象三大特性之一,它是在封装之后我们讲解的一个重要的性质,继承…

在github上创建个人主页的方法【2023更新版】

01-进入github的网站,链接 https://github.com/ ,然后注册,登陆,注意登陆时设置的用户名(username)就是将来你个人主页的三级域名,所以这里一定要慎重填写username。如下图所示: 02-注册完成后进入个人主…

2024考研408-计算机组成原理第四章-指令系统

文章目录 前言一、指令系统现代计算机的结构1.1、指令格式1.1.1、指令的定义1.1.2、指令格式1.1.3、指令—按照地址码数量分类①零地址指令②一地址指令(1个操作数、2个操作数情况)③二地址指令④三地址指令⑤四地址指令 1.1.4、指令-按照指令长度分类1.…

【计算机组成原理】Yy-z02模型机的硬布线控制器设计

目录 一、Yy-z02模型机的系统结构 二、Yy-z02模型机的数据通路 三、Yy-z02模型机的指令执行 四、Yy-z02模型机的硬布线控制器 一、Yy-z02模型机的系统结构 指令系统的实现 <--- 构造它的硬件系统 硬件系统构造过程&#xff1a; 分析指令格式和各指令的功能确定部件连…

金蝶软件遭遇.locked勒索病毒攻击:如何保护与解救您的数据?

引言&#xff1a; 近期&#xff0c;部分运行金蝶云星空软件的服务器遭受了一场勒索病毒的网络安全攻击&#xff0c;其重要数据遭到了.locked勒索病毒的加密。作为一个知名的企业级ERP软件及财务软件&#xff0c;金蝶软件的数据安全事关客户和企业的利益。91数据恢复在本文将深…

【王道·操作系统】第四章 文件管理【未完】

一、初识文件管理 文件&#xff1a;一组有意义的信息/数据集合文件属性&#xff1a; 文件名&#xff1a;创建文件的用户决定&#xff0c;主要是为了方便用户找到文件&#xff0c;同一目录下不允许有重名文件标识符&#xff1a;一个系统内的各文件标识符唯一&#xff0c;对用户来…

老大给了个新需求:如何将汉字转换成拼音字母?1行Python代码搞定!

大家好&#xff0c;这里是程序员晚枫&#xff0c;小红薯也叫这个名。 之前的视频给大家分享了&#xff1a;中文编程&#xff0c;一行代码实现。 今天给大家分享一下&#xff0c;如何通过1行Python代码&#xff0c;实现汉语转拼音 1、先上代码 实现汉语转拼音效果的第三方库…

逻辑回归(Logistics Regression)

1.逻辑回归&#xff08;Logistics Regression&#xff09; 逻辑回归用于解决二分类问题 1.1 Sigmoid函数 sigmoid函数在神经网络中如何起作用&#xff1f;详见本人笔记&#xff1a;机器学习和AI底层逻辑 复杂非线性分类->多个线段->每个线段是叠加而来的->sigmoid函…

计算机视觉 + Self-Supervised Learning 五种算法原理解析

计算机视觉领域下自监督学习方法原理 导语为什么在计算机视觉领域中进行自我监督学习&#xff1f; 自监督学习方法Generative methodsBEiT 架构 Predictive methodsContrastive methodsBootstraping methodsSimply Extra Regularization methods 导语 自监督学习是一种机器学习…

Web服务器群集:Nginx网页及安全优化

目录 一、理论 1.Nginx网页优化 2.Nginx安全优化 3.Nginx日志分割 二、实验 1.网页压缩 2.网页缓存 3.连接超时设置 4.并发设置 5.隐藏版本信息 6.脚本实现每月1号进行日志分割 7.防盗链 三、总结 一、理论 1.Nginx网页优化 &#xff08;1&#xff09;概述 在企…

神仙打架的618,谁才是真正的大赢家?

618大促已经缓缓落下帷幕&#xff0c;各大平台和品牌方准时准点晒出成绩单。在一串又一串红彤彤的战报中&#xff0c;家电品牌你追我赶的激烈战况一如以往。 我们从中也得以窥见新消费时代下中国家电行业的未来&#xff0c;尤其是在消费者纷纷捂紧钱袋子的今年&#xff0c;红色…

快解析域名映射,通过外网域名来访问内网

在本地搭建主机应用后&#xff0c;由于没有公网IP或没有公网路由权限&#xff0c;在需要发布互联网时&#xff0c;就需要用到外网访问内网的一些方案。由于内网IP在外网不能直接访问&#xff0c;通常就用通过外网域名来访问内网的方法。那么&#xff0c;公网域名如何解析到内网…

无法开启nginx -t等命令的解决办法

在 Windows 下载安装了 Nginx&#xff0c;配置了环境变量全局使用 Nginx。但是仍旧无法开启nginx -t等命令。 原因&#xff1a; Nginx 在使用时 conf-path 是根据相对路径来找的。 匹配的路径是 C:\Users\20210121/conf/nginx.conf &#xff0c;完全是错的。 解决办法&#x…

Redis进阶 - Redis持久化

原文首更地址&#xff0c;阅读效果更佳&#xff01; Redis进阶 - Redis持久化 | CoderMast编程桅杆https://www.codermast.com/database/redis/redis-advance-persistence.html 单点Redis的问题 数据丢失问题&#xff1a;Redis 是内存存储&#xff0c;服务重启可能会丢失数据。…

基于卷积神经网络的高光谱图像分类

文章目录 引言1. 基于光谱特征2. 基于空间特征3. 基于空谱特征3.1 空间特征和光谱特征的融合3.2 基于3D-CNN分类 4. 总结 引言 近年来深度学习的技术在计算机视觉领域中大放异彩&#xff0c;使得对多光谱数据分类的研究迅速发展&#xff0c;结合2D-CNN&#xff0c;3D-CNN&…

数据结构第六章 图 6.4 错题整理

4.A A. 不是简单路径的话&#xff0c;有环&#xff0c;去环路径会更短 B. 适合的 弗洛伊德算法才不适合 C. 本来就是 D 2X2矩阵拓展到3X3矩阵 再扩大 若是子集 即加入新顶点后&#xff0c;最短路径都没有变&#xff0c;错 5.B 本题用弗洛伊德更合适 但这道题只需全部代入求最…