Cortex-M与RISC-V区别

环境

Cortex-M以STM32H750为代表,RISC-V以芯来为代表

RTOS版本为RT-Thread 4.1.1


寄存器

在这里插入图片描述


RISC-V

在这里插入图片描述


常用汇编

RISC-V

关于STORE x4, 4(sp)这种寄存器前面带数字的写法,其意思为将x4的值存入sp+4这个地址,即前面的数字表示偏移的意思

反之LOAD表示从内存里面取值

la

地址加载 (Load Address). 伪指令(Pseudoinstruction), RV32I and RV64I.

la rd, symbol x[rd] = &symbol

例如将函数irq_entry()的地址放入t0中

la t0, irq_entry

mv

移动(Move). 伪指令(Pseudoinstruction), RV32I and RV64I.

mv rd, rs1 x[rd] = x[rs1]

把寄存器 x[rs1]复制到 x[rd]中。实际被扩展为 addi rd, rs1, 0

addi

加立即数(Add Immediate). I-type, RV32I and RV64I.

把符号位扩展的立即数加到寄存器 x[rs1]上,结果写入 x[rd]。忽略算术溢出。

addi rd, rs1, immediate x[rd] = x[rs1] + sext(immediate)

add

加 (Add). R-type, RV32I and RV64I.

把寄存器 x[rs2]加到寄存器 x[rs1]上,结果写入 x[rd]。忽略算术溢出。

add rd, rs1, rs2 x[rd] = x[rs1] + x[rs2]

压缩形式:c.add rd, rs2; c.mv rd, rs2

Cortex-M


中断与异常处理

中断与异常入口

Cortex-M

Cortex-M在启动文件中会初始化一个中断向量表,所有的异常和中断根据这个表的地址跳转

; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size

__Vectors       DCD     __initial_sp                      ; Top of Stack
                DCD     Reset_Handler                     ; Reset Handler
                DCD     NMI_Handler                       ; NMI Handler
                DCD     HardFault_Handler                 ; Hard Fault Handler
                DCD     MemManage_Handler                 ; MPU Fault Handler
                DCD     BusFault_Handler                  ; Bus Fault Handler
                DCD     UsageFault_Handler                ; Usage Fault Handler
                DCD     0                                 ; Reserved
                DCD     0                                 ; Reserved
                DCD     0                                 ; Reserved
                DCD     0                                 ; Reserved
                DCD     SVC_Handler                       ; SVCall Handler
                DCD     DebugMon_Handler                  ; Debug Monitor Handler
                DCD     0                                 ; Reserved
                DCD     PendSV_Handler                    ; PendSV Handler
                DCD     SysTick_Handler                   ; SysTick Handler

                ; External Interrupts
                DCD     WWDG_IRQHandler                   ; Window WatchDog interrupt ( wwdg1_it)                                         
                DCD     PVD_AVD_IRQHandler                ; PVD/AVD through EXTI Line detection       

RISC-V

RISC-V的异常会跳入所有异常共享的异常处理程序入口mtvec,在启动文件中的初始化如下:

exc_entry由汇编编写,其中又会调用core_exception_handler()

    /*
     * Set Exception Entry MTVEC to exc_entry
     * Due to settings above, Exception and NMI
     * will share common entry.
     */
    la t0, exc_entry
    csrw CSR_MTVEC, t0

ECLIC 的每个中断源均可以设置成向量或者非向量处理(通过寄存器 clicintattr[i]的 shv 域),其要点如下:

当 mtvec.MODE != 6’b000011 时,处理器使用默认中断模式

mtvec.MODE = 6’b000011 时,处理器使用ECLIC 中断模式,推荐使用此模式。

    /* Set the interrupt processing mode to ECLIC mode */
    la t0, 0x3f
    csrc CSR_MTVEC, t0
    csrs CSR_MTVEC, 0x3
  • 如果被配置成为向量处理模式,则该中断被处理器内核响应后,处理器直接跳入该中断的向量入口(Vector Table Entry)存储的目标地址

  • 如果被配置成为非向量处理模式,则该中断被处理器内核响应后,处理器直接跳入所有中断共享的入口地址

    默认情况下异常和所有非向量中断共享入口地址(不推荐),推荐将 CSR 寄存器 mtvt2 的最低位设置为 1,则所有非向量中断共享的入口地址由 CSR 寄
    存器 mtvt2 的值(忽略最低 2 位的值)指定

    /*
         * Set ECLIC non-vector entry to be controlled
         * by mtvt2 CSR register.
         * Intialize ECLIC non-vector interrupt
         * base address mtvt2 to irq_entry.
         */
        la t0, irq_entry
        csrw CSR_MTVT2, t0
        csrs CSR_MTVT2, 0x1
    

进入irq_entry后会自动关闭中断MIE

保存完上下文之后会调用对应中断服务程序,在跳入中断服务程序的同时,硬件也会同时打开中断的全局使能

中断服务程序执行完成之后需要将中断全局使能再次关闭,保证恢复上下文的原子性

MPIE会记录异常发生前得MIE值,以便异常结束时恢复到原来的值

csrrw ra, CSR_JALMNXTI, ra

在跳入中断服务程序的同时,“csrrw ra, CSR_JALMNXTI, ra”指令还会达到 JAL(Jump and Link)的效果,硬件同时更新 Link 寄存器的值为该指令的 PC 自身作为函数调用的返回地址。因此,从中断服务程序函数返回后会回到该“csrrw ra,CSR_JALMNXTI, ra”指令重新执行,重新判断是否还有中断在等待(Pending),从而达到中断咬尾的效果。如果没有中断在等待(Pending),则该指令相当于是个 Nop 指令不做任何操作。

对于中断嵌套,会重新从irq_entry进入


保存上下文

Cortex-M

当Cortex-M开始响应一个中断时,会自动完成如下操作:

  • 入栈: 自动把8个寄存器的值压入栈

  • 取向量:从向量表中找出对应的服务程序入口地址

  • 选择堆栈指针MSP/PSP,更新堆栈指针SP,更新连接寄存器LR,更新程序计数器PC

响应异常的第一个行动,就是自动保存现场的必要部分:依次把xPSR, PC, LR, R12以及R3‐R0由硬件自动压入适当的堆栈中:如果当响应异常时,当前的代码正在使用PSP,则压入PSP,即使用线程堆栈;否则压入MSP,使用主堆栈。一旦进入了服务例程,就将一直使用主堆栈。

压栈顺序如下:

地址(设原SP为 N-0)寄存器被保存的顺序
N-4xPSR2
N-8PC1
N-12LR8
N-16R127
N-20R36
N-24R25
N-28R14
N-32 (新SP也指向这里)R03

RISC-V

RISC-V 架构的处理器在进入和退出中断处理模式时没有硬件自动保存和恢复上下文(通用寄存器)的操作,因此需要软件明确地使用(汇编语言编写的)指令进行上下文的保存和恢复。根据中断是向量处理模式还是非向量处理模式,上下文的保存和恢复涉及到的内容会有所差异

在上述异常exc_entry和中断irq_entry中,都有SAVE_CONTEXTRESTORE_CONTEXT来保存上下文和恢复上下文

.macro SAVE_CONTEXT
    csrrw sp, CSR_MSCRATCHCSWL, sp
    /* Allocate stack space for context saving */

    addi sp, sp, -20*REGBYTES

    STORE x1, 0*REGBYTES(sp)
    STORE x4, 1*REGBYTES(sp)
    STORE x5, 2*REGBYTES(sp)
    STORE x6, 3*REGBYTES(sp)
    STORE x7, 4*REGBYTES(sp)
    STORE x10, 5*REGBYTES(sp)
    STORE x11, 6*REGBYTES(sp)
    STORE x12, 7*REGBYTES(sp)
    STORE x13, 8*REGBYTES(sp)
    STORE x14, 9*REGBYTES(sp)
    STORE x15, 10*REGBYTES(sp)
    STORE x16, 14*REGBYTES(sp)
    STORE x17, 15*REGBYTES(sp)
    STORE x28, 16*REGBYTES(sp)
    STORE x29, 17*REGBYTES(sp)
    STORE x30, 18*REGBYTES(sp)
    STORE x31, 19*REGBYTES(sp)
.endm
    
.macro RESTORE_CONTEXT
	LOAD x1, 0*REGBYTES(sp)
	LOAD x4, 1*REGBYTES(sp)
	LOAD x5, 2*REGBYTES(sp)
	LOAD x6, 3*REGBYTES(sp)
	LOAD x7, 4*REGBYTES(sp)
	LOAD x10, 5*REGBYTES(sp)
	LOAD x11, 6*REGBYTES(sp)
	LOAD x12, 7*REGBYTES(sp)
	LOAD x13, 8*REGBYTES(sp)
	LOAD x14, 9*REGBYTES(sp)
	LOAD x15, 10*REGBYTES(sp)
    LOAD x16, 14*REGBYTES(sp)
    LOAD x17, 15*REGBYTES(sp)

    LOAD x28, 16*REGBYTES(sp)
    LOAD x29, 17*REGBYTES(sp)
    LOAD x30, 18*REGBYTES(sp)
    LOAD x31, 19*REGBYTES(sp)

    addi sp, sp, 20*REGBYTES

    csrrw sp, CSR_MSCRATCHCSWL, sp
.endm

还有SAVE_CSR_CONTEXTRESTORE_CSR_CONTEXT来保存和恢复这三个MCAUSE MEPC MSUBMCSR寄存器

例如下面的第一条命令表示把MCAUSE存到SP+11*4的位置,正好上面留了三个位置给CSR寄存器

/**
 * \brief  Macro for save necessary CSRs to stack
 * \details
 * This macro store MCAUSE, MEPC, MSUBM to stack.
 */
.macro SAVE_CSR_CONTEXT
    /* Store CSR mcause to stack using pushmcause */
    csrrwi  x5, CSR_PUSHMCAUSE, 11
    /* Store CSR mepc to stack using pushmepc */
    csrrwi  x5, CSR_PUSHMEPC, 12
    /* Store CSR msub to stack using pushmsub */
    csrrwi  x5, CSR_PUSHMSUBM, 13
.endm

.macro RESTORE_CSR_CONTEXT
    LOAD x5,  13*REGBYTES(sp)
    csrw CSR_MSUBM, x5
    LOAD x5,  12*REGBYTES(sp)
    csrw CSR_MEPC, x5
    LOAD x5,  11*REGBYTES(sp)
    csrw CSR_MCAUSE, x5
.endm

栈指针的切换

Cortex-M

Cortex-M有主栈MSP和线程栈PSP自动切换,默认是使用的MSP,那么疑问来了,怎么使用线程栈呢?

PendSV_Handler   PROC
switch_to_thread
    LDR     r1, =rt_interrupt_to_thread
    LDR     r1, [r1]
    LDR     r1, [r1]                ; load thread stack pointer

    LDMFD   r1!, {r4 - r11}         ; pop r4 - r11 register
    MSR     psp, r1                 ; update stack pointer

pendsv_exit
    ; restore interrupt
    MSR     PRIMASK, r2

    ORR     lr, lr, #0x04
    BX      lr
    ENDP

在线程切换的时候,会把线程的sp——rt_interrupt_to_thread赋给psp

在进入异常服务程序后,LR的值被自动更新为特殊的EXC_RETURN,所以只需要在异常中将LR的bit2置1就可以切换PSP了

EXC_RETURN会根据进入异常前的模式和SP使用情况生成(保持进入异常前的值),理论上只用第一次线程切换时手动把MSP改成PSP

EXC_RETURN位段含义
[31:4]EXC_RETURN的标识:必须全为1
30=返回后进入Handler模式
1=返回后进入线程模式
20=从主堆栈中做出栈操作,返回后使用MSP,
1=从进程堆栈中做出栈操作,返回后使用PSP
1保留,必须为0
00=返回ARM状态。
1=返回Thumb状态。在CM3中必须为1

LR在函数调用时会自动更新,对于函数的返回,将LR出栈给PC即可

在异常退出时,也会将LR赋给PC,但是很显然这不是代码空间的地址,系统会根据标识检测到这是一条EXC_RETURN命令,进一步根据进入中断时入栈的PC进行返回

在这里插入图片描述


RISC-V

在保存上下文和恢复上下文中都有如下语句:

csrrw sp, CSR_MSCRATCHCSWL, sp

mscratchcswl 寄存器用于在多个中断 level 间切换时,交换目的寄存器与 mscratch 的值来加速中断处理

使用带读操作的 CSR 指令访问 mscratchcsw,当特权模式不变,在出现中断程序和应用程序的切换时,有以下伪指令所示的寄存器操作:

mcause.mpil表示前一个中断级别

mintstatus.mil表示Machine Mode 的有效中断级别

csrrw rd, mscratchcswl, rs1
    
// Pseudocode operation.
// 栈指针的切换只在中断中操作,mintstatus.mil肯定不为0
// 如果mcause.mpil==0表示从线程中进入的中断(待验证),即判断成立,使用mscratch和SP交换来加载主栈
// RESTORE_CONTEXT中再次调用即再次交互,把主栈存入mscratch并把线程栈交换到SP中
if ( (mcause.mpil==0!= (mintstatus.mil == 0) )
{
    t = rs1; rd = mscratch; mscratch = t;
} 
else 
{	// 中断嵌套使用同一个栈,不需要改变
	rd = rs1; // mscratch unchanged.
}
// Usual use: csrrw sp, mscratchcswl, sp

看到这里,就会有一个疑问,第一次进中断时,mscratch中的内容从哪里来呢?

rt_hw_context_switch_to表示没有来源即第一次切换线程(在开始OS调度时调用,不是在中断切换)

所以第一次切换线程时会将主栈存入mscratch,之后就不需要再管了

之后便线程栈赋值给sp

rt_hw_context_switch_to:
    /* Setup Interrupt Stack using
       The stack that was used by main()
       before the scheduler is started is
       no longer required after the scheduler is started.
       Interrupt stack pointer is stored in CSR_MSCRATCH */
    la t0, _sp
    csrw CSR_MSCRATCH, t0
    LOAD sp, 0x0(a0)                /* Read sp from first TCB member(a0) */

在进入中断时,mepc 寄存器被同时更新,以反映当时遇到中断时的 PC 值。软件必须使用 mret指令退出中断,执行 mret 指令后处理器将从 mepc 定义的 pc 地址重新开始执行。通过这个机制,意味着 mret 指令执行后处理器回到了当时遇到中断时的 PC 地址,从而可以继续执行之前被中止的程序流。
在这里插入图片描述
在这里插入图片描述

  • 29
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
j-flash v6.40是一个用于编程和调试嵌入式软件的工具。它是由SEGGER公司开发的一款强大且易于使用的编程器和调试器。与其他编程器相比,j-flash v6.40具有许多优势。 首先,j-flash v6.40支持许多不同的处理器和微控制器,包括ARM、Cortex-MRISC-V等。它可以与各种不同的开发板和目标设备一起使用,使其适用于各种不同的嵌入式应用场景。 其次,j-flash v6.40具有快速的编程和调试速度。它可以在很短的时间内完成程序的下载和调试过程,从而提高开发效率。此外,它还支持多种编程模式和方法,用户可以根据自己的需求选择最合适的方式进行编程和调试。 此外,j-flash v6.40还提供了丰富的调试功能和工具。它支持实时追踪和监视程序的执行过程,可以帮助开发人员快速定位和解决问题。通过与其他SEGGER工具的配合使用,如SystemView和Ozone等,可以进一步提高调试效率和精确度。 最后,j-flash v6.40具有友好的用户界面和简单的操作流程。即使对于初学者来说,也能够轻松上手并进行编程和调试工作。它还提供了详细的帮助文档和技术支持,使用户能够充分利用其所有功能和特性。 总之,j-flash v6.40是一款功能强大且易于使用的编程器和调试器,适用于各种嵌入式应用场景。它的快速编程和调试速度、丰富的调试功能以及友好的用户界面使其成为嵌入式软件开发的理想选择。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值