八年级学生做的简易网站,汉中做网站公司,淘宝开店流程步骤,wordpress主题汉化中文环境
Cortex-M以STM32H750为代表#xff0c;RISC-V以芯来为代表
RTOS版本为RT-Thread 4.1.1 寄存器 RISC-V 常用汇编
RISC-V
关于STORE x4, 4(sp)这种寄存器前面带数字的写法#xff0c;其意思为将x4的值存入sp4这个地址#xff0c;即前面的数字表示偏移的意思
反之LOA…环境
Cortex-M以STM32H750为代表RISC-V以芯来为代表
RTOS版本为RT-Thread 4.1.1 寄存器 RISC-V 常用汇编
RISC-V
关于STORE x4, 4(sp)这种寄存器前面带数字的写法其意思为将x4的值存入sp4这个地址即前面的数字表示偏移的意思
反之LOAD表示从内存里面取值
la
地址加载 (Load Address). 伪指令(Pseudoinstruction), RV32I and RV64I.
la rd, symbol x[rd] symbol
例如将函数irq_entry()的地址放入t0中
la t0, irq_entrymv
移动(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 ResetAREA RESET, DATA, READONLYEXPORT __VectorsEXPORT __Vectors_EndEXPORT __Vectors_Size__Vectors DCD __initial_sp ; Top of StackDCD Reset_Handler ; Reset HandlerDCD NMI_Handler ; NMI HandlerDCD HardFault_Handler ; Hard Fault HandlerDCD MemManage_Handler ; MPU Fault HandlerDCD BusFault_Handler ; Bus Fault HandlerDCD UsageFault_Handler ; Usage Fault HandlerDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD 0 ; ReservedDCD SVC_Handler ; SVCall HandlerDCD DebugMon_Handler ; Debug Monitor HandlerDCD 0 ; ReservedDCD PendSV_Handler ; PendSV HandlerDCD SysTick_Handler ; SysTick Handler; External InterruptsDCD 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_entrycsrw CSR_MTVEC, t0ECLIC 的每个中断源均可以设置成向量或者非向量处理通过寄存器 clicintattr[i]的 shv 域其要点如下
当 mtvec.MODE ! 6’b000011 时处理器使用默认中断模式
mtvec.MODE 6’b000011 时处理器使用ECLIC 中断模式推荐使用此模式。 /* Set the interrupt processing mode to ECLIC mode */la t0, 0x3fcsrc CSR_MTVEC, t0csrs 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_entrycsrw CSR_MTVT2, t0csrs CSR_MTVT2, 0x1进入irq_entry后会自动关闭中断MIE
保存完上下文之后会调用对应中断服务程序在跳入中断服务程序的同时硬件也会同时打开中断的全局使能
中断服务程序执行完成之后需要将中断全局使能再次关闭保证恢复上下文的原子性
MPIE会记录异常发生前得MIE值以便异常结束时恢复到原来的值
csrrw ra, CSR_JALMNXTI, ra在跳入中断服务程序的同时“csrrw ra CSR_JALMNXTI ra”指令还会达到 JALJump and Link的效果硬件同时更新 Link 寄存器的值为该指令的 PC 自身作为函数调用的返回地址。因此从中断服务程序函数返回后会回到该“csrrw raCSR_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-4xPSR2N-8PC1N-12LR8N-16R127N-20R36N-24R25N-28R14N-32 (新SP也指向这里)R03 RISC-V
RISC-V 架构的处理器在进入和退出中断处理模式时没有硬件自动保存和恢复上下文通用寄存器的操作因此需要软件明确地使用汇编语言编写的指令进行上下文的保存和恢复。根据中断是向量处理模式还是非向量处理模式上下文的保存和恢复涉及到的内容会有所差异
在上述异常exc_entry和中断irq_entry中都有SAVE_CONTEXT和RESTORE_CONTEXT来保存上下文和恢复上下文
.macro SAVE_CONTEXTcsrrw sp, CSR_MSCRATCHCSWL, sp/* Allocate stack space for context saving */addi sp, sp, -20*REGBYTESSTORE 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_CONTEXTLOAD 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*REGBYTEScsrrw sp, CSR_MSCRATCHCSWL, sp
.endm还有SAVE_CSR_CONTEXT和RESTORE_CSR_CONTEXT来保存和恢复这三个MCAUSE MEPC MSUBMCSR寄存器
例如下面的第一条命令表示把MCAUSE存到SP11*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_CONTEXTLOAD x5, 13*REGBYTES(sp)csrw CSR_MSUBM, x5LOAD x5, 12*REGBYTES(sp)csrw CSR_MEPC, x5LOAD x5, 11*REGBYTES(sp)csrw CSR_MCAUSE, x5
.endm栈指针的切换
Cortex-M
Cortex-M有主栈MSP和线程栈PSP自动切换默认是使用的MSP那么疑问来了怎么使用线程栈呢
PendSV_Handler PROC
switch_to_threadLDR r1, rt_interrupt_to_threadLDR r1, [r1]LDR r1, [r1] ; load thread stack pointerLDMFD r1!, {r4 - r11} ; pop r4 - r11 registerMSR psp, r1 ; update stack pointerpendsv_exit; restore interruptMSR PRIMASK, r2ORR lr, lr, #0x04BX lrENDP在线程切换的时候会把线程的sp——rt_interrupt_to_thread赋给psp
在进入异常服务程序后LR的值被自动更新为特殊的EXC_RETURN所以只需要在异常中将LR的bit2置1就可以切换PSP了
EXC_RETURN会根据进入异常前的模式和SP使用情况生成保持进入异常前的值理论上只用第一次线程切换时手动把MSP改成PSP
EXC_RETURN位段含义[31:4]EXC_RETURN的标识必须全为130返回后进入Handler模式1返回后进入线程模式20从主堆栈中做出栈操作返回后使用MSP1从进程堆栈中做出栈操作返回后使用PSP1保留必须为000返回ARM状态。1返回Thumb状态。在CM3中必须为1
LR在函数调用时会自动更新对于函数的返回将LR出栈给PC即可
在异常退出时也会将LR赋给PC但是很显然这不是代码空间的地址系统会根据标识检测到这是一条EXC_RETURN命令进一步根据进入中断时入栈的PC进行返回 RISC-V
在保存上下文和恢复上下文中都有如下语句:
csrrw sp, CSR_MSCRATCHCSWL, spmscratchcswl 寄存器用于在多个中断 level 间切换时交换目的寄存器与 mscratch 的值来加速中断处理
使用带读操作的 CSR 指令访问 mscratchcsw当特权模式不变在出现中断程序和应用程序的切换时有以下伪指令所示的寄存器操作
mcause.mpil表示前一个中断级别
mintstatus.mil表示Machine Mode 的有效中断级别
csrrw rd mscratchcswl rs1// Pseudocode operation.
// 栈指针的切换只在中断中操作mintstatus.mil肯定不为0
// 如果mcause.mpil0表示从线程中进入的中断待验证即判断成立使用mscratch和SP交换来加载主栈
// RESTORE_CONTEXT中再次调用即再次交互把主栈存入mscratch并把线程栈交换到SP中
if mcause.mpil0 ! 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 usingThe stack that was used by main()before the scheduler is started isno longer required after the scheduler is started.Interrupt stack pointer is stored in CSR_MSCRATCH */la t0, _spcsrw CSR_MSCRATCH, t0LOAD sp, 0x0(a0) /* Read sp from first TCB member(a0) */在进入中断时mepc 寄存器被同时更新以反映当时遇到中断时的 PC 值。软件必须使用 mret指令退出中断执行 mret 指令后处理器将从 mepc 定义的 pc 地址重新开始执行。通过这个机制意味着 mret 指令执行后处理器回到了当时遇到中断时的 PC 地址从而可以继续执行之前被中止的程序流。