历史上的今天
今天是:2025年06月04日(星期三)
2018年06月04日 | ARM中断向量表与响应流程
2018-06-04 来源:eefocus
一首先中断向量表定义在哪里?如何加载?
二 中断向量表与中断服务程序
三处理流程
////////////////////////////////////////////////////////////////////////////////////////////////////
一首先中断向量表定义在哪里?__vectors_start
首先中断向量表定义的是什么?定义的就是中断服务程序的跳转指令,因为每个中断向量在向量表中只有一个字节的存储空间,只能存放一条指令,所以通常存放跳转指令,使程序跳转到存储器的其他地方,再执行中断处理。这里cpu就可以找中断服务程序,跳转指令如例如:
LDRPC, =ISR_HANDLER;
或者
指令与不同的cpu平台有关系。
1.1 vector表定义的方式:往往是变量地址:
如 .
.globl __vectors_start 定义__vectors_start符号,这样外部程序可以访问到。entry-armv.S
__vectors_start:
swi SYS_ERROR0
b vector_und + stubs_offset
ldr pc, .LCvswi + stubs_offset
b vector_pabt + stubs_offset
b vector_dabt + stubs_offset
b vector_addrexcptn + stubs_offset
b vector_irq + stubs_offset
b vector_fiq + stubs_offset
ARM的异常处理向量表在entry-armv.S文件中:
1.2 中断向量表 类型
From ARM
.globl __vectors_start 定义__vectors_start符号,这样外部程序可以访问到。
__vectors_start:定义异常(地址逻辑自上而下0x00----0x1c)跟具体的cpu特性有关
ARM( swi SYS_ERROR0 )向量0:reset,但是这里被修改了,如果是cpu跑到了0地址,用软件中断SYS_ERROR0来处理.
THUMB( svc #0 )向量1
THUMB( nop )向量2
W(b) vector_und+ stubs_offset 向量3 #未定义指令异常
W(ldr) pc,.LCvswi + stubs_offset 向量4#软中断
W(b) vector_pabt+ stubs_offset #向量5指令预取异常中断(Prefetch Abort)
W(b) vector_dabt+ stubs_offset #向量6数据中止
W(b) vector_addrexcptn+ stubs_offset #向量7地址异常Thesearen't too critical.
W(b) vector_irq+ stubs_offset #向量8.IRQ(一般中断)
W(b) vector_fiq+ stubs_offset #向量9 FIQ(快速中断)
/*关于.globl指令:
.global/.globl 命令
.global symbol
.global 使得连接程序(ld)能够识别symbl
声明symbol是全局可见的。标号_start是GNU链接器用来指定第一个要执行指令所必须的,同样的是全局可见的(并且只能出现在一个模块中)
例如:
.global_start #定义_start为外部程序可以访问的标签
__vectors_start符号,又存放在哪里呢?
有不同的方式,可以指定加载的ram地址,如\kernel\arch\c6x\kernel平台
SECTIONS
{
/*
* Start kernel read only segment
*/
READONLY_SEGMENT_START
.vectors :
{
_vectors_start =.;
*(.vectors)
. = ALIGN(0x400);
_vectors_end = .;
}
指定好了vector在内核镜像加载到内存后的地址0x400;
但是arm就不指定,如下,在启动之后存放的地址:
//中断服务处理程序
c000b500 T__kuser_helper_start
c000b500 t__kuser_memory_barrier
c000b520 t__kuser_cmpxchg
c000b540 t__kuser_get_tls
c000b55c t__kuser_helper_version
c000b560 T__kuser_helper_end
c000b560 T __stubs_start //中断服务处理程序
c000b560 tvector_irq
c000b5e0 tvector_dabt
c000b660 tvector_pabt
c000b6e0 tvector_und
c000b760 tvector_fiq
c000b764 tvector_addrexcptn
c000b784 T__stubs_end
c000b784 T __vectors_start中断向量表的起始地址 32字节
c000b7a4 T__vectors_end
2.其次 向量表在系统bootup的时候被链接在哪里?
/out/target/product/huaqin82_cwet_kk/obj/KERNEL_OBJ/arch/arm/kernel/entry-armv.o 打包成build-in.o
3,最后内核建立向量表vector的拷贝
__trap_init函数填充后的向量表如下:
虚拟地址 | 异常 | 处理汇编代码 |
0xffff0000 | reset swi | SYS_ERROR0 |
0xffff0004 | undefined | b __real_stubs_start + (vector_undefinstr - __stubs_start) |
0xffff0008 | 软件中断 | ldr pc, __real_stubs_start + (.LCvswi - __stubs_start) |
0xffff000c | 取指令异常 | b __real_stubs_start + (vector_prefetch - __stubs_start) |
0xffff0010 | 数据异常 | b __real_stubs_start + (vector_data - __stubs_start) |
0xffff0014 | reserved | b __real_stubs_start + (vector_addrexcptn - __stubs_start) |
0xffff0018 | irq | b __real_stubs_start + (vector_IRQ - __stubs_start) |
0xffff001c | Fiq | b __real_stubs_start + (vector_FIQ - __stubs_start) |
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
二 中断向量表与中断服务程序
总的来说对于中断向量表的定义和存放(加载)和处理流程如下:
首先理解相关概念:
中断控制器 | 负责(1)屏蔽和过滤中断信号(2)唤醒cpu。 分为向量中断模式和非向量中断模式: ---向量中断模式用于RESET、NMI、异常处理。当向量中断产生时,控制器直接将PC赋值,如IRQ异常 跳到0x0000000d处,而在0x0000000d地址处通常放置ISR服务程序地址LDR PC, =ISR_HANDLER。
---非向量中断模式,有一个寄存器标识位,跳转到统一的函数地址,此函数通过判别寄存器标识位和优先级关系进行中断-处理。 |
跳转指令: | 我分为两种: 1是中断控制器的跳转指令(实际上编译好的机器码):为何需要? 因为当cpu在中断发生的时候,cpu如何知道把pc指针执行哪里去执行指令呢。所以通过中断控制器的跳转指令帮助把cpu的执行指针pc,执行相应的中断向量表。
2是cpu相关的跳转指令,如arm处理器:b bl ,ldr等:完成跳转到不同的中断服务处理程序。
|
1)中断服务程序 定义在哪里?如arm的dataabort异常处理程序:
首先跳转指令:b vector_dabt + stubs_offset ---->这个地址的指令定义也在entry-armv.S:
vector_stub dabt, ABT_MODE,8
----》__dabt_svc (内核模式发生dataabort) 或者 __dabt_usr(用户模式发生dataabort)
-----》dabt_helper是一个宏--->bl CPU_DABORT_HANDLER
2)存放(加载)的地址?中断向量表定义好了之后,存放了ram的哪里呢?也就是__vectors_start存在内存什么地址?
答案:可以定在你需要的任何可访问ram地址(这里指的虚拟地址,不是物理ram地址)。
例子1 :单片机
非向量中断模式
假定非向量中断表定义在0x00400000开始的外部RAM空间:
引用网络 图2 中断解析示例流程
图2中实线表示的流程都用ARM汇编语言编写,一般作为boot代码的一部分放在系统的底层模块中。
填写向量表的操作可以在上层应用程序中方便地实现,比如在C语言中: *( int *(0x00400018)) = (int) ISR_IRQ;
这样就将IRQ中断的服务程序入口地址(0x00300260)填写到中断向量表中的固定地址0x00400018开始的4字节空间了。
简单说就是:
在0x00000018的地址的跳转指令是:B0x00000600 ;
而0x00000600存放的指令是:ldr r0 =0x004000018;
而0x004000018 存放的是0x00300260:=中断的服务程序ISR_IRQ的入口地址(0x00300260)
例子2:
ARM 的vector表是存放在
c000b500 T__kuser_helper_start
c000b500 t__kuser_memory_barrier
c000b520 t__kuser_cmpxchg
c000b540 t__kuser_get_tls
c000b55c t__kuser_helper_version
c000b560 T__kuser_helper_end
c000b560 T __stubs_start
c000b560 tvector_irq
c000b5e0 tvector_dabt
c000b660 tvector_pabt
c000b6e0 tvector_und
c000b760 tvector_fiq
c000b764 tvector_addrexcptn
c000b784 T__stubs_end
c000b784 T __vectors_start中断向量表的起始地址
c000b7a4 T __vectors_end
内核建立vector的拷贝
__trap_init函数填充后的向量表如下:
虚拟地址 | 异常 | 处理汇编代码 |
0xffff0000 | reset swi | SYS_ERROR0 |
0xffff0004 | undefined | b __real_stubs_start + (vector_undefinstr - __stubs_start) |
0xffff0008 | 软件中断 | ldr pc, __real_stubs_start + (.LCvswi - __stubs_start) |
0xffff000c | 取指令异常 | b __real_stubs_start + (vector_prefetch - __stubs_start) |
0xffff0010 | 数据异常 | b __real_stubs_start + (vector_data - __stubs_start) |
0xffff0014 | reserved | b __real_stubs_start + (vector_addrexcptn - __stubs_start) |
0xffff0018 | irq | b __real_stubs_start + (vector_IRQ - __stubs_start) |
0xffff001c | Fiq | b __real_stubs_start + (vector_FIQ - __stubs_start) |
---为何内核要拷贝到0xffff0000?这个是arm cpu的规定:对于ARMv4及其以上的版本,异常向量表的起始位置由协处理器15(cp15)的控制寄存器(c1)里的V位(bit13)有关,当V=0时,异常向量表的起始位置在0x00000000,而当V=1时,异常向量表就起始于0xffff0000位置。当有异常发生时,处理器会跳转到对应的0xffff0000起始的向量处取指令,然后,通过b指令散转到异常处理代码.因为ARM中b指令是相对跳转,而且只有+/-32MB的寻址范围,所以把__stubs_start~__stubs_end之间的异常处理代码复制到了0xffff0200起始处.这里可直接用b指令跳转过去,这样比使用绝对跳转(ldr)效率高。
三处理流程?cpu发生中断的时候,PC指针如何知道到0x000000-0x0000001c(linux内核copy到0xffff0000)的 地址(也就是到中断向量表vector中哪一种异常:swi,数据异常,irq等)去执行中断跳转指令呢?
答案是:中断控制器完成。如下:
上图(来自网络ppt)
向量中断模式用于RESET、NMI、异常处理。当向量中断产生时,控制器直接将PC赋值,如跳到0x0000000d处,而在0x0000000d地址处通常放置ISR服务程序地址。
处理流程分为两部分:如下
1。硬件部分:EINT orIRQ硬件信号-----》中断控制器跳转---到对应的异常----(硬件do it)-----》改变pc指针的地址------》
2。软件部分:中断向量表跳转指令(如b __real_stubs_start)-------》对应的中断处理程序,比如一般的irq流程 ---》entry-armv.S@ -----》vector_stub irq,IRQ_MODE, 4
-).macrovector_stub, name, mode, correction=0(完成中断现场保护,CPU异常模式切换)
-) 根据进入中断前的工作模式不同,程序下一步将跳转到_irq_usr、或__irq_svc等位置
.long __irq_usr @ 0 (USR_26 / USR_32)
.long __irq_invalid @ 1 (FIQ_26 / FIQ_32)
.long __irq_invalid @ 2 (IRQ_26 / IRQ_32)
.long __irq_svc @ 3 (SVC_26 / SVC_32)
----》__irq_usr定义如下:
__irq_usr:
usr_entry
kuser_cmpxchg_check
irq_handler
get_thread_infotsk
mov why,#0
b ret_to_user_from_irq
UNWIND(.fnend )
ENDPROC(__irq_usr)
-----》irq_handler定义如下:
.macro irq_handler
#ifdefCONFIG_MULTI_IRQ_HANDLER
ldr r1,=handle_arch_irq
mov r0,sp
adr lr,BSYM(9997f)
ldr pc,[r1]
#else
arch_irq_handler_default
#endif
----》arm/include/asm/entry-macro-multi.S:6:@ .macro arch_irq_handler_default:
.macro arch_irq_handler_default
get_irqnr_preambler6, lr
1: get_irqnr_and_base r0, r2, r6, lr
#get_irqnr_and_base函数完成获取IRQ中断号(irq number),依赖不同的soc的中断控制器
movne r1,sp
@
@ routine calledwith r0 = irq number, r1 = struct pt_regs *
@
adrne lr,BSYM(1b)
bne asm_do_IRQ
/*get_irqnr_and_base实现是依赖具体的硬件的,对于pxa270 cpu,其实现如下:
.macro get_irqnr_and_base, irqnr, irqstat, base, tmp
mov /base,#io_p2v(0x40000000) @ IIR Ctl = 0x40d00000
add /base, /base,#0x00d00000
ldr /irqstat,[/base, #0] @ ICIP
ldr /irqnr,[/base, #4] @ ICMR
ands /irqstat,/irqstat, /irqnr
beq 1001f /* 没找到中断,跳转*/
rsb /irqnr,/irqstat, #0
and /irqstat,/irqstat, /irqnr
clz /irqnr,/irqstat
rsb /irqnr,/irqnr, #(31 - PXA_IRQ_SKIP)
#ifdefCONFIG_CPU_BULVERDE
b 1002f
#endif
1001:
1002:
.endm
.macroirq_prio_table
.endm
*/
接着---》asm_do_IRQ:-->handle_IRQ()------>执行request_irq()注册的中断。
补充,EINT 是共享一个IRQ,所以要到对应的IRQ handle里面,再处理不同的EINT handler,,如MTK
void mt_eint_registration(unsignedint eint_num, unsigned int flag,#eint注册到IRQ:MT_EINT_IRQ_ID
void(EINT_FUNC_PTR) (void), unsigned int is_auto_umask)
{
。。。。。。
EINT_FUNC.eint_func[eint_num] = EINT_FUNC_PTR;
spin_unlock(&eint_lock);
EINT_FUNC.eint_auto_umask[eint_num] = is_auto_umask;
mt_eint_ack(eint_num);
mt_eint_isr(){
...
...
if(EINT_FUNC.eint_func[index]) {
EINT_FUNC.eint_func[index] ();
上一篇:S3C2440之看门狗
史海拾趣
|
MINI_STM32开发板(最小系统核心板) 主板配置: 1、 CPU:STM32F103RBT6,ARM Cortex-M3内核,128kB Flash, 20KB RAM,最高工作时钟72MHz,64脚 2、 USB接口,可以做USB实验 3、 RS232(ISP下载)包括串口电平转换芯片SP3232,可做RS232通信实验 4 ...… 查看全部问答> |
|
请教各位,我目前有个X86下的BSP安装包,安装后sysgen发现产生了一个too many errors 的错误 后来又得到了一个BSP的原文件,但是没有CEC文件 请问我怎么能把这个BSP的原文件添加到PB中,可以制订NK工程?? 注:WINCE50下!… 查看全部问答> |
|
这个是我把100MHZ的分成10MHZ的程序 我想把修改下 能把2MHZ的分成1HZ的 望各位指教 library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use ieee.std_logic_unsigned.all; entity df is port (rese ...… 查看全部问答> |
|
Hplips LPC 2214 ISP下载的上位机程序谁有???? Hplips LPC 2214 ISP下载的上位机程序谁有? 能不能给我个Demo Email:zengkun258@126.com… 查看全部问答> |
|
系统是IXP425做主处理器,外挂了2个网口,分别接NPEA、B,接口方式MII,PHy用的是SMSC的 LAN8700问题:如果我把PHY配置成10M的,通过PC ping我的板子,没有问题,下载也没有问题。但是把PHY配置成100M,PC就ping不通板子,板子当然也pin ...… 查看全部问答> |
|
现在要做一个项目,开发工具是EVC,PDA系统是WCE.NET 5.0,PDA上有CDMA无线通讯模块,我想实现PDA远程访问ACCESS数据库,请问前辈们具体要哪些数据库开发工具和步骤。有开发过的同志指点一二,在此不盛感谢。sqlce可以访问access数据库吗?… 查看全部问答> |
|
大家好!我们现在正在开发一套会员管理系统,需要使用射频读写器读写非接触式ic卡,多个卡机并联联网,现在想要一些产品的接口函数、范例、演示程序,关键是想知道如何实现多机联网的应用(数据发送时如何根据机号接受),有产品能满足要求吗?接口 ...… 查看全部问答> |
|
同样一个C语言式子,只是一个采用16进制,一个采用10进制,为何反汇编代码不同? 各位前辈们是否有遇到这种问题?是什么原因呢。 for example: void a(void) { unsigned int MAX_C, MIN_C; &n ...… 查看全部问答> |
|
前段时间工作上的事情确实有点多,拿到板子有一段时间了!确实是玩玩的时候了! 先发个贴子,占个位子,督促一下自己,慢慢更新 常用的软件与资料地址: http://software-dl.ti.com/tiva-c/SW-TM4C/latest/index_FDS.html [ 本帖最后由 wuyanyan ...… 查看全部问答> |




