Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
rt-smart
risc-v_RISCV
全志_RISCV_D1_D1S
RT-Smart riscv64汇编注释
发布于 2023-02-07 00:27:04 浏览:727
订阅该版
[tocm] # RT-Smart riscv64汇编注释 --- 以rt-smart在全志D1上的代码为例,主要注释了rt-smart在riscv64上的系统初始化和异常处理的代码 仓库地址https://gitee.com/rtthread/rt-thread/tree/rt-smart ## 启动 代码路径 > libcpu\risc-v\t-head\c906\startup_gcc.S ```ASM /* * Copyright (c) 2006-2018, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2018/10/01 Bernard The first version * 2018/12/27 Jesven Add SMP support * 2020/6/12 Xim Port to QEMU and remove SMP support */ #define __ASSEMBLY__ #define SSTATUS_FS 0x00006000U /* initial state of FPU, clear to disable */ #include
.global _start .section ".start", "ax" _start: j 1f .word 0xdeadbeef .align 3 .global g_wake_up g_wake_up: .dword 1 .dword 0 1: csrw sie, 0 /*超级用户模式中断使能关闭*/ csrw sip, 0 /*超级用户模式中断等待关闭*/ la t0, trap_entry /*将trap_entry的地址放入t0寄存器*/ csrw stvec, t0 /*配置异常服务程序的入口地址*/ li x1, 0 /*...........*/ /*初始化通用寄存器*/ li x31,0 /* set to disable FPU */ li t0, SSTATUS_FS /*将FS的bit位写入t0寄存器*/ csrc sstatus, t0 /*清除sstatus中的FS bit,关闭浮点单元*/ li t0, 0x40000 /*当 SUM=1 时,超级用户模式下,加载、存储和取指令请求可以访问标记为用户态的虚拟内存空间*/ csrs sstatus, t0 /*置位sstatus中的SUM位*/ .option push .option norelax la gp, __global_pointer$ .option pop // removed SMP support here la sp, __stack_start__ /*栈指针的值来自于链接脚本中的__stack_start*/ li t0, __STACKSIZE__ add sp, sp, t0 /*栈自上到下增长*/ csrw sscratch, sp /*sscratch存储栈顶的地址 */ j primary_cpu_entry /*跳转到board中的C程序入口*/ ``` ```C //BSP的C入口 void primary_cpu_entry(void) { extern void entry(void); //初始化BSS init_bss(); //关中断 rt_hw_interrupt_disable(); rt_assert_set_hook(__rt_assert_handler); //启动RT-Thread Smart内核 entry(); } ``` ## 异常处理 ### 异常处理流程图  ### 异常处理上半部分 ```C /*libcpu\risc-v\t-head\c906\interrupt_gcc.S*/ #define __ASSEMBLY__ #include "cpuport.h" #include "encoding.h" #include "stackframe.h" .section .text.entry .align 2 .global trap_entry .extern __stack_cpu0 .extern get_current_thread_kernel_stack_top trap_entry: /*异常处理函数的入口*/ //backup sp csrrw sp, sscratch, sp /*将当前栈与sscratch做交换*/ //load interrupt stack la sp, __stack_cpu0 /*sp指向cpu0的中断栈的栈顶*/ //backup context SAVE_ALL /*CPU寄存器入栈,使能浮点的情况下浮点相关的寄存器也要入栈 并且要保存sstatus中浮点的运算状态*/ RESTORE_SYS_GP /*gp操作不用了解*/ //check syscall csrr t0, scause /*读取scaue到t0*/ li t1, 8 //environment call from u-mode /*用户模式环境调用异常*/ beq t0, t1, syscall_entry /*如果是系统调用则跳转到系统调用处理函数,这个函数最终会调用sret*/ csrr a0, scause /*读取scause到a0,机器模式异常事件向量寄存器(MCAUSE)用于保存触发异常的异常事件向量号,用于在异常服务程序中处理对应事件*/ csrrc a1, stval, zero /*读取stval到a1,发生异常或者中断,且在机器模式响应时,处理器会更新 pc 到 MEPC,并根据异常类型更新 MTVAL*/ csrr a2, sepc /*读取sepc到a2, 超级用户模式异常保留程序计数器(SEPC)用于存储程序从异常服务程序退出时的程序计数器值(即 PC 值)*/ mv a3, sp /*读取sp的值到a3*/ /* scause, stval, sepc, sp */ call handle_trap /*进行中断处理*/ ``` ### 中断处理 ```C /*libcpu\risc-v\t-head\c906\trap.c*/ /* Trap entry */ void handle_trap(rt_size_t scause,rt_size_t stval,rt_size_t sepc,struct rt_hw_stack_frame *sp) { /* SCAUSE bit63 Interrupt-中断标记位 当 Interrupt 位为 0 时,表示触发异常的来源不是中断, Exception Code 按照异常解析。当 Interrupt 位为 1 时,表示触发异常的来源是中断, Exception Code 按照中断解析。该位会被 reset 置为 1’ b0。 bit0~4 Exception Code-异常向量号位 在处理器响应异常或中断时,该域会被更新为对应异常号,具体请参考 表 3.9 异常和中断向量分 配。该位会被 reset 置为 5’ b0。 */ /*我理解这里是想获取Exception Code,但是Exception Code是bit0 ~ bit4,这里用__MASK(5UL)更合适吧*/ rt_size_t id = __MASKVALUE(scause,__MASK(63UL)); const char *msg; /* supervisor external interrupt */ /*如果scause的bit63是1,scause的bit0~4是9超级用户模式外部中断*/ if ((SCAUSE_INTERRUPT & scause) && SCAUSE_S_EXTERNAL_INTR == (scause & 0xff)) { rt_interrupt_enter(); plic_handle_irq(); rt_interrupt_leave(); return; } /*如果scause的bit63是1,scause的bit0~4是超级用户模式计时器中断*/ else if ((SCAUSE_INTERRUPT | SCAUSE_S_TIMER_INTR) == scause) { /* supervisor timer */ rt_interrupt_enter(); tick_isr(); rt_interrupt_leave(); return; } /*其他中断*/ else if (SCAUSE_INTERRUPT & scause) { if(id < sizeof(Interrupt_Name) / sizeof(const char *)) { msg = Interrupt_Name[id]; } else { msg = "Unknown Interrupt"; } LOG_E("Unhandled Interrupt %ld:%s\n",id,msg); } else /*异常处理*/ { #ifdef RT_USING_USERSPACE /* page fault 缺页异常处理*/ if (id == EP_LOAD_PAGE_FAULT || id == EP_STORE_PAGE_FAULT) { arch_expand_user_stack((void *)stval); return; } #endif /*其他异常处理,走到这里后打印一些必要信息,最终会走到while(1),进入死循环*/ if(id < sizeof(Exception_Name) / sizeof(const char *)) { msg = Exception_Name[id]; } else { msg = "Unknown Exception"; } rt_kprintf("Unhandled Exception %ld:%s\n",id,msg); } rt_kprintf("scause:0x%p,stval:0x%p,sepc:0x%p\n",scause,stval,sepc); dump_regs(sp); while(1); } ``` 在rt-smart中任务切换有三个相关的线程函数 - rt_hw_context_switch_to():没有来源线程,切换到目标线程,在调度器启动第一个线程的时候 被调用 - rt_hw_context_switch():在线程环境下,从当前线程切换到目标线程 - rt_hw_context_switch_interrupt ():在中断环境下,从当前线程切换到目标线程。 rt_hw_context_switch_interrupt ()会将rt_thread_switch_interrupt_flag置为1,真正的线程切换动作在异常处理函数中完成。 ### 异常处理下半部分 ```C /* need to switch new thread 查询线程切换的flag是否被置位为1*/ la s0, rt_thread_switch_interrupt_flag /*读取rt_thread_switch_interrupt_flag*/ lw s2, 0(s0) beqz s2, spurious_interrupt /*rt_thread_switch_interrupt_flag如果为0那么直接跳转到spurious_interrupt进行寄存器恢复,并调用sret回到异常之前的状态*/ sw zero, 0(s0) /*rt_thread_switch_interrupt_flag = 0*/ .global rt_hw_context_switch_interrupt_do rt_hw_context_switch_interrupt_do: //swap to thread kernel stack csrr t0, sstatus /*读取sstatus到t0*/ andi t0, t0, 0x100 /*bit8 超级用户模式保留特权状态位*/ /* 该位用于保存处理器在降级到超级用户模式进入异常服务程序前的特权状态。 • 当 SPP 为 2’ b00 时,表示处理器进入异常服务程序前处于用户模式; • 当 SPP 为 2’ b01 时,表示处理器进入异常服务程序前处于超级用户模式; 该位会被 reset 置 2’ b01。 */ beqz t0, __restore_sp_from_tcb_interrupt /*如果是内核态发生异常*/ __restore_sp_from_sscratch_interrupt: csrr t0, sscratch /*获取发生异常时的上下文数据*/ j __move_stack_context_interrupt /*如果是用户态发生异常*/ /*获取当前线程的栈顶位置存到t0中*/ __restore_sp_from_tcb_interrupt: la s0, rt_interrupt_from_thread LOAD a0, 0(s0) jal rt_thread_sp_to_thread jal get_thread_kernel_stack_top mv t0, a0 __move_stack_context_interrupt: mv t1, sp//src /*当前栈,当前栈存储的是发生异常时的通用寄存器信息*/ mv sp, t0//switch stack /* 将发生异常时的栈的值写回到sp寄存器 */ addi sp, sp, -CTX_REG_NR * REGBYTES /*栈指针向下移动CTX_REG_NR * REGBYTES*/ //copy context li s0, CTX_REG_NR//cnt /*需要恢复的寄存器的个数加载到s0*/ mv t2, sp//dst /*栈指针加载到t2*/ /*总结就是,当前CPU的中断栈存储了当前线程的通用寄存器的信息,如果发生任务切换,需要把这些信息拷贝到线程的栈里*/ copy_context_loop_interrupt: LOAD t0, 0(t1) /*t1的值放到t0*/ STORE t0, 0(t2) /*t0的值放到t2*/ addi s0, s0, -1 /*要恢复的寄存器个数-1*/ addi t1, t1, 8 /*t1的地址加8*/ addi t2, t2, 8 /*t2的地址加8*/ bnez s0, copy_context_loop_interrupt /*如果s0不为0就重复拷贝*/ la s0, rt_interrupt_from_thread LOAD s1, 0(s0) STORE sp, 0(s1) /*更新from线程的sp指针*/ la s0, rt_interrupt_to_thread LOAD s1, 0(s0) LOAD sp, 0(s1) /*恢复to线程的sp*/ #ifdef RT_USING_USERSPACE mv a0, s1 jal rt_thread_sp_to_thread jal lwp_mmu_switch /*切换mmu,函数内部会判断from线程和to线程是不是在同一个lwp中,不是的话就会切换MMU*/ #endif spurious_interrupt: RESTORE_ALL /*恢复寄存器*/ sret /*超级用户模式异常返回指令*/ ``` [1]: https://gitee.com/rtthread/rt-thread/tree/rt-smart
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
HAHABO
这家伙很懒,什么也没写!
文章
4
回答
1
被采纳
0
关注TA
发私信
相关文章
1
studio能否支持risc-v的工程,包括调试。
2
移植rt-nano至risc-v时,无法在main函数创建用户线程
3
risc-v移植rtthread,程序莫名跳转到异常Exception
4
GD32VF103出现to free a bad data block:错误
5
rtt os riscv Store address misaligned异常
6
仅实现机器模式的芯片是否可以移植RT-THREAD系统
7
nano版本移植finsh(基于risc-v)
8
C++在多核下cout打印引起崩溃
9
RT-SMART求助
10
RT-Thread在RISC-V架构的芯片上有成熟的市场项目吗?
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
DMA
USB
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
rt-smart
FAL
I2C_IIC
UART
ESP8266
cubemx
WIZnet_W5500
ota在线升级
PWM
BSP
flash
freemodbus
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
keil_MDK
ulog
SFUD
msh
C++_cpp
MicroPython
本月问答贡献
RTT_逍遥
10
个答案
2
次被采纳
xiaorui
3
个答案
2
次被采纳
winfeng
2
个答案
2
次被采纳
三世执戟
8
个答案
1
次被采纳
KunYi
8
个答案
1
次被采纳
本月文章贡献
lizimu
2
篇文章
7
次点赞
catcatbing
2
篇文章
5
次点赞
swet123
1
篇文章
4
次点赞
Days
1
篇文章
4
次点赞
YZRD
1
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部