Cortex-A与Cortex-M对比

一、芯片运行模式

1、Cortex-M

        M系列处理器仅支持2种模式,2种访问级别

        1)Thread mode(线程模式):该模式下的执行可以是非特权的特权的。运行异常服务程序以外的程序。

        2)Handler mode(中断模式):该模式下的执行始终处于特权级。异常触发,处理器会切换为特权级访问,所有异常处理都在 Handler模式下执行。运行异常服务程序。

        注意:

        --特权级和非特权级可以理解为内核态和用户态。非特权执行会限制对某些资源的访问,如用户级禁止访问和修改特殊寄存器组的(第三节说特殊寄存器组)。

        --CONTROL寄存器的bit0仅在特权级别可访问修改,bit0用于切换线程模式下的访问级别。在系统复位后,处理器进入线程模式+特权级+默认使用MSP。运行在用户级的代码想要进入特权级,必须触发异常(如SVC异常),在异常的ISR中修改CONTROLbit0。(M系列的特殊寄存器组看第三节)。

   --ARM系统的“异常”指:打断程序顺序执行的事件都称为异常。外部中断产生或者当有指令执行了“非法操作”, 或者访问被禁的内存区间, 因各种错误触发 fault异常。还可以代码主动请求进入异常状态(利用SVC系统调用)。使用主动进入的例子:

//__svc是编译器关键字,编译器会插入svc指令,触发svc异常,0是svc编号,可以根据svc_args获取
__svc(0) uint32_t svc_service_add(uint32_t x, uint32_t y);
void SVC_Handler_C(uint32_t* svc_args) {    // C语言服务函数
    // ... ...
}
__asm void SVC_Handler(void) {  // svc向量入口
    IMPORT SVC_Handler_C        // 导入外部函数
    // ... ...
    B SVC_Handler_C             // 跳转
}
int main(void) {
    uint32_t  result = svc_service_add(100, 200);    // 调用
}

Cortex-A与Cortex-M对比_第1张图片

Cortex-A与Cortex-M对比_第2张图片

【顺带一提RTOS】:

        M3跑RTOS系统的时候,一旦OS启动后开始运行任务,任务是要运行在 用户态+PSP堆栈指针,默认上电系统运行在线程模式+特权级+默认使用MSP,所以要运行系统必须完成 CPU模式切换和栈指针切换(核心就是修改CONTROL寄存器或者通过EXC_RETURN机制间接修改)。

  •        方式1. 系统启动后,默认在特权级,可以直接使用MSR指令访问并修改CONTROL;
  •        方式2. 通过中断机制,一般是SVC或PendSV触发异常,进入ISR去修改CONTROL寄存器;

2、Cortex-A:1,3列即是A芯片常见的工作模式(除了用户模式是用户级,其他模式都是特权级)。系统上电之后,CPU默认进入SVC模式。

Cortex-A与Cortex-M对比_第3张图片

二、中断系统

        抢占优先级:抢占优先级高的中断发生,低优先级中断执行过程会被打断,形成中断嵌套去执行高优先级中断;

        响应优先级:同时来两个中断,其抢占优先级与正在执行的一个中断相同,正在执行的中断不会被打断,等它执行完后,再执行响应优先级高的那个;

        注意:若同时来两个中断,优先考虑抢占优先级,再考虑响应优先级,最后再考虑向量表顺序;抢占优先级具备打断性。

1、Cortex-MNVIC(内嵌向量中断控制器),具备抢占优先级和响应优先级。

        中断向量表:表中列举了一款芯片所有的中断向量,包括芯片外设的所有中断:

Cortex-A与Cortex-M对比_第4张图片

        CPU收到的就是某一个具体的中断请求,自己跳转到对应的具体的某个ISR。 

2、Cortex-AGIC(general interrupt controller),具备抢占优先级和响应优先级。

        中断向量表:1,2列即是A芯片的所有中断向量:

Cortex-A与Cortex-M对比_第5张图片

         GIC 接收到外部中断信号以后就会报给 ARM 内核,但是 ARM 内核只提供了4个信号给 GIC 来汇报中断情况:VFIQ(虚拟快速中断)VIRQ(虚拟外部中断)FIQ IRQ

Cortex-A与Cortex-M对比_第6张图片

 GIC 将众多的中断源分为3类:

  1. SPI(共享中断):所有 Core 共享的中断,所有核都可以处理的(eg: GPIO中断、串口中断这些);
  2. PPI(私有中断):GIC支持多核CPU,每个核自己独有的中断;
  3. SGI(软件中断):通过向寄存器GICD_SGIR 写入数据来触发,系统会使用 SGI 中断来完成多核之间的通信。

        中断源有很多,区分不同的中断源通过分配一个的中断ID,ID绑定具体哪个中断源半导体厂商去定义:

#define NUMBER_OF_INT_VECTORS 160 /* 中断源 160 个,SGI+PPI+SPI*/ 
typedef enum IRQn { 
    /* Auxiliary constants */ 
    NotAvail_IRQn = -128, 

    /* Core interrupts */ 
    Software0_IRQn = 0,  
    Software1_IRQn = 1, 
      ......  ...... 
    Reserved158_IRQn = 158, 
    PMU_IRQ2_IRQn = 159 
} IRQn_Type; 

三、寄存器组

1、Cortex-M:M系列处理器实现了2个堆栈指针:MSP和PSP。在 Handler 模式下,处理器使用 Main SP, 在Thread模式下,它可以使用任一堆栈指针(上电系统运行在线程模式+特权级+默认使用MSP。使用CONTROL寄存器的bit1控制。堆栈指针的切换在异常服务的始末由硬件自动切换。

  1. 【特殊功能寄存器】
  2.         程序状态寄存器组(PSRs或xPSR);
  3.         中断屏蔽寄存器组(PRIMASK, FAULTMASK,以及BASEPRI);
  4.         控制寄存器(CONTROL)---设置堆栈指针/系统访问级别,对于RTOS运行非常重要!

Cortex-A与Cortex-M对比_第7张图片

2、Cortex-A:只有用户模式处于用户级,其他模式都处于特权级!

Cortex-A与Cortex-M对比_第8张图片

另外,A系列还有重要的协处理器寄存器组:

CP15 协处理器通过MRC 或者 MCR 指令访问,指令中的控制位 CRnopc1CRm opc2 通过不同的搭配可以选择c1~c15是作为什么寄存器被设置。

  1. c1作为STRAL 寄存器: 使能或禁止 MMUI/D Cache,设置允许软件可配置中断向量表基地址;(这个寄存器在前面的文章uboot启动分析过)
  2. c12作为VBAR寄存器:设置中断向量表基地址;
  3. c15作为CBAR寄存器:获取GIC基地址;

 四、MPU-MMU

       1、 MPU(内存保护单元):Cortex-M中,同属于CPU中的一个硬件单元。只具备内存保护的作用,防止进程访问不是分配给它内存

        2、MMU(内存管理单元):Cortex-A中,同属于CPU中的一个硬件单元。主要是虚拟内存虚拟地址到物理地址的转换)和内存保护。MMU可以看作是MPU的超集。

五、Cortex-A裸机中断coding流程

通过中断号判断决定对应的ISR调用,ISR通用写法流程为:

1)中断处理函数形式:

    typedef void (*system_irq_handler_t) (unsigned int giccIar, void *param); 

2)中断处理结构体:

typedef struct _sys_irq_handle 
{ 
     system_irq_handler_t irqHandler; 	/* 中断处理函数 */ 
     void *userParam; 					/* 中断处理函数参数:中断号 */
} sys_irq_handle_t;

3)系统有多少个中断ID,就定义多少个sys_irq_handle_t类型的变量:

static sys_irq_handle_t irqTable[160];

4)根据系统内部定义的IRQn_Type注册对应的ISR:

void system_register_irqhandler(IRQn_Type irq, 
                                system_irq_handler_t handler,
                                void *userParam) 
{ 
    irqTable[irq].irqHandler = handler; 
    irqTable[irq].userParam = userParam; 
}

5)根据中断ID调用对于的处理函数,中断发生时可读取相关寄存器获取对应中断号:

void system_irqhandler(unsigned int x_ID) 
{
    ... ...
    irqTable[x_ID].irqHandler(x_ID, irqTable[x_ID].userParam); 
    ... ...
}

6)在编写的汇编文件xx.S(C语言环境的准备)中调用C编写的中断函数:在汇编调用 C 函数的时候建议形参不要超过 4 个,形参可以由 r0~r3 这四个寄存器来传递。

IRQ_Handler:
    ... ...
    ldr r0, [r1, #0XC]            // 从GICC_IAR 寄存器获取中断号,通过r0传递参数system_irqhandler
    ldr r2, =system_irqhandler    // 实际的中断函数

    ... ...

你可能感兴趣的:(嵌入式,操作系统,linux,嵌入式,RTOS)