精华内容
下载资源
问答
  • 6. ARM中断处理过程

    2020-12-23 01:30:09
    具体整个处理过程分成三步骤来描述:1、第二章描述了中断处理的准备过程2、第三章描述了当发生中的时候,ARM硬件的行为3、第章描述了ARM的中断进入过程4、第五章描述了ARM的中断退出过程本文涉及的代码来自3.14...

    1 前言

    2 中断处理的准备过程

    2.1 中断模式的stack准备

    1 前言

    本文主要以ARM体系结构下的中断处理为例,讲述整个中断处理过程中的硬件行为和软件动作。具体整个处理过程分成三个步骤来描述:

    1、第二章描述了中断处理的准备过程

    2、第三章描述了当发生中的时候,ARM硬件的行为

    3、第四章描述了ARM的中断进入过程

    4、第五章描述了ARM的中断退出过程

    本文涉及的代码来自3.14内核。另外,本文注意描述ARM指令集的内容,有些source code为了简短一些,删除了THUMB相关的代码,除此之外,有些debug相关的内容也会删除。

    2 中断处理的准备过程

    2.1 中断模式的stack准备

    ARM处理器有多种processor mode,例如user mode(用户空间的AP所处于的模式)、supervisor mode(即SVC mode,大部分的内核态代码都处于这种mode)、IRQ mode(发生中断后,处理器会切入到该mode)等。对于linux kernel,其中断处理处理过程中,ARM 处理器大部分都是处于SVC mode。但是,实际上产生中断的时候,ARM处理器实际上是进入IRQ mode,因此在进入真正的IRQ异常处理之前会有一小段IRQ mode的操作,之后会进入SVC mode进行真正的IRQ异常处理。由于IRQ mode只是一个过渡,因此IRQ mode的栈很小,只有12个字节,具体如下:

    [arch/arm/kernel/setup.c]

    struct stack {

    u32 irq[3];

    u32 abt[3];

    u32 und[3];

    } ____cacheline_aligned;

    static struct stack stacks[NR_CPUS];

    除了irq mode,linux kernel在处理abt mode(当发生data abort exception或者prefetch abort exception的时候进入的模式)和und mode(处理器遇到一个未定义的指令的时候进入的异常模式)的时候也是采用了相同的策略。也就是经过一个简短的abt或者und mode之后,stack切换到svc mode的栈上,这个栈就是发生异常那个时间点current thread的内核栈。anyway,在irq mode和svc mode之间总是需要一个stack保存数据,这就是中断模式的stack,系统初始化的时候,cpu_init函数中会进行中断模式stack的设定:

    void notrace cpu_init(void)

    {

    unsigned int cpu = smp_processor_id();------获取CPU ID

    struct stack *stk = &stacks[cpu];---------获取该CPU对于的irq abt和und的stack指针

    ……

    #ifdef CONFIG_THUMB2_KERNEL

    #define PLC "r"------Thumb-2下,msr指令不允许使用立即数,只能使用寄存器。

    #else

    #define PLC "I"

    #endif

    __asm__ (

    "msr cpsr_c, %1\n\t"------让CPU进入IRQ mode

    "add r14, %0, %2\n\t"------r14寄存器保存stk->irq

    "mov sp, r14\n\t"--------设定IRQ mode的stack为stk->irq

    "msr cpsr_c, %3\n\t"

    "add r14, %0, %4\n\t"

    "mov sp, r14\n\t"--------设定abt mode的stack为stk->abt

    "msr cpsr_c, %5\n\t"

    "add r14, %0, %6\n\t"

    "mov sp, r14\n\t"--------设定und mode的stack为stk->und

    "msr cpsr_c, %7"--------回到SVC mode

    :--------------------上面是code,下面的output部分是空的

    : "r" (stk),----------------------对应上面代码中的%0

    PLC (PSR_F_BIT | PSR_I_BIT | IRQ_MODE),------对应上面代码中的%1

    "I" (offsetof(struct stack, irq[0])),------------对应上面代码中的%2

    PLC (PSR_F_BIT | PSR_I_BIT | ABT_MODE),------以此类推,下面不赘述

    "I" (offsetof(struct stack, abt[0])),

    PLC (PSR_F_BIT | PSR_I_BIT | UND_MODE),

    "I" (offsetof(struct stack, und[0])),

    PLC (PSR_F_BIT | PSR_I_BIT | SVC_MODE)

    : "r14");--------上面是input操作数列表,r14是要clobbered register列表

    }

    展开全文
  • UCOSII的中断过程简介系统接收到中断请求后,如果CPU处于开中断状态,系统就会中止正在运行的当前任务,而按中断向量的指向去运行中断服务子程序,当中断服务子程序运行完成后,系统会根据具体情况返回到被中止的...

    一.   UCOSII的中断过程简介

    系统接收到中断请求后,如果CPU处于开中断状态,系统就会中止正在运行的当前任务,而按中断向量的指向去运行中断服务子程序,当中断服务子程序运行完成后,系统会根据具体情况返回到被中止的任务继续运行,或转向另一个中断优先级别更高的就绪任务。

    由于UCOS II是可剥夺型的内核,所以中断服务程序结束后,系统会根据实际情况进行一次任务调度,如果有优先级更高的任务,就去执行优先级更高的任务,而不一定要返回被中断了的任务。

    二.UCOSII的中断过程的示意图

    三.具体中断过程

    1.中断到来,如果被CPU识别,CPU将查中断向量表,根据中断向量表,获得中断服务子程序的入口地址。

    2.将CPU寄存器的内容压入当前任务的任务堆栈中(依处理器的而定,也可能压入被压入被中断了的任务堆栈中。

    3.通知操作系统将进入中断服务子程序。即:调用OSIntEnter()或OSIntNesting直接

    加1。

    4.If(OSIntNesting==1) {OSTCBCur->OSTCBStrPtr=SP;} //如果是第一层中断,则将堆栈指针保存到被中断任务的任务控制块中

    5.清中断源,否则在开中断后,这类中断将反复的打入,导致系统崩贵

    6.执行用户ISR

    7.中断服务完成后,调用OSIntExit().如果没有高优先级的任务被中断服务子程序激活而进入就绪态,那么就执行被中断了的任务,且只占用很短的时间.

    8.恢复所有CPU寄存器的值.

    9.执行中断返回指令.

    四.相关代码

    与编译器相关的数据类型:

    typedef unsigned char BOOLEAN;

    typedef unsigned char INT8U;

    typedef unsigned int OS_STK; //堆栈入口宽度为16 位

    (一) void  OSIntEnter (void)的理解

    uCOS_II.H中定义:

    #ifdef   OS_GLOBALS

    #define  OS_EXT

    #else

    #define  OS_EXT  extern

    #endif     //定义全局宏OS_EXT

    #ifndef  TRUE

    #define  TRUE   1

    #endif

    OS_EXT  BOOLEAN   OSRunning; //定义外部BOOLEAN类型全局变量,用来指示

    //核是否在运行

    OS_EXT  INT8U   OSIntNesting;//定义外部8位无符号整型数全局变量,用来表

    //示中断嵌套层数

    OS_CORE.C中的OSIntEnter()函数原型:

    void  OSIntEnter (void)

    {

    if (OSRunning == TRUE) //如果内核正在运行则进入if

    {

    if (OSIntNesting < 255) //如果嵌套层数小于255,则可以继//续

    {

    OSIntNesting++; //嵌套层数加1

    }

    }

    }

    (二)在中断服务子程序中加if ( OSIntNesting == 1){…}的原因

    uCOS_II.H中定义:

    typedef struct os_tcb {

    OS_STK    *OSTCBStkPtr;//声明指向任务堆栈栈顶的16位指针

    ………………

    } OS_TCB;//定义名为OS_TCB的结构体数据类型,即任务控制块的数据结构

    OS_EXT  OS_TCB   *OSTCBCur;//声明一个指向任务控制块的全局指针变量

    //用于指向当前任务的任务控制块

    中断服务程序中添加的代码:

    if ( OSIntNesting == 1)

    {

    OSTCBCur->OSTCBStkPtr = SP; // 如果是第一层中断,则将被中断任务

    //的堆栈指针保存在被中断任务的任务

    //任务控制块中

    }

    关于uCOS-II的中断服务程序(ISR)中必须加“OSIntNesting == 1”的原因 ==避免调整堆栈指针.

    出现这个问题的根源是当低优先级的任务被中断,当中断完成后由于有高优先级的任务就绪,则必须调度高优先级的任务,原来的低优先级任务继续被中断着,但是此时的低优先级任务的堆栈已经被破坏,已不能被调度程序直接调度了,要想被调度而必须调整堆栈指针。如下图所示的场景:

    问题分析:   要想理解加上上面两句的原因,不妨假设有下面场景出现:void MyTask(void)

    {

    ...

    }该任务在执行过程中被中断打断,下面是它的服务子程序

    void MyISR(void)

    {保存现场(PUSHA)

    OSIntEnter();

    //此时的堆栈指针是正确的,再往下就不对了,应该在此处保存用户任务堆栈指针 OSIntExit();恢复现场(POPA)    中断返回}

    OSIntExit(),大体如下:

    OSIntExit()

    {

    OS_ENTER_CRITICAL();

    if( OSIntNesting==0 && OSLockNesting == 0 ) {找到目前系统中就绪表中优先级最的任务      如果不是当前任务,则调度它执行OSIntCtxSw();

    }

    OS_EXIT_CRITICAL();

    }

    综上所述,任务调用链如下:MyTask --> MyISR -->

    ①     OSIntExit -->

    ②        OS_ENTER_CRITICAL(); ③

    OSIntCtxSw();        ④

    然而在实际的移植过程中,需要调整的指针偏移量是与编译器相关的,如果想要避免调整,显然一个简单的方法就是在调用OSIntExit之前先把堆栈指针保存下来,以后调度该用户任务时,直接从此恢复堆栈指针,而不再管实际的堆栈内容了(因为下面的内容相对于调度程序来说已经没有用处了)

    (三) void OSIntExit (void)的理解

    OS_CPU.H中的宏定义:

    typedef unsigned short OS_CPU_SR;      //定义OS_CPU_SR为16位的CPU状态寄存器

    #if      OS_CRITICAL_METHOD == 1

    #define  OS_ENTER_CRITICAL()  asm  CLI // OS_ENTER_CRITICAL()即为将处理器标志

    //寄存器的中断标志为清0,不允许中断

    #define  OS_EXIT_CRITICAL()   asm  STI // OS_ENTER_CRITICAL()即为将处理器标志

    //寄存器的中断标志为置1,允许中断

    #endif                                 //此一整段代码定义为开关中断的方式一

    #if      OS_CRITICAL_METHOD == 2

    #define  OS_ENTER_CRITICAL()  asm {PUSHF; CLI} //将当前任务的CPU的标志寄存器入

    //然后再将中断标志位清0

    #define  OS_EXIT_CRITICAL()   asm  POPF  //将先前压栈的标志寄存器的值出栈,恢复

    //到先前的状态,如果先前允许中断则现在

    //仍允许,先前不允许现在仍不允许

    #endif                                 //此一整段代码定义为开关中断的方式二

    #if      OS_CRITICAL_METHOD == 3

    #define  OS_ENTER_CRITICAL()  (cpu_sr = OSCPUSaveSR()) //保存CPU的状态寄存器到

    //变量cpu_sr中,cpu_sr

    //为OS_CPU_SR型变量

    #define  OS_EXIT_CRITICAL()   (OSCPURestoreSR(cpu_sr))// 从cpu_sr中恢复状态寄存

    //器

    #endif                                  //此一整段代码定义为开关中断的方式三,

    //此段代码只是示意代码,OSCPUSaveSR()及

    //OSCPURestoreSR(cpu_sr)具体什么函数由

    //用户编译器所提供的函数决定.

    //以上宏定义非常重要,在使用不同处理器时要使用相应处理器的开关中断指令,在代码移//植时很有用

    uCOS_II.H中定义:

    OS_EXT  INT8U   OSLockNesting; //8位无符号全局整数,表示锁定嵌套计数器

    void  OSIntExit (void)

    {

    #if OS_CRITICAL_METHOD == 3

    OS_CPU_SR  cpu_sr;

    #endif   //采用开关中断方式三

    if (OSRunning == TRUE) //如果内核正在运行,则进入if

    {

    OS_ENTER_CRITICAL();//进入临界段,关中断

    if (OSIntNesting > 0) //判断最外层中断任务是否已完成

    {

    OSIntNesting--;//由于此层中断任务已完成,中断嵌套计数器减//一

    }

    if ((OSIntNesting == 0) && (OSLockNesting == 0))

    // OSIntNesting==0表示程序的最外层中断任务以完成, OSLockNesting == 0

    //表示是否存在任务锁定,整句代码的意思是如果全部中断处理完了且没有其他

    //任务锁定任务调度则执行下列任务调度代码

    {

    OSIntExitY  = OSUnMapTbl[OSRdyGrp];                  //1

    OSPrioHighRdy = (INT8U)((OSIntExitY << 3) + OSUnMapTbl[OSRdyTbl[OSIntExitY]]);  //2

    if (OSPrioHighRdy != OSPrioCur)                  //3

    {

    OSTCBHighRdy  = OSTCBPrioTbl[OSPrioHighRdy];

    OSCtxSwCtr++;

    OSIntCtxSw();

    }

    }

    OS_EXIT_CRITICAL();//开中断

    }

    }

    要理解1,2,3处的代码含义.首先要理解任务是如何调度的,所以先讲一下任务调度的核心算法:

    a.数据结构:

    1.就绪表:就绪表包含两个变量,他们分别是OSRdyGrp(在uCOS_II.H中为OS_EXT  INT8U  OSRdyGrp;即8位无符号整型的全局变量)和OSRdyTb1[](在uCOS_II.H中为OS_EXT  INT8U   OSRdyTbl[OS_RDY_TBL_SIZE];)

    先分析 OS_EXT INT8U  OSRdyTbl[OS_RDY_TBL_SIZE];是怎么回事

    #define OS_LOWEST_PRIO  12 //在OS_CFG.H中

    这个宏定义了任务所能具有的最低优先级,那么此处共有从0到12共13个优先级,用户在代码移植时可以修改它,自定义所需要的优先级个数,但max(OS_LOWEST_PRIO)==63

    #define  OS_RDY_TBL_SIZE   ((OS_LOWEST_PRIO) / 8 + 1) //在uCOS_II.中

    OS_RDY_TBL_SIZE用于确定数组OSRdyTbl[]的大小,如果OS_LOWEST_PRIO==63,则上述宏实际上为#define  OS_RDY_TBL_SIZE  8,由于每个数组元素为8位,如果每一位表示一个优先级,则共有8*8=64个优先级

    现在回到就绪表,操作系统将优先级分为8组,优先级从0到7分为第一组,对应于OSRdyGrp的第0位,从8到15分为第二组,对应于OSRdyGrp的第1位,以此类推,64个优先级就有下面的对应关系(OSRdyTb1[]每组元素的每一位代表一个优先级):

    OSRdyTb1[0]--------------优先级从0到7--------------OSRdyGrp第0位

    OSRdyTb1[1]--------------优先级从8到15-------------OSRdyGrp第1位

    OSRdyTb1[2]--------------优先级从16到23-------------OSRdyGrp第2位

    OSRdyTb1[3]--------------优先级从24到31-------------OSRdyGrp第3位

    OSRdyTb1[4]--------------优先级从32到39-------------OSRdyGrp第4位

    OSRdyTb1[5]--------------优先级从40到47-------------OSRdyGrp第5位

    OSRdyTb1[6]--------------优先级从48到55-------------OSRdyGrp第6位

    OSRdyTb1[7]--------------优先级从55到63-------------OSRdyGrp第7位

    现在再做如下对应:

    当OSRdyTbl[0]中的任何一位是1时,OSRdyGrp的第0位置1,

    当OSRdyTbl[1]中的任何一位是1时,OSRdyGrp的第1位置1,

    当OSRdyTbl[2]中的任何一位是1时,OSRdyGrp的第2位置1,

    当OSRdyTbl[3]中的任何一位是1时,OSRdyGrp的第3位置1,

    当OSRdyTbl[4]中的任何一位是1时,OSRdyGrp的第4位置1,

    当OSRdyTbl[5]中的任何一位是1时,OSRdyGrp的第5位置1,

    当OSRdyTbl[6]中的任何一位是1时,OSRdyGrp的第6位置1,

    当OSRdyTbl[7]中的任何一位是1时,OSRdyGrp的第7位置1,

    如果置1表示有任务进入就绪态,那么上面的表可以理解为:OSRdyGrp的第N位(0<=N<=7)为1,那么在OSRdyTb1[N]中至少有一位是1,也就是说在OSRdyTb1[N]对应的任务中至少有一个任务处于就绪态

    该表在OS_CORE.C中定义如下:

    INT8U  const  OSMapTbl[]={0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};

    //8位无符号整型常量数组

    3.表(数组)OSUnMapTb1[]:用于求出一个8位整型数最低位为1的位置

    该数组在OS_CORE.C中定义如下:

    INT8U  const  OSUnMapTbl[] = {

    0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x00 to 0x0F */

    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x10 to 0x1F */

    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x20 to 0x2F */

    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x30 to 0x3F */

    6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x40 to 0x4F */

    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x50 to 0x5F */

    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x60 to 0x6F */

    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x70 to 0x7F */

    7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x80 to 0x8F */

    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x90 to 0x9F */

    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xA0 to 0xAF */

    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xB0 to 0xBF */

    6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xC0 to 0xCF */

    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xD0 to 0xDF */

    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0xE0 to 0xEF */

    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0        /* 0xF0 to 0xFF */

    };

    理解: 我把问题转化为:

    “一个无符号的8位整数,如何确定最低位为1的位的位置?”

    即对于任意一个8位整型数,比如4,考虑它的二进制位中所有为1的位,确定最低位为1的位置(相对第0位的偏移),一般来讲首先想到的方法是移位的方法.如:

    pos=0;//pos用于统计相对于第0位的偏移

    while( !(num & 0x01) )//与00000001按位于,如果最低位为1,退出循环,即找到最低位//为1的位

    {

    num=num>>1;//将二进制数右移一位

    pos++;//进行一次移位,则pos加一

    }

    最后得到的pos就是所有位中为1的最低位的偏移量,但这样计算需要时间,尽管最多右移7次。为了节省时间,使用的方法是“空间换时间”的办法,即把8位无符号数,所有可能的情况的都列了出来,共有256个数字,把每个数字的最低为1位的位置都预先计算好。比如4对应二进制数为100,最低为1位相对第0位偏移量为2,则查表时,以4为索引,马上就得到2这个数字。(即:OSUnMapTb1[4]==2)

    b.构建OSRdyGrp和OSRdyTb1[]算法:

    代码原型在OS_CORE.C中,实际代码大致如下:(prio为任务优先级)

    INT8U  OS_TCBInit (INT8U prio, OS_STK *ptos, OS_STK *pbos, INT16U id, INT32U stk_size, void *pext, INT16U opt)

    {

    …………

    ptcb->OSTCBY         = prio >> 3;

    ptcb->OSTCBBitY      = OSMapTbl[ptcb->OSTCBY];

    ptcb->OSTCBX         = prio & 0x07;

    ptcb->OSTCBBitX      = OSMapTbl[ptcb->OSTCBX];

    …………..

    OSRdyGrp               |= ptcb->OSTCBBitY;

    OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;

    …………..

    }//此函数在创建任务时被调用,即OSTaskCreate(..)中,用于初始化任务控制块

    以上代码可以等效于:

    OSRdyGrp  |=  OSMapTbl[prio>>3];

    OSRdyTb1[prio>>3] |= OSMapTbl[prio&0x07];

    此处 prio >> 3 是右移3位,就相当于连续除以3个2,因此相当于:prio / 8 ;

    prio & 0x07 是求低3位的值,而由高5位构成的值正好是8的整数倍,因此是取余运算,

    即:prio % 8

    因此又可将算法等效为:

    OSRdyGrp  |=  OSMapTbl[prio / 8];

    OSRdyTb1[prio / 8] |= OSMapTbl[prio % 8];

    算法的作用相当于如下流程:(假定prio=28)

    就这样把任务的优先级放入了就绪表.在此我产生一个疑问,”prio>>3与prio&0x07并不直观,为什么不用prio/8与prio%8呢?”我做如下解释:

    处理器一般具有如下结构

    累加器是具有移位功能的,prio>>3可以在累加器中完成而不必进入ALU,而prio/8则不同,要进入ALU,ALU处理速度不如累加器,如果采用prio/8将降低操作系统的实时性,同样prio&0x07只是一个间单的位与操作,而prio%8则还要经过ALU,如采用prio%8也将降低实时性.

    现在回到OSIntExit()处,看1,2,3处的代码:

    OSIntExitY  = OSUnMapTbl[OSRdyGrp];                //1

    OSPrioHighRdy = (INT8U)((OSIntExitY << 3) + OSUnMapTbl[OSRdyTbl[OSIntExitY]]);//2

    if (OSPrioHighRdy != OSPrioCur)                    //3

    {

    OSTCBHighRdy  = OSTCBPrioTbl[OSPrioHighRdy];

    OSCtxSwCtr++;

    OSIntCtxSw();

    }

    在uCOS_II.H中有如下定义:

    OS_EXT  INT8U   OSIntExitY;//8位无符号全局整型变量,用于存放就绪表中就绪的任务组

    OS_EXT  INT8U   OSPrioHighRdy;// 8位无符号全局整型变量,用于存放具有最高优先级任务的优先级

    OSUnMapTbl[]:用于计算偏移量,偏移量即为优先级最高的就绪任务组在OSRdyGrp中的位置

    以及优先级最高的任务在最高优先级任务组OSRdyTbl[N](N表示最高优先级任务组,0<=N<=7)中的位置.

    OSIntExitY  = OSUnMapTbl[OSRdyGrp];//表示获得具有最高优先级的组

    例如OSRdyGrp值为01101000(0x68),则第3,5,6组中有任务就绪,查表OSUnMapTbl[0x68]==3,即优先级最高任务组为第3组.

    OSUnMapTbl[OSRdyTbl[OSIntExitY]] //表示获得最高优先级任务组中的优先级最高的任务

    例如OSRdyTbl[3]的值为01110000(0x70),则第4,5,6位中有任务绪,OSUnMapTbl[0x70]==4,即优先级最高的任务在组中位于第4位.

    OSPrioHighRdy = (INT8U)((OSIntExitY << 3) + OSUnMapTbl[OSRdyTbl[OSIntExitY]]);

    //就是3*8+4==28,再经强制类型转换成INT8U型,赋给OSPrioHighRdy

    这样OSPrioHighRdy就获得了就绪表中优先级最高的任务

    展开全文
  • ARM中断处理过程

    2021-05-17 17:36:15
    以s3c2440 ARM9核为例:一:s3c2440 ARM处理器特性:1、S3C2440支持60个中断源,含子中断源;2、ARM9采用五级流水线方式;3、支持外部中断和内部中断;二、s3c2440 支持的寄存器:2.1 外部中断寄存器24外部中断占用...

    以s3c2440 ARM9核为例:

    一:s3c2440 ARM处理器特性:

    1、S3C2440支持60个中断源,含子中断源;

    2、ARM9采用五级流水线方式;

    3、支持外部中断和内部中断;

    二、s3c2440 支持的寄存器:

    2.1 外部中断寄存器

    24个外部中断占用GPF0-GPF7(EINT0-EINT7),GPG0-GPG15(EINT8-EINT23)。用这些脚做中断输入,则必须配置引脚为中断,并且不要上拉。具体可参考datesheet数据手册。

    寄存器:

    EXTINT0-EXTINT2:分别设置EINT0—EINT7、EINT8—EINT15、EINT16—EINT23的触发方式(高电平触发、低电平触发、下降沿触发、上升沿触发)。

    EINTFLT0-EINTFLT3:控制滤波时钟和滤波宽度。

    EINTPEND:这个是中断挂起寄存器,清除时要写1,后面还有几个是写1清除。当一个外部中断(EINT4-EINT23)发生后,那么相应的位会被置1。为什么没有EINT0-EINT3,因为它们分别由SRCPND寄存器的后4位控制。

    EINTMASK:这个简单,是屏蔽中断用的,也就是说位为1时,此次中断无效。

    2.2 内部中断寄存器

    内部中断有8个寄存器。

    寄存器:

    SUBSRCPND:当一个中断发生后,那么相应的位会被置1,表示一个中断发生了。

    INTSUBMSK:与上一个是一样的,中断屏蔽寄存器。

    SRCPND:当一个中断发生后,那么相应的位会被置1,表示一个或一类中断发生了。

    INTMSK:用来屏蔽SRCPND寄存器所标识的中断。但只能屏蔽IRQ中断,不能屏蔽FIQ中断。

    INTMOD:当INTMOD中某位被设置为1时,它对应的中断被设为FIQ,CPU将进入快速中断模式。

    PRIORITY:用于设置IRQ中断的优先级。具体使用方法可参考芯片手册。

    INTPND:中断优先级仲裁器选出优先级最高中断后,这个中断在INTPND寄存器中的相应位被置1,随后,CPU进入中断模式处理它。同一时间内,此寄存器只有一位被置1。

    INTOFFSET:用来表示INTPND寄存器中哪位被置1了,即记录INTPND中位[x]为1的位x的值。清除INTPND、SRCPND时自动清除。

    三、中断处理流程

    1、中断控制器汇集各类外设发出的中断信号,然后通知CPU。

    2、CPU保存当前程序的运行环境,然后调用中断服务程序(ISR),来处理中断。

    3、在ISR中通过读取外设的相关的寄存器来识别中断的类型,并进行相应的处理。

    4、清除中断:通过读写相关中断控制寄存器和外设相关寄存器来实现。(注意消除中断是必要的)

    5、恢复被中断程序的执行环境,继续执行被中断的程序。

    四、代码:

    main.c

    1 intmain()2 {3 ................4 //其他部分初始化

    5 button_init();6 init_irq();7 while(1);8 return 0;9 }

    interrupt.c

    1 /*interrupt registes*/

    2 #define SRCPND (volatile unsigned long *)0x4A000000

    3 #define INTMOD (volatile unsigned long *)0x4A000004

    4 #define INTMSK (volatile unsigned long *)0x4A000008

    5 #define PRIORITY (volatile unsigned long *)0x4A00000c

    6 #define INTPND (volatile unsigned long *)0x4A000010

    7 #define INTOFFSET (volatile unsigned long *)0x4A000014

    8 #define SUBSRCPND (volatile unsigned long *)0x4A000018

    9 #define INTSUBMSK (volatile unsigned long *)0x4A00001c

    10

    11 #define EINTMASK (volatile unsigned long *)0x560000a4

    12 #define EINTPEND (volatile unsigned long *)0x560000a8

    13

    14 voidinit_irq()15 {16

    17 //对于EINT4,需要在EINTMASK寄存器中使能它

    18 *(EINTMASK) &= ~(1<<4);19

    20

    21 //EINT0、EINT1、EINT2、EINT4_7使能

    22 *(INTMSK) &= (~(1<<0)) & (~(1<<1)) & (~(1<<2)) & (~(1<<4));23

    24 __asm__(25 /*开中断*/

    26 "mrs r0,cpsr\n"

    27 "bic r0, r0, #0x80\n"

    28 "msr cpsr_c, r0\n"

    29 :30 :31 );32 }33

    34 voidhandle_int()35 {36 /*读取产生中断的源*/

    37 unsigned long value = *(INTOFFSET);38

    39 switch(value)40 {41 case 0: //EINT0~K4

    42 led_on();43 break;44

    45 case 1: //EINT1~K1

    46 led_off();47 break;48

    49 case 2: //EINT2~K3

    50 led_on();51 break;52

    53 case 4: //EINT4~K2

    54 led_off();55 break;56

    57 default:58 break;59 }60

    61 /*中断清除*/

    62 if(value == 4)63 *(EINTPEND) = (1 << 4);64 *(SRCPND) = 1 <

    start.S

    1 irq:

    2 sub lr, lr, #4

    3 stmfd sp!, {r0-r12, lr} /* 保护现场 */4 bl handle_int5 ldmfd sp!, {r0-r12, pc}^ /* 恢复现场,^表示把spsr恢复到cpsr */

    五:部分代码解释:

    sub lr, lr, #4

    0b1331709591d260c1c78e86d0c51c18.png

    展开全文
  • 简述CPU中断响应过程的九大步骤

    千次阅读 2020-12-19 13:28:25
    CPU响应中断:就是CPU要去执行相应的中断服务程序,其响应过程是CPU将现在执行程序的指令地址压入堆栈,跳转到中断服务程序入口地址,中断服务程序的入口地址就是中断向量,这个中断向量用216位寄存器存放。...

    hello,大家好!播妞又来给大家分享干货了!今天就CPU中断响应过程的九个步骤进行简单讲述,希望大家都能通过本文大致了解中断响应过程。

    CPU响应中断:就是CPU要去执行相应的中断服务程序,其响应过程是CPU将现在执行程序的指令地址压入堆栈,跳转到中断服务程序入口地址,中断服务程序的入口地址就是中断向量,这个中断向量用2个16位寄存器存放。

    入口地址是22位的,地址的低16位保存在该向量的低16位,地址的高16位则保存在它的高6位,更高的10位保留。

    步骤一:任何一个PIE中断组的外设或外部中断产生中断。如果外设模块内的中断被使能,中断请求将被送到PIE模块。

    步骤二:PIE模块将识别出别的PIE中断组x内的y中断(INTx.y)申请,然后相应的PIE中断标志位被锁存:PIEIFRx.y=1。

    步骤三:PIE的中断如要送到CPU需满足下面两个条件:

    1.相应的使能位必须被设置(PIEIERx.y=1)。

    2.相应的PIEACKx位必须被清除。

    步骤四:如果满足步骤三中的两个条件,中断请求将被送到CPU并且相应的响应寄存器位被置1(PIEACKx=1)。PIEACKx位将保持不变,除非为了使本组中的其他中断向CPU发出申请而清除该位。

    步骤五:CPU中断标志位被置位(CPUIFRx=1),表明产生一个CPU级的挂起中断。

    步骤六:如果CPU中断被使能(CPUIERx=1,或DBGIERx=1),并且全局中断使能(INTM=0),CPU将处理中断INTx。

    步骤七:CPU识别到中断并且自动保存相关的中断信息,清除使能寄存器(IER)位,设置INTM,清除EALLOW。CPU完成这些任务准备执行中断服务程序。

    步骤八:CPU从PIE中获取相应的中断向量。

    步骤九:对于复用中断,PIE模块用PIEIERx和PIEIFRx寄存器中的值确定响应中断的向量地址。有以下两种情况:

    1.在步骤四中若有更高优先级的中断产生,并使能了PIEIERx寄存器,且PIEIFRx的相应位处于挂起状态,则首先响应优先级更高的中断。

    2.如果在本组内没有挂起的中断被使能,PIE将响应组内优先级最高的中断,调转地址使用INTx.1。这种操作相当于处理器的TRAP或INT指令。

    CPU进入中断服务程序后,将清除PIEIFRx.y位。需要说明的是,PIEIERx寄存器用来确定中断向量,在清除PIEIERx寄存器时必须注意。

    以上就是CPU中断响应过程,讲解的每一个步骤都是干货,大家都弄清楚了吗?

    展开全文
  • Linux中断和中断处理程序 由于处理器的速度与外设的速度相差很大,无法采取处理器向外设发出请求然后等待的方法。处理器与外设通信的方法: 轮询:处理器定期对设备的状态进行查询(缺点:在不需要通信的情况下,...
  • Linux中断处理

    2021-05-19 00:14:32
    中断处理 - 上半部(硬中断)由于APIC中断控制器有点小复杂,所以本文主要通过8259A中断控制器来介绍Linux对中断的处理过程中断处理相关结构前面说过,8259A中断控制器...
  • 原标题:Linux中断机制:硬件处理,初始化和中断处理来源: CSDN | phenix_lord的专栏硬件处理最近解决一关于Linux中断的问题,把相关机制整理了一遍,记录在此。不同的外部设备、不同的体系结构、不同的OS其中断...
  • 中断处理

    2021-05-17 23:52:13
    作者:smcdef 发布于:2018-1-1 17:03分类:中断子系统1) 设备唤醒cpu之后是立即跳转中断向量表指定的位置吗?如果不是,那么是什么时候才会跳转呢?2) 已经跳转到中断服务函数开始执行代码,后续就会调用你注册的...
  • 判断正确的是【单选题】由曲面围成或由曲面和平面围成的立体称为曲面立体,如圆柱体由圆形平面和柱面构成,圆环体由圆环面构成,圆锥体由【判断题】若圆柱体的底面为水平面,则圆柱体的三面投影是一圆和两大小相等的...
  • 多重中断处理及其中断源屏蔽字

    千次阅读 2021-01-08 19:25:09
    一。...否则,CPU不予响应,必须待原中断处理完毕且返回主程序后,再响应新的中断。(这地方其实可以参考一下计算机操作系统里面对动态的优先级算法) 3、单重中断和多重中断比较 在中断响应部分
  • 具体整个处理过程分成三步骤来描述:1、第二章描述了中断处理的准备过程2、第三章描述了当发生中的时候,ARM硬件的行为3、第章描述了ARM的中断进入过程4、第五章描述了ARM的中断退出过程本文涉及的代码来自3.14...
  • 初期中断和异常处理 在上一 部分 我们谈到了初期中断初始化。目前我们已经处于解压缩后的Linux内核中了,还有了用于初期启动的基本的 分页 机制。我们的目标是在内核的主体代码执行前做好准备工作。 我们已经在 本...
  • 8259A中断原理 重 点 中断的概念 CPU响应可屏蔽中断的条件 中断处理过程 硬件中断与软件中断的比较 中断向量的装入 8.1 中断的概念 1 为什么要用中断? 中断:外设向CPU发中断请求,CPU接收到中断请求并在一定条件下...
  • Linux中断子系统()之中断申请注册 备注:   1. Kernel版本:5.4 ...  中断处理主要有以下几功能模块:硬件中断号到Linux irq中断号的映射,并创建好irq_desc中断描述符;中断注册时,先获取设备的
  • 硬件处理最近解决一关于Linux中断的问题,把相关机制整理了一遍,记录在此。不同的外部设备、不同的体系结构、不同的OS其中断实现机制都有差别,本文对应的OS为linux3.4版本,外部设备为PCI设备、系统为X86。概览...
  • GIC驱动分析2.GIC驱动流程分析第二部分 device node转化为platform_device第三部分:platform_device注册添加第部分 GPIO控制器驱动第五部分 引用GPIO中断的节点的解析第六部分 GPIO中断处理流程 本文以AM5728 ...
  • 系统结构-3-3中断级屏蔽位设置 ...只有比它高一级的中断请求才能中断处理,等响应和处理完后再继续处理原先的那个中断请求。中断响应的次序用排队器硬件实现,次序是由高到低固定的,不便于改动。
  • 中断是指CPU在正常执行指令过程中,出现了突发且紧急的事件,CPU必须暂时停止执行当前的程序,跳转到处理突发事件的指令处执行,处理完毕后返回到原来的指令处继续执行。根据中断的来源,中断可分为内部中断和外部...
  • GIC驱动程序对中断处理流程1. 一级中断控制器处理流程2. 多级中断控制器处理流程 参考资料: linux kernel的中断子系统之(七):GIC代码分析 使用逐步演进的方法才能形象地理解。 1. 一级中断控制器处理流程 ...
  • 补充:事件不仅包含中断和异常,还包含系统调用,这属于用户主动请求的事件。 上一节,只有一溢出异常,那么,如果很多异常、中断呢?(中断向量表) 另外,之前0号地址只能存储两条指令,如果需要更多指令...
  • 随着嵌入式微处理器性能的提高,集成的外围接口设备越来越多,而外围设备与处理器之间多采用中断方式进行通信,即使在没有操作系统的情况下,也常需要对多外围设备的中断处理例程进行动态加载,从而实现对多外围...
  • 一、中断处理基础 中断系统是一处理器的重要且基本的组成部分,可以极大提高CPU的执行效率,一般中断系统框架组成如下图:
  • 释放中断号中断处理程序的实现:中断处理程序事实上还是一普通的函数,只是我们要注意中断处理程序是 在中断期间运行的,具有最高的优先级别。还有中断处理程序必须要在尽量短的时间内完成,这对于那些需在中断...
  • 当有外部中断发生时,硬件会根据异常向量表,找到中断的入口地址,开始调用由操作系统提供的通用中断处理函数。而系统初始化阶段的异常向量表的设置、执行中断服务例程前后的上下文保护与恢复等等,这部分是CPU架构...
  • 1.概述 在第一章《Linux中断系统介绍及底层中断处理源代码分析》中,Linux对中断的高层处理,最终调用的是handle_arch_irq或arch_irq_handler_default函数...因此这里以zynq7为例,介绍Linux内核对uart0的中断处理过程
  • 第7章 输入输出系统一、名词解释:历年真题:(2001年)9.DMA 方式:...中断屏蔽:CPU处理个中断过程中,对其他一些外部设备的中断进行阻止。(2004年)17.统一编址:将输入输出设备中控制寄存器、数据寄存器、状态...
  • 在Linux中,分为中断处理采用“上半部”和“下半部”处理机制。 一、中断处理“下半部”机制 中断服务程序一般都是在中断请求关闭的条件下执行的,以避免嵌套而使中断控制复杂化。但是,中断是一随机事件,它随时...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 170,501
精华内容 68,200
关键字:

中断处理的四个过程