历史上的今天
今天是:2024年09月29日(星期日)
2021年09月29日 | Linux ARMv7架构通用中断流程(1)
2021-09-29 来源:eefocus
一、ARMv7 Cortex-A系列处理器寄存器组介绍及其功能介绍
1. ARMv7 Cortex-A处理器一般共有37寄存器,其中包括:
(1) 31个通用寄存器,包括PC(程序计数器)在内,都是32位的寄存器。
(2) 6个状态寄存器,都是32位的寄存器。
2. ARMv7 Cortex-A系列处理器的模式
ARMv7 Cortex-A系列处理器共有7种处理器模式分别是:用户模式(User)、快速中断模式(FIQ)、普通中断模式(IRQ)、管理模式(Supervisor SVC)、数据访问中止模式(Abort)、未定义指令中止模式(Undefined)、系统模式(System)。在每一种处理器模式中有一组相应的寄存器。在任意一种寄存器模式下,可见的寄存器包括15个通用寄存器(R0~R14)、程序计数器(PC)、一个或者两个状态寄存器(CPSR、SPSR)。在所有寄存器中,有些是各个模式共用同一个物理寄存器,有些寄存器是各个模式自己拥有独立的物理寄存器。各种模式下的寄存器组如下入所示。

其中R0~R3主要用于子程序间传递参数,R4~R11主要用于保存局部变量, 但在Thumb程序中,通常智能使用R4~R7来保存局部变量;R12(Intra-Procedure-call scratch register,详细介绍参见"Procedure Call Standard for the ARM Architecture",)用作子程序间的scratch 寄存器,即IP;R13通常用作栈指针,即SP;R14寄存器又被称为连接寄存器,即LR,用于保存子程序以及中断的返回地址;R15用作程序计数器(PC),由于ARM采用流水线机制,PC的值当前正在指令地址加8个字节,即PC指向当前指令的下两条指令地址。CPSR和SPSR都是程序状态寄存器,其中SPSR是用来保存中断前的CPSR中的值,一边在中断返回后恢复处理器状态。
3. CPSR寄存器详解

所有处理器模式下都可访问当前程序状态寄存器CPSR。CPSR中包含条件码标志、中断禁止位、当前处理器模式以及其他状态和控制信息。在每种异常模式下都有一个对应的程序状态寄存器SPSR。当异常出现时,SPSR用于保存CPSR的状态,以便异常返回后恢复异常发生时的工作状态。
(1)条件码标志
程序状态寄存器CPSR的最高4位N、Z、C、V是条件码标志。ARM的大多数指令可条件执行,即通过检测这些条件码标志来决定程序指令如何执行。
各个标志的含义如下:
N: 在结果有符号的二进制补码的情况下,如果结果为负数,则N=1;如果为非负数,则N=0.
Z:如果结果为0,则Z=1;如果结果为非零,则Z=0;
C:其设置分一下几种情况:
(1)对于加法指令(包括比较指令CMN),如果产生进位,则C=1;否则C=0。
(2)对于减法指令(包括比较指令CMP),如果产生借位,则C=0;否则C=1。
(3)对于有移位操作的非法指令,C为移位操作中最后移出位的值。
(4) 对于其他指令,C通常不变。
V:对于加减法指令,在操作数和结果是有符号的整数时,如果发生溢出,则V=1;如果无溢出发生,则V=0;对于其他指令,V通常不发生变化。
(2)控制位的作用在上图中可以看出来。
4. CPSR与CPSR_c的区别
CPSR有4个8位区域:标志域(F)、状态域(S)、扩展域(X)、控制域(C).
C控制域屏蔽字节(CPSR[7:0])
X扩展域屏蔽字节(CPSR[15:8])
S状态域屏蔽字节(CPSR[23:16])
F标志域屏蔽字节(CPSR[31:24])
常用于MRS或MSR指令,用于CPSR的值转移到寄存器或把寄存器的内容加载到CPSR中。如:
MSR CPSR_c , #0xd3
二、Linux ARMv7 Cortex-A系列处理器中断向量表处理和代码分析
1. ARMv7 Cortex-A系列处理器打开关闭irq中断
ARMv7 Cortex-A系列处理器打开关闭irq中断是通过改变CPSR寄存器的bit7位完成的。
开启和关闭当前处理器的本地中断,会产生中断信号,但不处理 。
local_irq_disable()关闭中断指令:cpsid i;
local_irq_enable()开启中断指令:cpsie i;
关闭和开启中断,不会产生中断信号。
disable_irq/enable_irq
2. linux系统为了实现异常处理引入了栈帧的概念
// arch/arm/include/uapi/asm/ptrace.h
/*
* This struct defines the way the registers are stored on the
* stack during a system call. Note that sizeof(struct pt_regs)
* has to be a multiple of 8.
*/
#ifndef __KERNEL__
struct pt_regs {
long uregs[18];
};
#endif /* __KERNEL__ */
#define ARM_cpsr uregs[16]
#define ARM_pc uregs[15]
#define ARM_lr uregs[14]
#define ARM_sp uregs[13]
#define ARM_ip uregs[12]
#define ARM_fp uregs[11]
#define ARM_r10 uregs[10]
#define ARM_r9 uregs[9]
#define ARM_r8 uregs[8]
#define ARM_r7 uregs[7]
#define ARM_r6 uregs[6]
#define ARM_r5 uregs[5]
#define ARM_r4 uregs[4]
#define ARM_r3 uregs[3]
#define ARM_r2 uregs[2]
#define ARM_r1 uregs[1]
#define ARM_r0 uregs[0]
#define ARM_ORIG_r0 uregs[17]
//arch/arm/kernel/asm-offsets.c
DEFINE(S_R0, offsetof(struct pt_regs, ARM_r0));
DEFINE(S_R1, offsetof(struct pt_regs, ARM_r1));
DEFINE(S_R2, offsetof(struct pt_regs, ARM_r2));
DEFINE(S_R3, offsetof(struct pt_regs, ARM_r3));
DEFINE(S_R4, offsetof(struct pt_regs, ARM_r4));
DEFINE(S_R5, offsetof(struct pt_regs, ARM_r5));
DEFINE(S_R6, offsetof(struct pt_regs, ARM_r6));
DEFINE(S_R7, offsetof(struct pt_regs, ARM_r7));
DEFINE(S_R8, offsetof(struct pt_regs, ARM_r8));
DEFINE(S_R9, offsetof(struct pt_regs, ARM_r9));
DEFINE(S_R10, offsetof(struct pt_regs, ARM_r10));
DEFINE(S_FP, offsetof(struct pt_regs, ARM_fp));
DEFINE(S_IP, offsetof(struct pt_regs, ARM_ip));
DEFINE(S_SP, offsetof(struct pt_regs, ARM_sp));
DEFINE(S_LR, offsetof(struct pt_regs, ARM_lr));
DEFINE(S_PC, offsetof(struct pt_regs, ARM_pc));
DEFINE(S_PSR, offsetof(struct pt_regs, ARM_cpsr));
DEFINE(S_OLD_R0, offsetof(struct pt_regs, ARM_ORIG_r0));
DEFINE(S_FRAME_SIZE, sizeof(struct pt_regs));
3. ARM异常处理类型和模式
ARM的各种异常类型和每种异常类型处于的处理器模式,如下表所示:

4. ARM中断处理汇编代码分析(基于LINUX 4.4.49内核分析)
4.1 arm中断处理总入口
//arch/arm/kernel/entry-armv.S
/*
*注释:
* 1)ARM架构异常处理向量表起始地址__vectors_start(定义在arch/arm/kernel/vmlinux.lds).
* 2)ARM架构定义7种异常包括中断、系统调用、缺页异常等,发生异常时处理器会跳转到相应入口。
* 3)异常向量表的起始位置由CP15协处理器的控制寄存器C1的bit13决定:
* v=0,Normal exception vectors, base address 0x00000000. Software can remap this
* base address using the VBAR(CP15 C12寄存器);
* v=1,High exception vectors, base address 0xFFFF0000-0xFFFF001C. This base address
* is never remapped.
* The primary input VINITHI defines the reset value of the V bit.
* VINITHI: Controls the location of the exception vectors at reset:
* 0 = starts exception vectors at address 0x00000000
* 1 = starts exception vectors at address 0xFFFF0000.
* This pin is only sampled during reset of the processor
*/
.section .vectors, "ax", %progbits
__vectors_start:
W(b) vector_rst
W(b) vector_und
/*
*系统调用入口点:
* __vectors_start + 0x1000 = __stubs_start(由arch/arm/kernel/vmlinux.lds链接脚本可知)
* 此时PC指向系统调用异常的处理入口:vector_swi用户态通过swi指令产生软中断。因为vector_swi系统
* 调用异常代码在(arch/arm/kernel/entry-common.S),其入口地址与异常向量相隔较远,使用b指令无
* 法跳转过去。b指令只能相对当前PC跳转 +/-32M范围)。
*/
W(ldr) pc, __vectors_start + 0x1000
W(b) vector_pabt //取指令异常
W(b) vector_dabt //数据异常--缺页异常
W(b) vector_addrexcptn
W(b) vector_irq //irq中断异常
W(b) vector_fiq
4.2 以vector_irq为例进行深入分析
vector_irq是通过vector_stub宏定义的,vector_stub宏定义尤为关键,ARM任何异常都是通过将r0,lr,spsr保存到异常模式的栈中(每种异常模式都有自己的栈,栈的初始化在cpu_init,见下面分析), vector_stub通过vector_name实现其功能。
//arch/arm/kernel/entry-armv.S
/*
*注释:
*当irq发生时,硬件自动完成如下操作:
*1. arm在irq模式下有自己的lr寄存器lr_irq、spsr_irq、sp_irq.
* r14_irq = lr_irq = address pf next instruction to be executed+4;
*2. spsr_irq = cpsr,保存了处理器当前的状态,中断屏蔽位以及各种条件标志位,保存后cpsr会切换到
* irq模式。
*3. cpsr[4 :0] = 0b10010,设置arm为irq模式
*4. cpsr[5] = 0,arm状态执行
*5. cpsr[7] = 1,禁止irq
*6. pc = 0xffff0018(High exception vectors,取决于CP15协处理器的C1寄存器的配置,参看上面的分
* 析), 将pc值设置成异常中断的中断向量地址,即vectot_irq.
*/
/*
* Interrupt dispatcher
*/
vector_stub irq, IRQ_MODE, 4
.long __irq_usr @ 0 (USR_26 / USR_32) 从用户态下进入的irq,执行__irq_usr代码
.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,执行__irq_svc代码
.long __irq_invalid @ 4
.long __irq_invalid @ 5
.long __irq_invalid @ 6
.long __irq_invalid @ 7
.long __irq_invalid @ 8
.long __irq_invalid @ 9
.long __irq_invalid @ a
.long __irq_invalid @ b
.long __irq_invalid @ c
.long __irq_invalid @ d
.long __irq_invalid @ e
.long __irq_invalid @ f
sp在不同的模式下有不同寄存器,在cpu_init中进行初始化。
/* arch/arm/kernel/setup.c
* cpu_init - initialise one CPU.
*
* cpu_init sets up the per-CPU stacks.
*/
void notrace cpu_init(void)
{
#ifndef CONFIG_CPU_V7M
unsigned int cpu = smp_processor_id();
struct stack *stk = &stacks[cpu];
if (cpu >= NR_CPUS) {
pr_crit("CPU%u: bad primary CPU numbern", cpu);
BUG();
}
/*
* This only works on resume and secondary cores. For booting on the
* boot cpu, smp_prepare_boot_cpu is called after percpu area setup.
*/
set_my_cpu_offset(per_cpu_offset(cpu));
cpu_proc_init();
/*
* Define the placement constraint for the inline asm directive below.
* In Thumb-2, msr with an immediate value is not allowed.
*/
#ifdef CONFIG_THUMB2_KERNEL //此宏为定义
#define PLC "r"
#else
#define PLC "I" //表示是立即数,其他定义可以查看GCC ARM C语言嵌入汇编语法
#endif
/*
* setup stacks for re-entrant exception handlers
* 修改几种模式下的sp指向struct stack结构体类型变量stacks中定义的各个变量,每种模式下的栈为3个字
*/
__asm__ (
"msr cpsr_c, %1nt" //msr cpsr_c,(PSR_F_BIT|PSR_I_BIT|IRQ_MODE)切换为irq模式
"add r14, %0, %2nt" //r14 = r +offset(struct stack, irq[0])
"mov sp, r14nt"
"msr cpsr_c, %3nt" //msr cpsr_c,(PSR_F_BIT|PSR_I_BIT|ABT_MODE)切换为abt模式
"add r14, %0, %4nt" //r14 = r +offset(struct stack, abt[0])
"mov sp, r14nt"
"msr cpsr_c, %5nt" //msr cpsr_c,(PSR_F_BIT|PSR_I_BIT|UND_MODE)切换为und模式
"add r14, %0, %6nt" //r14 = r +offset(struct stack, und[0])
"mov sp, r14nt"
"msr cpsr_c, %7nt" //msr cpsr_c,(PSR_F_BIT|PSR_I_BIT|FIQ_MODE)切换为fiq模式
"add r14, %0, %8nt" //r14 = r +offset(struct stack, fiq[0])
"mov sp, r14nt"
"msr cpsr_c, %9" //msr cpsr_c,(PSR_F_BIT|PSR_I_BIT|FIQ_MODE)切换为svc模式
:
: "r" (stk),
史海拾趣
|
我在VS2005下已做好WM5的项目,可转WM2003编译供WM2003的PDA使用,但找不到选项更改?! 我想到的笨办法是要另建一个WM2003的同名项目,再把WM5的项目中的同名文件覆盖过来,然后在项目不添加后加的文件,但工作量好象不小。 特此请教!… 查看全部问答> |
|
我想设计一个电路用来测量三角波的幅度,频率,以及斜率,我觉得对幅度来说可以用数模转换,在不同的幅度输出不同的值;在频率方面,用时钟电路来做计时,在一个波完时再对时钟中断;而在斜率方面,就不太清楚了,也不知道对不对,请给个方向吧!但 ...… 查看全部问答> |
|
请问大家.半导体集成,它的集成程度分为小规模集成,中规模集成,大规模集成,超大规模集成,特大规模集成,巨大规模集成.请问是如何区分的.请举个具体型号的例子.谢谢 [ 本帖最后由 jirongchang 于 2010-8-26 13:15 编辑 ]… 查看全部问答> |




