PowerPC基于vxWorks的中断初始化分析

1. 本文简介

      本文主要介绍P2020芯片中vxWorks中断初始化过程(部分讲解是以linux为例)。P2020属于PPC85XX系列,内核为e500v2,它是PowerPC体系结构中主要应用于通信领域的片子,PowerPC体系结构规范发布于1993年,它是一个64位规范(也包含32位子集)。但几乎所有常规的PowerPC的芯片都是32位的,除了部分新型高端芯片(eg:IBM RS/6000等)。

      本文主要章节如下:

       2 参考书籍介绍

       3 OEA相关寄存器(中断相关)

       4 PowerPC架构异常处理机制(P2020为代表)  

       5 vxWorks中断初始化过程

       6 总结

 2.参考书籍

      (1) IBM官方文档,关于PowerPC Architecture Boook I II III ,其中Book III 主要讲解与操作系统相关的寄存器。

            下载链接: https://www.ibm.com/developerworks/systems/library/es-archguide-v2.html.

     

       (2)  <<E500CORERM.pdf>> 该手册描述的是e500核的工作过程以及Reg详细描述,包括电源管理,中断处理,Cache,MMU.

       (3)  <<P2020RM.pdf>> 该手册主要描述的是芯片与外设相关的控制器,但很少介绍到CPU通用寄存器,特殊寄存器等.

       (4)  <<821INSTSET.pdf>> PowerPC 汇编指令集介绍

       (5)   PowerPC 汇编指令简单介绍

    (2.3.4)都可以在NXP官网中下载

 3 OEA相关寄存器(中断相关)

     PowerPC 处理器有 32 个(32 位或 64 位)GPR(通用寄存器)以及诸如 PC(程序计数器,也称为 IAR/指令地址寄存器 或 NIP/下一指令指针)、LR(链接寄存器)、CR(条件寄存器)等各种其它寄存器。有些 PowerPC CPU 还有32个64位的FPR(浮点寄存器). 下图展示了Power ISA2.04寄存器模式,且值得注意的是 PowerPC有两种执行模式,分别为用户模式(UserMode) 和特权模式(SupervisorMode),MSR[PR]=0 表示特权模式。Linux内核运行在特权模式,而普通程序运行在用户模式。VxWorks全部运行在特权模式。

   3.1 OEA寄存器集包括四类寄存器(具体介绍只涉及中断相关的,其它请参考下来的链接)

    本小章节参考:32位PowerPC构架通用寄存器分析及总结

    (1)配置寄存器(Configuration Registers)

             MSR寄存器:(与中断息息相关

             定义处理器的状态,它可以被mtmsr, sc, rfi指令修改;可以被mfmsr读取;

详细说明:基于603E内核的PowerPC处理器,中断向量为一个固定的地址,可以通过设置MSR[IP]位来决定这个固定地址是0x0,还是0xfff0_0000;而E500内核在进入中断和异常处理程序时,不能关闭MMU,因此不能使用物理地址作为中断向量,而应使用IVPR和IVOR寄存器保存相应的中断向量(在下面中断机制章节详细介绍),下面为e500核的MSR:

     中断的开关由寄存器MSR来控制的
     CE:if set 1,Critical input and watchdog timer interrupts are enabled.
     EE:if set 1,External input, decrementer, fixed-interval timer and performance monitor interrupts are enabled
     ME:if set 1, Machine check interrupts are enabled.
     DE:if set 1,Debug interrputs are enabled if DBCR0[IDM] = 1.

    MSR[EE]这个bit很重要,中断的使能要靠它。
    注意:当发生中断后,MSR[EE]会自动置0屏蔽所有其他的中。
      如果在MSR[EE]置1前,又来了一个中断
           1.此中断是边沿触发的,那么此中断丢失。此种中断就是电平变化,一次就没了
           2.此中断时水平触发的,此中断不会丢失。此种中断的电平会一直active,直到硬件处理它

             PVR寄存器

              定义寄存器模型的版本和处理器的版

 (2)内存管理寄存器(Memory Management Registers)

             BAT寄存器:

            OEA定义了四组BAT指令寄存器(IBAT0U-IBAT3U和IBAT0L-IBAT3L),也定义了四组BAT数据寄存器(DBAT0U-DBAT3U和DBAT0U-DBAT3U

            SDR1寄存器

            该寄存器定义了用于虚拟地址转换为物理地址所需要的页表基地址

            SR寄存器:

            OEA定义了16个32位的SR寄存器(SR0-SR15

 (3)中断处理寄存器(Interrupt Handing Register)

        详细介绍请看《E500CORE.pdf》2.7 Interrupt Registers

 (4)多功能寄存器(Miscellaneous Registers)

       TB(time base)寄存器:CPU时间片基准

       DEC寄存器:这是一个32位的递减计数器

       EAR(External Access Register)寄存器:用于访问外部设备

       DABR(Data address breakpoint register):用来控制数据地址断点功能

       PIR(Processor identification register):在多处理器的芯片上用来标识一个核,例如在MPC8641d芯片上有两个E600的core,就用PIR来定位其中的core。

   3.3 通用寄存器与专用寄存器的用途

   本小章节参考:PowerPC汇编指令以及通用与专用寄存器介绍

   r3,r4-r10经常用于C,汇编的混合编程中

在target\h\arch\ppc\toolPpc.h中有对以上寄存器的重定义

 

 4 PowerPC架构异常处理机制(P2020为代表)     

      4.1 PowerPC中断系统(P2020)

         从CPU的角度来讲,中断源可以分为自己内核产生的异常和PIC提供的中断。

         异常:是e500核产生的,它是同步产生(可以预知的),如非法指令,或访问存储器时出现TLB Miss(Data | Instruction )等.

         <<E500CORERM.pdf>>5.11.1描述了PowerPC架构可能出现的全部异常(这里描述包括中断) 

         中断:是e500核外部引脚产生的,由PIC送到内核处理,它是异步产生的(无法预知的).大致分为一般中断(int)(包括内外部等),关键中断(cint)和机器检查中断(machine check). 下图为中断源架构图,其中Message数量应该是写错了(理论为4).

         从图中可以看出PowerPC处理器中断系统由内核中断以及异常处理系统和PIC中断控制器构成.

      

      4.2 中断向量

       在E500内核中,使用IVPR和IVORx寄存器共同确定中断或者异常程序的入口地址。其中,IVPR寄存器提供中断程序入口地址的第0~15位,IVORx提供中断程序入口地址的第16~27位,而中断程序的入口地址的第28~31位为0。IVORx与异常的对应关系如下图所示:


       每类中断都有自己的中断向量,通过它能计算出中断handler的指令地址。
       指令地址的计算方法:
        IVPR[32-47] || IVORn[48-59] || 0b0000
       这样就得到一个32个bit地址

有2个中断向量对程序员来说比较亲切:
    一个是 IVOR4 用来处理外部中断和内部SOC中断
    一个是 IVOR8 用来处理系统调用

随便找一个Start.S进行简单的分析
    185 /* Setup interrupt vectors 设置IVPR寄存器*/
    186 lis r1,TEXT_BASE@h   /* load value to r1 */
    187 mtspr IVPR, r1  /* write r1 to IVPR */
    197 li r1,0x0500
    198 mtspr IVOR4,r1 /* 4: External interrupt */
     .......
    205 li r1,0x0900
    206 mtspr IVOR8,r1 /* 8: System call */
    207 /* 9: Auxiliary processor unavailable(unsupported) */
    208 li r1,0x0a00
    209 mtspr IVOR10,r1 /* 10: Decrementer */
    ......
看board/pq37pc/pq37pc_8560/config.mk 里有 
     TEXT_BASE = 0xFF800000
     所以 IVPR   = 0xFFF80000
     IVOR10 = 0x00000a00
     计算 IVPR[32-47] || IVPOR10[48-59] || 0b0000  得 0xFFF80a00

4.3 中断源寄存器

 中断向量只能确定类型,里面保存了interrupt handler的地址。
 在interrupt handler里,需要根据中断源寄存器来进一步确定到底是哪里发生了中断

 E500内部中断源有64个(部分reserve),来自TSEC,LBC,DMA,CPM等

 寄存器组 PIC_IIVPRn 与 P2020内部SOC中断一一对应(n取值 0~63)

  

 E500外部中断源有12个,来自外部引脚 IRQ[0:11]
 寄存器组 EIVPRn 与 MPC8560 外部中断一一对应(n取值 0~11)

下面具体介绍外部源寄存器

   MSK:  若置1,此中断源的中断被无视.
   A:    若置1,此中断源有中断发生
   P:    若置1, active-high; 若置0, active-low.
   S:    若置1,此中断是水平触发。若置0,此中断是边界触发
   PRIORITY: 优先级0-15。15是最高优先级,0时相当于无视此中断。
   VECTOR: 硬件中断号。中断发生后处于pengding时,此字段被写在寄存器IACK

 

总结:处理内,外中断的handle都是同一个functions(流程),EntInt-->Handle-->ExitInt,在处理函数中读取IACK,就知道是哪个中断源触发了中断,然后做对应的处理

知道中断向量 和 中断源,系统处理中断的大致流程就能理出来了,以外部中断eg:

4.4 外部中断处理流程

     1.首先E500内核清除在指令完成队列CQ中所有的执行,然后把正在执行的指令序列下一条指令地址保存到中断寄存器                     SRR0(Save/RestoreRegiser 0)

     2.把当前 MSR 的内容保存到 SRR1

     3.把 MSR 某些比特置为 0,如MSR[SPE,WE,EE,PR,FP,FE0,FE1,IS,DS]are 0 by all interrupts.

   (E500内核将MSR寄存器的CE、ME、DE位保留,其他位全部清零。因此E500内核在进行外部中断处理程序时,仍然可以被Critical中断,Machine check中断和调试中断程序重入,但是不能被外部中断立即重入。在Linux PowerPC中,外部中断处理程序会选择合适时机使能MSR寄存器的EE位,以支持外部中断的重入)

     4.在新的 MSR 状态下, E500内核将根据IVPR,IVOR4寄存器确定中断向量,从中断向量偏移处开始指令读取和执行

    5. 在中断处理程序执行完毕后,使用rfi指令进行中断返回。rfi指令将从SRR1寄存器中恢复MSR寄存器的值,并从SRR0寄存器中获得程序返回地址。rfi指令在进行程序正文切换之前还会进行指令和数据的同步,还给被中断的程序一个“干净”的空间,之后E500内核进行中断返回。

 

5.vxWorks中断初始化过程(跳过bootrom过程)

    usrInit(prjConfig.c)--> {sysStart(startType)-->intVecBaseSet ((FUNCPTR *) VEC_BASE_ADRS)}

     -->excVecInit()

 (1)sysStart (first C code executed from usrInit) 主要作用是清除BSS,以及设置中断向量的基地址

Code in arget\config\comps\src\usrStartup.c

中断向量的基地址一般设置为0x0000_0000 | 0xfff0_00000
macro in BSP/config.h
#define VEC_BASE_ADRS           LOCAL_MEM_LOCAL_ADRS
#define LOCAL_MEM_LOCAL_ADRS    0x00000000

void sysStart(int startType){

#if (CPU_FAMILY == PPC) || (CPU_FAMILY == MIPS)

    /* 
     * For PPC and MIPS, the call to vxSdaInit() must be the first operation 
     * in sysStart(). This is because vxSdaInit() sets the SDA registers
     * (r2 and r13 on PPC, gp on MIPS) to the SDA base values. No C code 
     * must be placed before this call.
     */

    _WRS_ASM ("");   /* code barrier to prevent compiler moving vxSdaInit() */
    vxSdaInit ();    /* this MUST be the first operation in usrInit() for PPC */
    _WRS_ASM ("");   /* code barrier to prevent compiler moving vxSdaInit() */

#endif	/* (CPU_FAMILY == PPC) || (CPU_FAMILY == MIPS) */
...

#ifdef	CLEAR_BSS
	bzero (edata, end - edata);
#endif	/* CLEAR_BSS */

    sysStartType = startType;
    intVecBaseSet ((FUNCPTR *) VEC_BASE_ADRS);
...

#ifdef _WRS_CONFIG_USE_MEMDESC
    sysMemDescInit ();
#endif
}

   (2) intVecBaseSet

Code in target\src\arch\ppc\intArchLib.c

说明:此阶段函数并未真正设置中断向量基地址到e500内核中,只是对_ppcExcIntVecBase赋值的
     过程,实际设置的过程在excVecInit里面。

int	  (* _func_intLevelSetRtn) (int) = NULL;
int	  (* _func_intEnableRtn) (int) = NULL;
int	  (* _func_intDisableRtn) (int) = NULL;

上面的函数指针初始化在vxbEpicIntCtlr.c里面

int intEnable
    (
    int intLevel		/* interrupt level to enable */
    )
    {
    /* execute VxBus Legacy interrupt enable routine first if supported */

    if ((_func_vxbIntEnable != NULL) &&
	(_func_vxbIntEnable (intLevel) != ERROR))
	return (OK);

    if (_func_intEnableRtn != NULL)
	return (_func_intEnableRtn (intLevel));

    return (ERROR);
    }

void intVecBaseSet(FUNCPTR * baseAddr){

  /* 主要_func_intVecBaseSetRtn实际为空,在vxWorks目前阶段函数指针未赋值 
  ** 可在vxWorks shell 中 _func_intVecBaseSetRtn == 1 测试返回值
  */
    if (_func_intVecBaseSetRtn != NULL)
    	_func_intVecBaseSetRtn (baseAddr);

    _ppcExcIntVecBase = baseAddr;

    _ppcAllocationQuantumSize = _CPU_ALLOC_ALIGN_SIZE;
    _ppcStackAlignSize = _CPU_STACK_ALIGN_SIZE;

    ...
}

FUNCPTR * intVecBaseGet (void)
{
    if (_func_intVecBaseGetRtn == NULL)
	return (_ppcExcIntVecBase);

    return (_func_intVecBaseGetRtn ());
}

(3)excVecInit

Code in target\src\arch\ppc\excArchLib.c

STATUS excVecInit (void)
    {
    FAST int ix;

#if !defined(_WRS_CONFIG_WRHV_GUEST) || defined(_VB_PISA_EHV)
    if (excExtendedVectors == TRUE)
	{
	entOffset     = EXT_ENT_OFF;
	isrOffset     = EXT_ISR_OFF;
	exitOffset    = EXT_EXIT_OFF;
#ifdef _EXC_OFF_CRTL
    ...
#endif  /* _EXC_OFF_CRTL */
	}
    else
	{
     ...
	}

    excVecBaseSet(intVecBaseGet());

    for (ix = 0; excBlTbl[ix].excHandler != (void (*)()) NULL; ix++)
	{
	excVecConnectCommon (excBlTbl[ix].vecOff,
			     excBlTbl[ix].vType,
			     excBlTbl[ix].excHandler,
			     excBlTbl[ix].vecOffReloc);
	}


#ifndef _WRS_CONFIG_WRHV_GUEST
#ifdef	IVOR0
    excIvorInit();
#endif	/* IVOR0 */
#endif  /* _WRS_CONFIG_WRHV_GUEST */
      
    ...

    /*
     * Now that the vectors are set up, and provided we can safely do
     * so prior to MMU setup, set the recoverability indicator if so 
     * equipped.  (If excVecBase and excVecBaseAltAdrs differ, 
     * no exceptions can be handled until the MMU has been set up.) 
     * We don't enable Machine Check exceptions here because excHandler 
     * is not ready, i.e. taskIdCurrent is a meaningless value now. 
     * We postpone it to usrRoot and use taskMsrDefault to enable it.
     */
    if (excVecBaseAltAdrs == excVecBase)
	vxMsrSet (vxMsrGet() 
#ifdef	_PPC_MSR_RI
			     | _PPC_MSR_RI
#endif	/* _PPC_MSR_RI */
	);

#ifndef _WRS_CONFIG_WRHV_GUEST
    /* Used for the generic layered exception handler */
    hdlrBase = excVecBase + _EXC_OFF_END;
    /* save the Data and/or Instruction MMU selected */
    hdlrCodeBase = excVecBaseAltAdrs + _EXC_OFF_END;

#if (CPU==PPC85XX)
# if !defined(PPC_e200) && !defined(PPC_e500mc) || defined(PPC_e6500)
    installE500ParityErrorRecovery();
# endif /* !PPC_e200 && !PPC_e500mc || PPC_e6500 */
#endif /* (CPU==PPC85XX) */
#endif  /* _WRS_CONFIG_WRHV_GUEST */

#else   /* _WRS_CONFIG_WRHV_GUEST && !_VB_PISA_EHV */
    {
    char * addr;
    int excSize = (int)&_func_exc_handler_end - (int)&_func_exc_handler;
    int intSize = (int)&_func_int_handler_end - (int)&_func_int_handler;

    bzero ((void *)0, _EXC_OFF_END);

    for (addr = 0; addr < (char *)_EXC_OFF_END; addr+=0x100)
	{
        bcopy ((void *)&_func_exc_handler, (void *)addr, excSize);
        CACHE_TEXT_UPDATE ((void *)addr, excSize);
        }
    /* Set interrupt handlers */
    bcopy ((void *)&_func_int_handler, (void *)_EXC_OFF_INTR, intSize);
    CACHE_TEXT_UPDATE ((void *)_EXC_OFF_INTR, intSize);

#ifdef _EXC_OFF_DIRECT_INTR
    bcopy ((void *)&_func_int_handler, (void *)_EXC_OFF_DIRECT_INTR, intSize);
    CACHE_TEXT_UPDATE ((void *)_EXC_OFF_DIRECT_INTR, intSize);
#endif /* _EXC_OFF_DIRECT_INTR */

#ifdef _EXC_OFF_DECR
    bcopy ((void *)&_func_int_handler, (void *)_EXC_OFF_DECR, intSize);
    CACHE_TEXT_UPDATE ((void *)_EXC_OFF_DECR, intSize);
#endif /* _EXC_OFF_DECR */
    }
#endif  /* _WRS_CONFIG_WRHV_GUEST && !_VB_PISA_EHV */

    return (OK);
    }

   下面按excVecInit里面函数的初始化顺序讲解:

macro in target\h\arch\ppc\private\exArchLibP.h

#   define    EXT_ENT_OFF     3      /* offset for ext intEnt/excEnt */
#   define    EXT_ISR_OFF     8      /* offset for ext ISR or exc handler */
#   define    EXT_EXIT_OFF     15    /* offset for ext intExit/excExit */

描述的是在每类中断向量里面中断入口函数,处理函数,退出函数相对于这类向量地址的偏移量,目前我还没理解值为啥是这个数。

(1)excVecInit里面有:
entOffset     = EXT_ENT_OFF;
isrOffset     = EXT_ISR_OFF;
exitOffset    = EXT_EXIT_OFF;

(2)excVecInit 调用 excVecBaseSet(intVecBaseGet()); 此时才真正设置了中断向量基地址

   void excVecBaseSet(FUNCPTR * baseAddr){
        ...
        excVecBase = (vectorBase)((uint32_t)baseAddr & 0x0ffff0000);
        vxIvprSet ((int) excVecBase);
        ...
    }
 
  /* code in vxALib.s */
  FUNC_BEGIN(vxIvprGet)
	mfspr	p0, IVPR
	blr
  FUNC_END(vxIvprGet)


   FUNC_BEGIN(vxIvprSet)
	mtspr	IVPR, p0
	blr
   FUNC_END(vxIvprSet)
  
(3)excVecConnectCommon 该程序是安装所有中断默认处理的函数

 函数涉及vxWorks中一个重要的数据结构异常向量表excBlTbl[]
 typedef struct excTbl
 {
    vecTblOffset  vecOff;               /* vector offset */
    EXC_TYPE      vType;                /* exception type */
    void          (*excHandler) ();     /* exception handler routine */
    vecTblOffset  vecOffReloc;          /* relocated vector offset */
 } EXC_TBL;
  
vecTblOffset 为UINT32,在此代表的意义为相对于中断向量表基地址的偏移量(简单来说就是vecOff用来设置IVORs寄存器的,当然基地值为0的情况下)

异常向量表的表项数量和内容随CPU不同而不同,对于P2020处理器而言,其异常向量表中的表项如下:
{
    {_EXC_OFF_CRTL,	   V_CRIT_INT,  excIntHandle, 0}, /* critical int */
#ifdef _PPC_MSR_MCE
    {_EXC_OFF_MACH,	   V_MCHK_EXC,  excExcHandle, 0}, /* machine chk  */
#elif  _PPC_MSR_CE
    {_EXC_OFF_MACH,	   V_CRIT_EXC,  excExcHandle, 0}, /* machine chk  */
#endif /* _PPC_MSR_MCE */
    {_EXC_OFF_DATA,	   V_NORM_EXC,  excExcHandle, 0}, /* data storage */
    {_EXC_OFF_INST,	   V_NORM_EXC,  excExcHandle, 0}, /* instr access */
    {_EXC_OFF_INTR,	   V_NORM_INT,  excIntHandle, 0}, /* ext int */
#ifdef _EXC_OFF_DIRECT_INTR
    {_EXC_OFF_DIRECT_INTR, V_NORM_INT,  excIntHandle, 0}, /* ext int */
#endif /* _EXC_OFF_DIRECT_INTR */
    {_EXC_OFF_ALIGN,	   V_NORM_EXC,  excExcHandle, 0}, /* alignment */
    {_EXC_OFF_PROG,	   V_NORM_EXC,  excExcHandle, 0}, /* program */
    {_EXC_OFF_FPU,	   V_NORM_EXC,  excExcHandle, 0}, /* fp unavail */
    {_EXC_OFF_SYSCALL,	   V_NORM_EXC,  excExcHandle, 0}, /* syscall */
    {_EXC_OFF_APU,	   V_NORM_EXC,  excExcHandle, 0}, /* auxp unavail*/
    {_EXC_OFF_DECR,	   V_NORM_INT,  excDecrHandle, 0}, /* decrementer */
    {_EXC_OFF_FIT,	   V_NORM_INT,  excIntHandle, 0}, /* fixed timer */

    {_EXC_OFF_WD,	   V_CRIT_INT,  excIntHandle, 0}, /* watchdog */

    {_EXC_OFF_DATA_MISS,   V_NORM_EXC,  excExcHandle, 0}, /* data TLB miss */
    {_EXC_OFF_INST_MISS,   V_NORM_EXC,  excExcHandle, 0}, /* inst TLB miss */
    {_EXC_OFF_DBG,	   V_CRIT_EXC,  excExcHandle, 0}, /* debug events */
#ifdef _WRS_ALTIVEC_SUPPORT
    {_EXC_ALTIVEC_UNAVAILABLE, V_NORM_EXC,excExcHandle, 0}, /* altivec unav */
    {_EXC_ALTIVEC_ASSIST,  V_NORM_EXC, excExcHandle, 0},  /* altivec asst */
#endif /* _WRS_ALTIVEC_SUPPORT */
#ifdef _WRS_SPE_SUPPORT
    {_EXC_OFF_SPE,	   V_NORM_EXC,  excExcHandle, 0}, /* SPE */
    {_EXC_OFF_VEC_DATA,	   V_NORM_EXC,  excExcHandle, 0}, /* vector data */
    {_EXC_OFF_VEC_RND,	   V_NORM_EXC,  excExcHandle, 0}, /* vector round */
#endif /* _WRS_SPE_SUPPORT */
    {_EXC_OFF_PERF_MON,	   V_NORM_INT,  excIntHandle, 0}, /* perf monitor */
}

表格中涉及的宏target\h\arch\ppc\excPpcLib.h
macro in 
#define	_EXC_OFF_CRTL		0x0100	/* Critical Input */
#define	_EXC_OFF_MACH		0x0200	/* Machine Check */
#define	_EXC_OFF_DATA		0x0300	/* Data Storage */
#define	_EXC_OFF_INST		0x0400	/* Instruction Storage */
#define	_EXC_OFF_INTR		0x0500	/* External Input */
#define	_EXC_OFF_ALIGN		0x0600	/* Alignment */
#define	_EXC_OFF_PROG		0x0700	/* Program */
#define	_EXC_OFF_FPU		0x0800	/* Floating Point Unavailable */
#define	_EXC_OFF_SYSCALL	0x0900	/* System Call */
#define	_EXC_OFF_APU		0x0a00	/* Auxiliary Processor Unavailable */
#define	_EXC_OFF_DECR		0x0b00	/* Decrementer */
#define	_EXC_OFF_FIT		0x0c00	/* Fixed Interval Timer */
#define	_EXC_OFF_WD		0x0d00	/* Watchdog Timer */
#define	_EXC_OFF_DATA_MISS	0x0e00	/* Data TLB Error */
#define	_EXC_OFF_INST_MISS	0x0f00	/* Instruction TLB Error */
#define	_EXC_OFF_DBG		0x1000	/* Debug exception */

typedef enum excType
    {
    V_NORM_EXC = 0,
    V_NORM_INT /* 重点对象 */
#ifdef  _PPC_MSR_CE
    ,V_CRIT_EXC
    ,V_CRIT_INT
#ifdef  _PPC_MSR_MCE
    ,V_MCHK_EXC
#endif  /* _PPC_MSR_MCE */
#endif  /* _PPC_MSR_CE */
    } EXC_TYPE;

/* 中断的入口函数以及出口函数 */
LOCAL EXC_WRAPPERS excTypeRtnTbl[] =
    {
    {excEnt, excExit},                  /* V_NORM_EXC */
    {intEnt, intExit},                  /* V_NORM_INT */
#ifndef _VB_PISA_EHV
#ifdef  _PPC_MSR_CE
    {excCrtEnt, excCrtExit},            /* V_CRIT_EXC */
    {intCrtEnt, intCrtExit},            /* V_CRIT_INT */
#ifdef  _PPC_MSR_MCE
    {excMchkEnt, excMchkExit}           /* V_MCHK_EXC */
#endif  /* _PPC_MSR_MCE */
#endif  /* _PPC_MSR_CE */
#else  /* _VB_PISA_EHV */

/* XXX need spurious exception handler */
# ifdef  _PPC_MSR_CE
    {NULL, NULL},                       /* V_CRIT_EXC */
    {NULL, NULL},                       /* V_CRIT_INT */
#  ifdef  _PPC_MSR_MCE
    {NULL, NULL}                        /* V_MCHK_EXC */
#  endif  /* _PPC_MSR_MCE */
# endif  /* _PPC_MSR_CE */
#endif  /* _VB_PISA_EHV */
    };

总的说来:excVecConnectCommon 是对中断向量表做处理,根据中断向量表第一个参数vecoff,计算该类中断的内存地址位置,然后将存根机器代码拷贝到该位置上,将xxEnt,xxExit,handler放置各自对应的位置中    
/* copy the stub to the vector location */
bcopy((char *)stub, (char *)cVec, stubSize);

(4)excIvorInit 设置IVOR寄存器
macro in excPpcLib.h
/* Mappings between vector names and corresponding IVORs, for excALib.s */
#define	IVOR0_VAL   _EXC_OFF_CRTL	/* Critical Input */
#if ((defined  PPC_440x5) || (CPU == PPC465))
#define	IVOR1_VAL   _EXC_OFF_MCRECOV	/* recoverable Machine Check */
#else	/* PPC_440x5 || PPC465 */
#define	IVOR1_VAL   _EXC_OFF_MACH	/* Machine Check */
#endif	/* PPC_440x5 || PPC465 */
#define	IVOR2_VAL   _EXC_OFF_DATA	/* Data Storage */
#define	IVOR3_VAL   _EXC_OFF_INST	/* Instruction Storage */
#define	IVOR4_VAL   _EXC_OFF_INTR	/* External Input */
#define	IVOR5_VAL   _EXC_OFF_ALIGN	/* Alignment */
#define	IVOR6_VAL   _EXC_OFF_PROG	/* Program */
#define	IVOR7_VAL   _EXC_OFF_FPU	/* Floating Point Unavailable */
#define	IVOR8_VAL   _EXC_OFF_SYSCALL	/* System Call */
#define	IVOR9_VAL   _EXC_OFF_APU	/* Auxiliary Processor Unavailable */
#define	IVOR10_VAL  _EXC_OFF_DECR	/* Decrementer */
#define	IVOR11_VAL  _EXC_OFF_FIT	/* Fixed Interval Timer */
#define	IVOR12_VAL  _EXC_OFF_WD		/* Watchdog Timer */
#define	IVOR13_VAL  _EXC_OFF_DATA_MISS	/* Data TLB Error */
#define	IVOR14_VAL  _EXC_OFF_INST_MISS	/* Instruction TLB Error */
#define	IVOR15_VAL  _EXC_OFF_DBG	/* Debug exception */

code in .\target\src\arch\ppc\excALib.s
FUNC_EXPORT(excIvorInit)
FUNC_BEGIN(excIvorInit)
	li	p0, IVOR0_VAL
	mtspr	IVOR0, p0
	li	p0, IVOR1_VAL
	mtspr	IVOR1, p0
	li	p0, IVOR2_VAL
	mtspr	IVOR2, p0
	li	p0, IVOR3_VAL
	mtspr	IVOR3, p0
	li	p0, IVOR4_VAL
...
FUNC_END(excIvorInit)

 存根代码表:

LOCAL INSTR excConnectCode[]=
    {
    /*  data	      word    byte     opcode  operands		      */
    0x7c7343a6,	    /*  0     0x00     mtspr   SPRG3, p0	      */
#if	defined(_EXC_OFF_CRTL)
# if	defined(T4_ERRATUM_CPU6198) && defined(_WRS_CONFIG_SMP)
    0x7c6000a6,	    /*  1     0x04     mfmsr   p0		      */
    0x546303da,	    /*  2     0x08     rlwinm  p0,p0,0,15,13  clear MSR[CE] */
    0x7c7b8ba6,	    /*  3     0x0c     mtspr   MCSRR1,p0	      */
    0x3c600000,	    /*  4     0x10     lis     p0,HI(mtmsrwa)	      */
    0x60630000,	    /*  5     0x14     ori     p0,p0,LO(mtmsrwa)      */
    0x7c7a8ba6,	    /*  6     0x18     mtspr   MCSRR0,p0	      */
    0x4c00004c,	    /*  7     0x1c     rfmci			      */
# else
    0x7c6000a6,	    /*  1     0x04     mfmsr   p0		      */
    0x546303da,	    /*  2     0x08     rlwinm  p0,p0,0,15,13  clear MSR[CE] */
    0x7c600124,	    /*  3     0x0c     mtmsr   p0		      */
    0x60000000,	    /*  4     0x10     nop			      */
# endif	/* T4_ERRATUM_CPU6198 && _WRS_CONFIG_SMP */
#elif	defined(_WRS_PPC_64BIT)
    0x7c6000a6,	    /*  1     0x04     mfmsr   p0		      */
    0x786300c0,	    /*  2     0x08     clrldi  p0,p0,3 clear MSR[SF]  */
    0x7c600164,	    /*  3     0x0c     mtmsrd  p0		      */
    0x4c00012c,	    /*  4     0x10     isync			      */
#endif	/* _EXC_OFF_CRTL, _WRS_PPC_64BIT */
    /* If either of the above, add 4 words/0x10 bytes to following offsets */
    0x7c6802a6,	    /*  1     0x04     mflr    p0		      */
    0x48000001,	    /*  2(6)  0x08/18  bl      xxxEnt		      */
    0x38610000,	    /*  3     0x0c     addi    r3, sp, 0	      */
    0x9421fff0,	    /*  4     0x10     stwu    sp, -FRAMEBASESZ(sp)   */
    0x48000001,	    /*  5(9)  0x14/24  bl      xxxHandler	      */
    0x38210010,	    /*  6     0x18     addi    sp, sp, FRAMEBASESZ    */
    0x48000001	    /*  7(11) 0x1c/2c  bl      xxxExit		      */
    };

系统起来后在vxWorks shell 中输入d 0x500 (外部中断的内存地址),可以看到数据和excConnectCode存根一致

中断向量内存图分析:

 P2020 BSP 中使用的中断向量内存分布图:
 -------------
 |    0x0    |   vector base address
 -------------
 |   0x100   |   Critical Input
 ------------- 
 |   0x200   |   Machine Check
 ------------- 
 |   0x300   |   Data Storage
 ------------- 
 |   0x400   |   Instruction Storage
 ------------- 
 |   0x500   |   External Input[0x500开始处存放excConnectCode存根]
 |  cVec[3]  |   存放c函数地址高16位 cVec = 0x500 [3] = EXT_ENT_OFF
 |  cVec[4]  |   存放entInt函数地址低16位 
 |  cVec[8]  |   存放&excHandlers[n]地址,其中excHandlers存放默认的ISR,该函数会读取IACK      
 |  cVec[9]  |   [8] = EXT_ISR_OFF,存放ISR函数的低16位  
 |  cVec[15] |   存放exitInt函数地址高16位 [15] = EXT_EXIT_OFF
 |  cVec[16] |   存放exitInt函数地址低16位
 |   ...     |
 ------------- 
 |   0x600   |   Alignment
 ------------- 
 |   0x700   |   Program 
 ------------- 
 |   ...     |   /* execPpcLib.h 有定义 */
 -------------

总的说来中断处理流程为:中断产生后,e500内核根据MSR,IVPR,IVORs等寄存器来确认异常类型,
以及该类中断向量地址,然后进行存根机器代码-->entInt(汇编)保存现场-->ISR(确认中断源,
处理的functions)-->exitInt(汇编)恢复现场,至于PC<-->SRR0 ,MSR<-->SRR1是在存根,
还是在entInt与exitInt中操作,有待细化...

6.总结

   参考文献:

   1.POWERPC(e500) 基于linux 中断过程分析

   2.PowrPC基于vxWorks异常堆栈切换分析

   3.PowerPC汇编指令以及通用/专用寄存器介绍

   4.PowerPC OEA寄存器集介绍

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咸鱼看到猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值