ARM 基础学习记录 / 异常与GIC介绍

news2024/12/26 14:44:38
GIC概念

念课本(以下内容都是针对"通用中断控制器(GIC)"而言,直接摘录的,有的地方可能不符人类的理解方式):

通用中断控制器(GIC)架构提供了严格的规范,不同厂商的中断控制器之间具有很高的一致性;该控制器包括一组用于管理单核或多核系统中的中断的硬件资源。GIC提供了内存映射寄存器,可用于管理中断源和行为,以及(在多核系统中)用于将中断路由到各个CPU核。它使软件能够屏蔽、启用和禁用来自各个中断源的中断,以(在硬件中)对各个中断源进行优先级排序和生成软件触发中断。它还提供对TrustZone安全性扩展的支持。GIC接受系统级别中断的产生,并可以发信号通知给它所连接的每个内核,从而有可能导致IRQ或FIQ异常发生。

通用中断控制器的工作流程。GIC分为两部分:分发器(Distributor)和CPU接口(CPU interface)。系统中的所有中断源都连接到分发器。可以通过仲裁单元的寄存器来控制各个中断源的属性,例如优先级、状态、安全性、触发方式和使能状态。中断的优先级和可接收中断的核都在分发器中配置。分发器把中断输出到“CPU接口单元”,后者决定将哪个中断转发给CPU核。CPU接口单元寄存器用于屏蔽、识别和控制转发到CPU核的中断的状态。系统中的每个CPU核心都有一个单独的CPU接口,一个CPU核不可能访问另一个CPU核的CPU接口。中断处理详情请看下面的"处理中断"部分。

GIC作为内存映射的外围设备,被软件访问。所有内核都可以访问公共的 GIC的分发器 单元。

中断在软件中由一个称为中断ID的数字标识。中断ID唯一对应于一个中断源。软件可以使用中断ID来识别中断源并调用相应的处理程序来处理中断。呈现给软件的中断ID由系统设计确定,一般在SOC的数据手册有记录。

中断可以有多种不同的类型:

  • 软件触发中断(SGI,Software Generated Interrupt)。这是由软件通过写入专用仲裁单元的寄存器即软件触发中断寄存器(ICDSGIR)显式生成的。它最常用于CPU核间通信。SGI既可以发给所有的核,也可以发送给系统中选定的一组核心。中断号0-15保留用于SGI的中断号。用于通信的确切中断号由软件决定。

  • 私有外设中断(PPI,Private Peripheral Interrupt)这是由单个CPU核私有的外设生成的。PPI的中断号为16-31。它们标识CPU核私有的中断源,并且独立于另一个内核上的相同中断源,比如,每个核的计时器。

  • 共享外设中断(SPI,Shared Peripheral Interrupt)。这是由外设生成的,中断控制器可以将其路由到多个核。中断号为32-1020。SPI用于从整个系统可访问的各种外围设备发出中断信号。

GIC分发器 拥有许多寄存器,可以通过它们配置各个中断的属性。这些可配置属性是:

  • 中断优先级:GIC分发器使用它来确定接下来将哪个中断转发到CPU接口。

  • 中断配置:这确定中断是对电平触发还是边沿触发。

  • 中断目标:这确定了可以将中断发给哪些CPU核。

  • 中断启用或禁用状态:只有GIC分发器中启用的那些中断变为挂起状态时,才有资格转发。

  • 中断安全性:确定将中断分配给Secure还是Normal world软件。

  • 中断状态。中断标志位需要软件清除。

  • GIC分发器还提供优先级屏蔽,可防止低于某个优先级的中断发送给CPU核。

处理流程

众多的中断源,汇集于中断管理器,由中断管理器选择优先级最高的中断并通知CPU。CPU会根据中断的类型到跳转到不同的地址处理中断。当CPU核接收到中断时,它会跳转到异常向量表执行。顶层中断处理程序读取CPU接口模块的Interrupt Acknowledge Register,以获取中断ID。除了返回中断ID之外,读取操作还会使该中断在GIC分发器中标记为active状态。一旦知道了中断ID(标识中断源),顶层处理程序就可以根据中断ID来执行相应的处理任务。

当特定于设备的处理程序完成执行时,顶级处理程序将相同的中断ID写入CPU interface模块中的End of Interrupt register中断结束寄存器,指示中断处理结束。除了把当前中断移除active状态之外,这将使最终中断状态变为inactive或pending(如果状态为inactive and pending),这将使CPU interface能够将更多待处理pending的中断转发给CPU核。这样就结束了单个中断的处理。

同一CPU核上可能有多个中断等待服务,但是CPU interface一次只能发出一个中断信号。顶层中断处理程序重复上述顺序,直到读取特殊的中断ID值1023,表明该内核不再有任何待处理的中断。这个特殊的中断ID被称为伪中断ID(spurious interrupt ID),伪中断ID是保留值,不能分配给系统中的任何设备。

再讲一遍,中断信号先到达分发器,分发器根据该中断所设定的CPU,把中断发送到CPU对应的CPU interface上;在CPU interface里判断该中断的优先级是否足够高,能否抢断或打断当前的中断处理,如果可以,CPU interface就会发送一个物理的信号到CPU的IRQ(或FIQ)线上;CPU接收到中断信号,转到中断处理地址进行处理。

初始化流程

复位后,必须初始化GIC,中断才能生效。在初始化中断时,要初始化这4部分:产生中断的源头(GPIO模块或UART模块等)、GIC(内部有Distributor或CPU interface)、CPU本身(设置CPSR寄存器)。

最后提一句,相关的初始化和处理的代码,芯片官方会提供裸机编程的框架,提供基本的所有寄存器及其结构体的 .h 文件,以及相关使用例程代码,用时看懂就行。

前文根据 100ask的《imx6ull裸机编程》部分的 第十章 “异常与中断” 一节 进行简单总结,后面再看100ask的《imx6ull裸机编程》部分的 第十一章 “GPIO中断” 内容可了解裸机编程中的中断部分。

更多内容
  • 中断管理_lgjjeff的博客-CSDN博客。

  • ...


ARM异常处理 & 启动文件的示例

这里根据 100ask的《imx6ull裸机编程》部分的介绍内容,给出一个 比较丰富的、删去无关代码保留中断处理的、注释丰富的一个 启动文件 汇编程序示例。

    @ 本程序仅仅是一个示例
    @ 不同ARM内核的中断向量表不同,具体看手册
    @ 现在的微处理器寄存器结构非常复杂,不建议硬刚芯片手册手写配置代码,直接参考厂家和网络高手的例子特别省事
    @ 一些基础的、不变的、规律性的则必须要会
    
    @ 中断的保存、恢复现场,以及分辨中断号和调用相应中断函数,Cortex M3/M4 是硬件完成的, Cortex A7 是软件实现的
    
    .text                   @ 代码段(.text),表示代码段
                            @ 其他段介绍:
                            @ 只读数据段(.rodata):存放有初始值并且const修饰的全局类变量(全局变量或static修饰的局部变量)
                            @ 数据段(.data):存放有初始值的全局类变量
                            @ 注释段(.comment):存放注释,注释段里面的机器码是用来表示文字的
                            @ 零初始化段(.bss):存放没有初始值或初始值为0的全局类变量
                            @ 注:bss段和注释段不保存在bin/elf文件中,
                            @ 所以如果bss段的数据没有清0的话,没有初始值的变量在初始化时会是随机的,但个人觉得清不清0不是特别重要。
    
    .global start           @ .global 表示 start 是一个全局符号
    
start:                      @ 程序入口
    @异常向量表
    b   reset               @ 0x00 reset
    ldr pc,=_undef          @ 0x04 undef 未定义指令异常
    ldr pc,=_swi_handler    @ 0x08 swi 软中断入口 (如果用 mov 指令有32M地址大小限制)
    ldr pc,=_pre_fetch      @ 0x0c prefetch abort
    ldr pc,=_data_abort     @ 0x10 data abort
    nop                     @ 0x14 reserved
    ldr pc,=_irq            @ 0x18 irq
    ldr pc,=_fiq            @ 0x1c fiq
    
_undef:         .word       _undef
_swi_handler:   .word       _swi_handler
_pre_fetch:     .word       _pre_fetch
_data_abort:    .word       _data_abort
_irq:           .word       _irq
_fiq:           .word       _fiq
​
​
_swi_handler:
    stmfd sp!,{r0-r12,lr}   @ 把 r0-r12 和 lr 寄存器内容进栈,即保护现场
    sub r0,lr,#4            @ 软中断号被保存到 lr 寄存器的上一个指令地址,将其临时保存到 r0
    ldr r0,[r0]             @ 取 r0 保存的地址所指向的值
    bic r0,#0xff000000      @ 根据手册,软中断号被保存在低 24 位,则把高八位内容清 0
    bl swi_user_handle      @ 跳转到 swi_user_handle 软中断用户处理程序,并把下一指令的地址保存到 lr 中
    ldmfd sp!,{r0-r12,pc}^  @ 从栈恢复 r0-r12 寄存器内容,并把原来的 lr 内容 恢复到 pc 中,
                            @ 并从 spsr(cpsr的影子寄存器)恢复到 cpsr,即恢复现场
swi_user_handle:
    @... 软中断的用户应用程序,可以调用 c 函数
    
    cmp r0,#2               @ 判断软中断号是否为2,是则执行后面尾缀带eq的指令
    moveq r7,#2
    
    cmp r0,#5               @ 判断软中断号是否为5,是则执行后面尾缀带eq的指令
    moveq r7,#5
    
    cmp r0,#7               @ 判断软中断号是否为7,是则执行后面尾缀带eq的指令
    moveq r7,#7
    
    ldr pc,lr               @ 跳回
    
_irq:
    sub lr,lr,#4
    stmfd sp!,{r0-r12,lr}
    bl irq_user_handle
    ldmfd sp!,{r0-r12,pc}^
    
irq_user_handle:
    @... 外中断的用户应用程序,可以调用 c 函数
    @并在用户程序中,从中断控制器的寄存器中读出当前的中断号,做相应的相应,然后清中断标志位
    @现在的微处理器寄存器结构非常复杂,不建议硬刚芯片手册手写配置代码,直接参考厂家和网络高手的例子特别省事
    
    ldr pc,lr
​
reset:
    ldr sp,=stack_base      @ 分配栈地址到 sp 寄存器
    msr cpsr,#0x10          @ 切换到 user 模式
    
    @... 用户应用程序,可以调用 c 函数
    
    swi 2                   @ 触发软中断,自动跳转到软中断程序入口
                            @ 并自动把返回地址(下一个指令的地址)保存到 LR 寄存器
                            @ 并自动切换到 SVC(超级用户) 模式
    nop
    nop
    
    
    swi 5
    nop
    nop
    
    
    swi 7
    nop
    nop
    
    /*
        @ 这一段是调用 c 程序里面的 void print_test_string(unsigned int cpsr, char *str) 函数
        @ 给它传入的两个实参为 r0 和 r1
        @ 这时 ARM-THUMB procedure call standard(ARM-Thumb过程调用标准)所规定的
        mrs r0, cpsr
        ldr r1, =test_string
        bl print_test_string
    */
    
    b reset                 @ 返回 reset 地址,大循环
​
/*
        test_string:
            .string "test_string"   
*/
    
    @ 定义栈空间和地址,buf 为栈的开头地址,stack_base 为栈的尾地址,中间有 32 个 word 空间
    @ 我们使用进出栈的指令是 stmfd 和 ldmfd
    @ 这两个指令,为从 stack_base 开始向上递进存,向下递进取的顺序,与这里定义的顺序一致
    
    /*
        ldr sp,=0x80200000
        stmfd sp!, {r0-r2} @ 入栈
        ldmfd sp!, {r0-r2} @ 出栈
        
        结果:
                0x00000000
                ...
                0x801FFFF4  ->  R0
                               R1
                0x80200000  ->  R2  sp指针的移动方向:存向上,取向下
        
        也可以用下面指令,效果一样
        push {r0-r2} @ 入栈
        pop {r0-r2}  @ 出栈
​
    */
    
    /*
        栈的存取方式
        栈的存取方式,为后进先出(LIFO),是由于进出栈指令的作用方式决定的,数据在栈指针处入栈时,
        如 stmfd sp!,{r0-r12,lr},根据这个指令的含义,栈指针sp会根据数据存放的方向自增或自减,出栈的时候过程相反,
        在外界看来,这种方式就是只能在头部进出数据的线性表,从算法上来说是一种特殊的线性表,这种
        方式是由汇编指令和硬件的易实现性所决定的。
        
        堆是一块空闲空间,使用 malloc 函数来管理它,malloc 函数可以自己写
        
        "stack_base:" 这种带冒号的标签表示地址位置,通过其得到指令/数据地址
    */
    .data                   
buf:
    .space 32
stack_base:
    
    
    
    .end
​

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

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

相关文章

【C语言 | 预处理】C语言预处理详解(三)——内存对齐、手把手教你计算结构体大小

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C、数据结构、音视频🍭 🤣本文内容🤣&a…

Elasticsearch:Lucene 中引入标量量化

作者:BENJAMIN TRENT 我们如何将标量量化引入 Lucene。 Lucene 中的自动字节量化 虽然 HNSW 是一种强大而灵活的存储和搜索向量的方法,但它确实需要大量内存才能快速运行。 例如,查询 768 维的 1MM float32 向量大约需要 1,000,000*4*(7681…

Spring Boot 集成 ElasticSearch

1 加入依赖 首先创建一个项目&#xff0c;在项目中加入 ES 相关依赖&#xff0c;具体依赖如下所示&#xff1a; <dependency><groupId>org.elasticsearch</groupId><artifactId>elasticsearch</artifactId><version>7.1.0</version&g…

「算法小记」-2:矩阵链相乘的方案数【迭代/递归/动态规划/区域化DP/记忆化搜索】(C++ )

&#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主、前后端开发、人工智能研究生。公粽号&#xff1a;程序员洲洲。 &#x1f388; 本文专栏&#xff1a;本文…

PHP 使用递归方式 将其二维数组整合为层级树 其中层级id 为一个uuid的格式 造成的诡异问题 已解决

不啰嗦 直接上源代码 <?php function findChildren($list, $p_id){$r array();foreach ($list as $k > $item) {if ($item[fid] $p_id) {unset($list[$k]);$length count($r);$r[$length] $item;if ($t findChildren($list, $item[id])) {$r[$length][children] …

Godot Shader -变量的声明

变量的声明 uniform 可以将值传递给着色器。这些值对整个着色器来说是全局的&#xff0c;被称为 uniform。当一个着色器后来被分配给一个材质时&#xff0c;uniform 将作为可编辑的参数出现在其中。uniform 不能从着色器内部写入。 可以在材质编辑器的修改这些uniform的值&a…

万宾科技内涝积水监测仪使用效果一览

当一个城市突降暴雨&#xff0c;对城市管理部门来讲首当其中的是防止积水成患。随着城市人口快速增长&#xff0c;基础设施建设也日益受到更多的关注&#xff0c;城市内涝问题频繁增加&#xff0c;会给城市带来严重的经济损失和人员的安全问题。城市生命线工程建设过程中&#…

黑客(网络安全)技术——高效自学1.0

前言 前几天发布了一篇 网络安全&#xff08;黑客&#xff09;自学 没想到收到了许多人的私信想要学习网安黑客技术&#xff01;却不知道从哪里开始学起&#xff01;怎么学 今天给大家分享一下&#xff0c;很多人上来就说想学习黑客&#xff0c;但是连方向都没搞清楚就开始学习…

屏幕提词软件Presentation Prompter mac中文版使用方法

Presentation Prompter for mac是一款屏幕提词器软件&#xff0c;它可以将您的Mac电脑快速变成提词器&#xff0c;支持编写或导入&#xff0c;可以在一个或多个屏幕上平滑地滚动&#xff0c;Presentation Prompter 下载是为适用于现场表演者&#xff0c;新闻广播员&#xff0c;…

Ps:选区的基本操作

在 Photoshop 中&#xff0c;选区是为处理局部图像而自行定义的一个区域。 定义选区后&#xff0c;操作被限制在选区之内。选区周围显示的虚线边框&#xff0c;俗称“蚂蚁线”。 全选 Select All Ps菜单&#xff1a;选择/全部 Select/All 快捷键&#xff1a;Ctrl A 提示&#…

JVM字符串常量池StringTable

目录 一、StringTable为什么要调整 二、String的基本特性 三、String的内存分配 四、字符串拼接操作 五、intern()方法 六、Stringtable的垃圾回收 七、G1中String去重操作 一、StringTable为什么要调整 jdk7之前&#xff0c;hotspot对于方法区的实现是永久代&#xff…

C语言之文件操作(详解版)

不知不觉我们已经学到C语言的文件操作部分了&#xff0c;这部分内容其实很有意思&#xff0c;因为它可以直接把我们代码中的数据写入硬盘&#xff0c;而不是我们关掉这个程序&#xff0c;代码就没有了&#xff0c;让我们开始学习吧&#xff01; 目录 1.为什么使用文件 2.什么…

伪造referer [极客大挑战 2019]Http1

打开题目 没有发现什么&#xff0c;我们查看源代码 在这里我们发现了提示 访问一下页面得到 提示说不能来自于https://Sycsecret.buuoj.cn&#xff0c;我们尝试访问一下这个url 发现访问不了 我们bp抓包一下 伪造个referer头 referer:https://Sycsecret.buuoj.cn 发包过去…

ChatGPT、GPT-4 Turbo接口调用

接口地址 https://chat.xutongbao.top/api/light/chat/createChatCompletion 请求方式 post 请求参数 model可选值&#xff1a; “gpt-3.5-turbo-1106”、 “gpt-3.5-turbo-16k” 、 “gpt-4”、“gpt-4-1106-preview”。 默认值为&#xff1a; “gpt-3.5-turbo-1106” to…

Delphi TCP服务端监听端口获取客户端RFID网络读卡器上传的刷卡数据

本示例使用设备介绍&#xff1a;液显WIFI无线网络HTTP协议RFID云读卡器可编程实时可控开关TTS语-淘宝网 (taobao.com) unit Unit1;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, ComCtrls, ScktComp, StdCtrls, ScktCom…

JVM源码剖析之软、弱、虚引用的处理细节

目录 写在前面&#xff1a; 源码剖析&#xff1a; Java层面&#xff1a; JVM层面&#xff1a; 使用危险点&#xff1a; 总结&#xff1a; 版本信息&#xff1a; jdk版本&#xff1a;jdk8u40 垃圾回收器&#xff1a;Serial new/old 写在前面&#xff1a; 不同的垃圾回收…

PROFINET和UDP、MODBUS-RTU通信速度对比实验

这篇博客我们介绍PROFINET 和MODBUS-RTU通信实验时的数据刷新速度,以及这种速度不同对控制系统带来的挑战都有哪些,在介绍这篇对比实验之前大家可以参考下面的文章链接: S7-1200PLC和SMART PLC的PN智能从站通信 S7-200 SMART 和 S7-1200PLC进行PROFINET IO通信-CSDN博客文…

远程电脑未连接显示器时分辨率太小的问题处理

背景&#xff1a;单位电脑显示器坏了&#xff0c;使用笔记本通过向日葵远程连接&#xff0c;发现分辨率只有800*600并且不能修改&#xff0c;网上找了好久找到了处理方法这里记录一下&#xff0c;主要用到的是一个虚拟显示器软件usbmmidd_v2 1)下载usbmmidd_v2 2&#xff09;…

Flink SQL自定义表值函数(Table Function)

使用场景&#xff1a; 表值函数即 UDTF&#xff0c;⽤于进⼀条数据&#xff0c;出多条数据的场景。 开发流程&#xff1a; 实现 org.apache.flink.table.functions.TableFunction 接⼝实现⼀个或者多个⾃定义的 eval 函数&#xff0c;名称必须叫做 eval&#xff0c;eval ⽅法…

什么是进程等待?

什么是进程等待 在了解进程等待之前&#xff0c;我们要回顾一下什么是僵尸进程&#xff1a;是指一个已经终止执行的进程&#xff0c;但其父进程还没有通过 wait() 系统调用来获取该进程的退出状态信息。当一个进程正常退出或者被终止时&#xff0c;其所占用的系统资源会被操作…