2.5 移植rvos - preemptive

gd32vf103中断模式

gd32vf103的中断有2种模式:向量处理模式和非向量处理模式

非向量处理模式

中断被处理器内核响应后,处理器会直接跳入到所有非向量中断共享的入口地址,该入口地址可以通过软件进行设置。
非向量中断入口地址的选择方式如下:
mtvec 中记录了异常入口地址
mtvt2 中记录了非向量中断入口地址
mtvt2中有个有个bit0,值为0的时候,非向量中断入口地址是mtvec中的地址,这个时候异常和中断使用一个入口地址。值为1的时候,非向量中断入口地址是mtvt2中记录的地址,这个时候,非向量中断的入口地址是mtvt2,而异常的入口地址是mtevc,异常和中断的入口地址是分开的。

启动代码中,设置了入口地址为mtvt2中的地址

    /* 自定义
     * mtvt2 用于指定 ECLIC 非向量模式的中断 common-code 入口地址
     * bit2~bit31 用于存放common-code入口地址 所以改地址需要4bytes对齐
     * bit0:
     *  0: 入口地址有mtvec决定
     *  1: 入口地址就是设置的值
     */
    la t0, irq_entry
    csrw CSR_MTVT2, t0
    csrs CSR_MTVT2, 0x1

向量处理模式

如果被配置成为向量处理模式,则该中断被处理器内核响应后,处理器会直接跳入该中断的向
量入口(Vector Table Entry)存储的目标地址,即该中断源的中断服务程序。

    /*  
     * Intialize ECLIC vector interrupt
     * base address mtvt to vector_base
     */  
    /* mtvt ECLIC 中断向量表的基地址 
     * 填充基地址
     */
    la t0, vector_base
    csrw CSR_MTVT, t0

移植rvos的难点

首先rvos的timer中断函数处理是这样的

# 这里很明显的是用的非向量处理模式
# risc-v中异常和中断统称为trap
# 和上面介绍gd32vf103非向量模式中说的
# mtvt2中有个有个bit0,值为0的时候,非向量中断入口地址是mtvec中的地址,这个时候异常和中断使用一个入口地址
# 这里是一个意思
# 只是gd32vf103自己加了一个可以将异常和中断入口函数分开的功能

# interrupts and exceptions while in machine mode come here.
.globl trap_vector
# the trap vector base address must always be aligned on a 4-byte boundary
.align 4
trap_vector:
	  # 进入trap第一件事,保存任务上下文
	  # 难点在进入就保存上下文这里
	  # gd32vf103中使用的是nuclei sdk
	  # 即使是使用非向量模式中的,将中断和异常分开的方法,也是有上下文保护及csr寄存器保护的功能
	  # 这部分代码后面分析
    # save context(registers).
    csrr    t6, mscratch    # read t6 back from mscratch
    reg_save t6

    # save mepc to context of current task
    csrr    a0, mepc
    sw  a0, 124(t6)

    # call the C trap handler in trap.c
    csrr    a0, mepc
    csrr    a1, mcause
    # 调用处理程序
    # trap_handler中会判断是不是timer中断,是的话则会调用timer中断处理函数进行schedule()
    call    trap_handler

    # trap_handler will return the return address via a0. 
    csrw    mepc, a0

    # restore context(registers).
    csrr    t6, mscratch
    reg_restore t6

    # return to whatever we were doing before trap.
    mret

OK,现在看一下gd32vf103 nuclei sdk的处理

.global irq_entry
.weak irq_entry
/* This label will be set to MTVT2 register */
irq_entry:
	  /* 这里可以看到进入的第一件事就是保存上下文和csr 
	   */
    /* Save the caller saving registers (context) */
    SAVE_CONTEXT
    /* Save the necessary CSR registers */
    SAVE_CSR_CONTEXT

    /* This special CSR read/write operation, which is actually
     * claim the CLIC to find its pending highest ID, if the ID
     * is not 0, then automatically enable the mstatus.MIE, and
     * jump to its vector-entry-label, and update the link register
     */
    csrrw ra, CSR_JALMNXTI, ra

    /* Critical section with interrupts disabled */
    DISABLE_MIE

    /* Restore the necessary CSR registers */
    RESTORE_CSR_CONTEXT
    /* Restore the caller saving registers (context) */
    RESTORE_CONTEXT

    /* Return to regular code */
    mret

由于进入就保存上下文和csr,而且我们又不想修改这部分sdk代码,所以这里的解决方法是使用向量质量模式

使用向量处理模式

我们的调度是使用的timer中断,这里我们重写这个中断处理函数

.global eclic_mtip_handler
.align 4
eclic_mtip_handler:
    # save context(registers).
    csrr    t6, CSR_MSCRATCH
    reg_save t6

    # save mepc to context of current task
    csrr    a0, mepc
    sw  a0, 124(t6)

    call timer_handler

    mret

如何将中断改成向量模式?

void mtimer_init(void)
{
    uint64_t ticks = SYSTICK_TICK_CONST;
    SysTick_Config(ticks);
    ECLIC_DisableIRQ(SysTimer_IRQn);
    ECLIC_SetLevelIRQ(SysTimer_IRQn, configKERNEL_INTERRUPT_PRIORITY);

    /*  
     * mcu gd32vf103 Each interrupt can be configured with vectored interrupt or non-vectored interrupt.
     * For non-vector interrupts, the interrupt function entry is irq_entry. When processing the interrupt,
     * SAVE_CONTEXT and SAVE_CSR_CONTEXT will be executed first to save the context and csr registers.
     * After the rvos timer interrupt is triggered, we want to save the task directly to the context information,
     * so the non-vector interrupt mode cannot be used for the timer interrupt, but the vector interrupt is used.
     *
     * In the vector processing mode, because the processor does not save the context before jumping into the
     * interrupt service routine, in theory, the interrupt service routine function itself cannot make sub-function
     * calls (that is, it must be a Leaf Function).
     * If the interrupt service routine function accidentally calls other sub-functions (not Leaf Function),
     * it will cause a function error if it is not handled. In order to avoid this accidental error situation,
     * as long as the special __attribute__ ((interrupt)) is used to modify the interrupt service routine function,
     * then the compiler will automatically make a judgment. When function, it will automatically insert a piece of code to save the context.
     *
     * In general, for the vector processing mode, we need to write an interrupt processing function in C language,
     * and it must be modified with __attribute__ ((interrupt)). But when this function is called, there will be a stack operation first.
     * When we need it, the first thing to call this function is to save the task until morning and afternoon, which conflicts with our needs.
     *
     * So we use the vector processing mode here, but do not use the __attribute__ ((interrupt)) modification.
     */

    ECLIC_SetShvIRQ(SysTimer_IRQn, ECLIC_VECTOR_INTERRUPT);
    ECLIC_EnableIRQ(SysTimer_IRQn);
    __enable_irq();
}

关键语句
ECLIC_SetShvIRQ(SysTimer_IRQn, ECLIC_VECTOR_INTERRUPT);

向量模式需要注意的地方

向量模式中断服务程序函数不小心调用了其他的子函数(不是 Leaf Function),如果不加处理则会造成功能的错误。为了规避这种不小心造成的错误情形,只要使用了特殊的 attribute ((interrupt)) 来修饰该中断服务程序函数,那么编译器会自动的进行判断,当编译器发现该函数调用了其他子函数时,便会自动的插入一段代码进行上下文的保存。

当前移植到时候,如果中断函数写成c函数,那么执行这个中断函数的时候,编译器自动会加上入栈操作,所以不能写成c函数。但是写成汇编的话,无法使用**attribute ((interrupt))** 修饰,
这里有些纠结。

但是为什么最后这里决定使用汇编呢?
因为测试发现调度完成后,switch_to调度完成后,通过mret返回要切换的任务中执行,此时寄存器数据是上次调度时候保存的值。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

dianlong_lee

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

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

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

打赏作者

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

抵扣说明:

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

余额充值