内核移植笔记 Cortex-M移植

常用寄存器

PRIMASK寄存器
为1位宽的中断屏蔽寄存器。在置位时,它会阻止不可屏蔽中断(NMI)和HardFault异常之外的所有异常(包括中断)。
实际上,它是将当前异常优先级提升为0,这也是可编程异常/中断的最高优先级。

FAULTMASK寄存器
FAULTMASK与PRIMASK相类似,但同时它能屏蔽HardFault异常,它实际上是将异常优先级提升到了-1。

程序状态寄存器(xPSR)
xPSR包含:

  • 应用PSR(APSR)
  • 执行PSR(EPSR)
  • 中断PSR(IPSR)

在这里插入图片描述
注:GE 在 Cortex-M4 等 ARMv7E-M 处理器中存在,在 Cortex-M3 处理器中则不可用。

  • N:负标志
  • Z:零标志
  • C:进位(或者非借位)标志
  • V:溢出标志
  • Q:包含标志
  • GE:大于或等于标志
  • ICI/IT:中断继续指令位
  • T:Thumb状态,总是1,清除此位会引起错误异常
  • 异常变化:表示处理器正在处理的异常

中断向量表
Cortex-M系列处理器的中断向量表位于0x00000000,单Cortex-M3/4系列提供了SCB_VTOR,所以中断向量表的位置位于0x00000000+SCB_VTOR。

异常相关指令

  • CPSIE I:使能中断(清除PRIMASK)
  • CPSID I:禁止中断(设置PRIMASK),NMI和HardFault不受影响
  • CPSIE F:使能中断(清除FAULTMASK)
  • CPSID F:禁止中断(设置FAULTMASK),NMI不受影响

移植过程

在嵌入式领域有多种不同CPU架构,例如Cortex-M,ARM920T、MIPS、RISC-V等等。
为了使RT-Thread能够在不同CPU架构的芯片上运行,RT-Thread提供了一个libcpu抽象层适配不同的CPU架构

libcpu层向上对内核提供统一的接口,包括全局中断的开关,线程栈的初始化,上下文切换等。

libcpu抽象层向下提供了一套统一的CPU架构移植接口,这部分接口包含了全局中断开关函数,线程上下文切换函数,时钟节拍的配置和中断函数、Cache等等内容。

libcpu移植相关API,要对CPU进行移植只需要实现这些接口

关闭全局中断

/*rt_base_t rt_hw_interrupt_disable(void);*/

.global rt_hw_interrupt_disable
.type rt_hw_interrupt_disable,%function

rt_hw_interrupt_disable:
	MRS R0,PRIMASK ;将PRIMASK关中断前的状态存入R0,并作为函数返回值返回
	CPSID I	;关闭中断
	BX LR
/*void rt_hw_interrupt_enable(rt_base_t level); level是调用关中断函数时的返回值,代表关中断前PRIMASK的值*/
.global rt_hw_interrupt_enable
.type rt_hw_interrupt_enable,%function

rt_hw_interrupt_enable:
	MSR PRIMASK,R0 ;将level写入PRIMASK寄存器,恢复关中断前的状态
	BX LR

实现线程栈初始化

在动态创建线程和初始化线程的时候,会使用到内部的线程初始化函数_rt_thread_init(),_rt_thread_init()函数会调用栈初始化函数rt_hw_stack_init(),在栈初始化函数里会手动构造一个上下文内容,这个上下文内容将被作为每个线程第一次执行的初始值。
上下文在栈里的排布如图:
在这里插入图片描述

rt_uint8_t *rt_hw_stack_init(void *tentry,
							void *parameter,
							rt_uint8_t *stack_addr,
							void *texit)
{
	struct stack_frame *stack_frame;
    rt_uint8_t         *stk;
    unsigned long       i;

    stk  = stack_addr + sizeof(rt_uint32_t);
    stk  = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8);
    stk -= sizeof(struct stack_frame);

    stack_frame = (struct stack_frame *)stk;

    /* init all register */
    for (i = 0; i < sizeof(struct stack_frame) / sizeof(rt_uint32_t); i ++)
    {
        ((rt_uint32_t *)stack_frame)[i] = 0xdeadbeef;
    }
     /* 根据 ARM  APCS 调用标准,将第一个参数保存在 r0 寄存器 */
    stack_frame->exception_stack_frame.r0  = (unsigned long)parameter; 
    stack_frame->exception_stack_frame.r1  = 0;                        /* r1 */
    stack_frame->exception_stack_frame.r2  = 0;                        /* r2 */
    stack_frame->exception_stack_frame.r3  = 0;                        /* r3 */
    /* 将 IP(Intra-Procedure-call scratch register.) 设置为 0 */
    stack_frame->exception_stack_frame.r12 = 0;    
    //将线程退出函数的地址保存在lr寄存器
    stack_frame->exception_stack_frame.lr = (unsigned long) texit;
    //将线程入口函数的地址保存在pc寄存器
    stack_frame->exception_stack_frame.pc = (unsigned long) tentry;
    /* 设置 psr 的值为 0x01000000L,表示默认切换过去是 Thumb 模式 */
    stack_frame->exception_stack_frame.psr = 0x01000000L;              /* PSR */
    return stk;
}

实现上下文切换

在Cortex-M里面上下文切换都是统一使用PendSV异常来完成。
为了能适应不同的CPU架构,RT-Thread的libcpu抽象层需要实现三个线程相关的函数:

  1. rt_hw_context_switch_to():没有来源线程,切换到目标线程,在调度器启动第一个线程的时候被调用。
  2. rt_hw_context_switch():在线程环境下,从当前线程切换到目标线程。
  3. rt_hw_context_switch_intrrupt():在中断环境下,从当前线程切换到目标线程。

PendSV_Handler

产生PendSV异常时,Cortex-M系列处理器硬件会自动将from线程的PSR、PC、LR、R12、R3-R0压栈,因此在PendSV_Handler中,我们需要把from线程的R11-R4压栈,并把to线程的R11-R4弹出。修改PSP为to线程的栈地址,在退出PendSV中断时,硬件会自动弹出R3-R0、R12、LR、PC、PSR寄存器。

/*R0->保存from线程的栈,R1->保存to线程的栈*/
/*PSR PC LR...被放入from线程的栈*/
.global PendSV_Handler
.type PendSV_Handler, %function

PendCV_Handler:
	MRS R2,PRIMASK
	CPSID	I

	LDR R0, =rt_thread_switch_interrupt_flag
	LDR R1,[R0]
	CBZ R1,pendsv_exit 

	MOV R1,#0
	STR R1,[R0]

	LDR R0,=rt_interrupt_from_thread
	LDR R1,[R0]
	CBZ R1,switch_to_thread

	MRS R1,PSP
	STMFD R1!,{R4-R11} //将from线程的数据寄存器R4-R11压栈
	LDR R0,[R0] 
	STR R1,[R0]

switch_to_thread:
	LDR R1,=rt_interrupt_to_thread
	LDR R1,[R1]
	LDR R1,[R1]

	LDMFD R1!,{R4-R11} //将to线程寄存器弹出
	MSR PSP,R1 //更新栈指针

pendsv_exit:
	MSR PRIMASK,R2
	ORR LR,LR,#0X4
	BX LR

rt_hw_context_switch_to

在这里插入图片描述

.global rt_hw_context_switch_to
.type rt_hw_context_switch_to,%function

rt_hw_context_switch_to:
	LDR R1,=rt_interrupt_to_thread
	STR R0,[R1]

	LDR R1,=rt_interrupt_from_thread
	MOV R0,#0
	STR R0,[R1]
	
	LDR R1,=rt_thread_switch_interrupt_flag
	MOV R0,#1
	STR R0,[R1]

	
	LDR R0,=SHPR3
	LDR R1,=PENDSV_PRI_LOWEST
	LDR.W R2,[R0,#0]
	ORR     R1, R1, R2              /* modify */
    STR     R1, [R0]                /* write-back */

	LDR     R0, =ICSR               /* trigger the PendSV exception (causes context switch) */
    LDR     R1, =PENDSVSET_BIT        ;0x10000000,ICSR 的第 28 位为 PendSV set-pending bit.
    STR     R1, [R0]                ;Writing 1 to this bit is the only way to set the PendSV exception state to pending.

	//恢复MSP 
	LDR R0,=SCB_VTOR
	LDR R0,[R0] //读取中断向量表的位置
	LDR R0,[R0] //读取 SP初始值
	NOP
	MSR MSP,R0 ;将SP初始值赋给MSP

	   /* enable interrupts at processor level */
    CPSIE   F
    CPSIE   I
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

饼干饼干圆又圆

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

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

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

打赏作者

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

抵扣说明:

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

余额充值