以rt-smart在全志D1上的代码为例,主要注释了rt-smart在riscv64上的系统初始化和异常处理的代码仓库地址https://gitee.com/rtthread/rt-thread/tree/rt-smart
启动
代码路径
/*
*Copyright(c)2006-2018,RT-ThreadDevelopmentTeam
*
*SPDX-License-Identifier:Apache-2.0
*
*ChangeLogs:
*DateAuthorNotes
*2018/10/01BernardThefirstversion
*2018/12/27JesvenAddSMPsupport
*2020/6/12XimPorttoQEMUandremoveSMPsupport
*/
#define__ASSEMBLY__
#defineSSTATUS_FS0x00006000U/*initialstateofFPU,cleartodisable*/
#include
.global_start
.section".start","ax"
_start:
j1f
.word0xdeadbeef
.align3
.globalg_wake_up
g_wake_up:
.dword1
.dword0
1:
csrwsie,0/*超级用户模式中断使能关闭*/
csrwsip,0/*超级用户模式中断等待关闭*/
lat0,trap_entry/*将trap_entry的地址放入t0寄存器*/
csrwstvec,t0/*配置异常服务程序的入口地址*/
lix1,0
/*...........*//*初始化通用寄存器*/
lix31,0
/*settodisableFPU*/
lit0,SSTATUS_FS/*将FS的bit位写入t0寄存器*/
csrcsstatus,t0/*清除sstatus中的FSbit,关闭浮点单元*/
lit0,0x40000/*当SUM=1时,超级用户模式下,加载、存储和取指令请求可以访问标记为用户态的虚拟内存空间*/
csrssstatus,t0/*置位sstatus中的SUM位*/
.optionpush
.optionnorelax
lagp,__global_pointer$
.optionpop
//removedSMPsupporthere
lasp,__stack_start__/*栈指针的值来自于链接脚本中的__stack_start*/
lit0,__STACKSIZE__
addsp,sp,t0/*栈自上到下增长*/
csrwsscratch,sp/*sscratch存储栈顶的地址*/
jprimary_cpu_entry/*跳转到board中的C程序入口*/
//BSP的C入口
voidprimary_cpu_entry(void)
{
externvoidentry(void);
//初始化BSS
init_bss();
//关中断
rt_hw_interrupt_disable();
rt_assert_set_hook(__rt_assert_handler);
//启动RT-ThreadSmart内核
entry();
}
异常处理
异常处理流程图
异常处理上半部分
/*libcpu
isc-v -headc906interrupt_gcc.S*/
#define__ASSEMBLY__
#include"cpuport.h"
#include"encoding.h"
#include"stackframe.h"
.section.text.entry
.align2
.globaltrap_entry
.extern__stack_cpu0
.externget_current_thread_kernel_stack_top
trap_entry:/*异常处理函数的入口*/
//backupsp
csrrwsp,sscratch,sp/*将当前栈与sscratch做交换*/
//loadinterruptstack
lasp,__stack_cpu0/*sp指向cpu0的中断栈的栈顶*/
//backupcontext
SAVE_ALL/*CPU寄存器入栈,使能浮点的情况下浮点相关的寄存器也要入栈并且要保存sstatus中浮点的运算状态*/
RESTORE_SYS_GP/*gp操作不用了解*/
//checksyscall
csrrt0,scause/*读取scaue到t0*/
lit1,8//environmentcallfromu-mode/*用户模式环境调用异常*/
beqt0,t1,syscall_entry/*如果是系统调用则跳转到系统调用处理函数,这个函数最终会调用sret*/
csrra0,scause/*读取scause到a0,机器模式异常事件向量寄存器(MCAUSE)用于保存触发异常的异常事件向量号,用于在异常服务程序中处理对应事件*/
csrrca1,stval,zero/*读取stval到a1,发生异常或者中断,且在机器模式响应时,处理器会更新pc到MEPC,并根据异常类型更新MTVAL*/
csrra2,sepc/*读取sepc到a2,超级用户模式异常保留程序计数器(SEPC)用于存储程序从异常服务程序退出时的程序计数器值(即
PC值)*/
mva3,sp/*读取sp的值到a3*/
/*scause,stval,sepc,sp*/
callhandle_trap/*进行中断处理*/
中断处理
/*libcpu
isc-v -headc906 rap.c*/
/*Trapentry*/
voidhandle_trap(rt_size_tscause,rt_size_tstval,rt_size_tsepc,structrt_hw_stack_frame*sp)
{
/*
SCAUSE
bit63Interrupt-中断标记位
当 Interrupt 位为0时,表示触发异常的来源不是中断, Exception Code 按照异常解析。当 Interrupt 位为 1 时,表示触发异常的来源是中断, Exception Code 按照中断解析。该位会被 reset 置为 1’ b0。
bit0~4ExceptionCode-异常向量号位
在处理器响应异常或中断时,该域会被更新为对应异常号,具体请参考表3.9异常和中断向量分
配。该位会被 reset 置为 5’ b0。
*/
/*我理解这里是想获取ExceptionCode,但是ExceptionCode是bit0~bit4,这里用__MASK(5UL)更合适吧*/
rt_size_tid=__MASKVALUE(scause,__MASK(63UL));
constchar*msg;
/*supervisorexternalinterrupt*/
/*如果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是超级用户模式计时器中断*/
elseif((SCAUSE_INTERRUPT|SCAUSE_S_TIMER_INTR)==scause)
{
/*supervisortimer*/
rt_interrupt_enter();
tick_isr();
rt_interrupt_leave();
return;
}/*其他中断*/
elseif(SCAUSE_INTERRUPT&scause)
{
if(id< sizeof(Interrupt_Name)/sizeof(constchar*))
{
msg=Interrupt_Name[id];
}
else
{
msg="UnknownInterrupt";
}
LOG_E("UnhandledInterrupt%ld:%s
",id,msg);
}
else/*异常处理*/
{
#ifdefRT_USING_USERSPACE
/*pagefault缺页异常处理*/
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(constchar*))
{
msg=Exception_Name[id];
}
else
{
msg="UnknownException";
}
rt_kprintf("UnhandledException%ld:%s
",id,msg);
}
rt_kprintf("scause:0x%p,stval:0x%p,sepc:0x%p
",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,真正的线程切换动作在异常处理函数中完成。
异常处理下半部分
/*needtoswitchnewthread查询线程切换的flag是否被置位为1*/
las0,rt_thread_switch_interrupt_flag/*读取rt_thread_switch_interrupt_flag*/
lws2,0(s0)
beqzs2,spurious_interrupt/*rt_thread_switch_interrupt_flag如果为0那么直接跳转到spurious_interrupt进行寄存器恢复,并调用sret回到异常之前的状态*/
swzero,0(s0)/*rt_thread_switch_interrupt_flag=0*/
.globalrt_hw_context_switch_interrupt_do
rt_hw_context_switch_interrupt_do:
//swaptothreadkernelstack
csrrt0,sstatus/*读取sstatus到t0*/
andit0,t0,0x100/*bit8超级用户模式保留特权状态位*/
/*
该位用于保存处理器在降级到超级用户模式进入异常服务程序前的特权状态。
•当 SPP 为 2’ b00时,表示处理器进入异常服务程序前处于用户模式;
•当 SPP 为 2’ b01 时,表示处理器进入异常服务程序前处于超级用户模式;
该位会被 reset 置 2’ b01。
*/
beqzt0,__restore_sp_from_tcb_interrupt/*如果是内核态发生异常*/
__restore_sp_from_sscratch_interrupt:
csrrt0,sscratch/*获取发生异常时的上下文数据*/
j__move_stack_context_interrupt/*如果是用户态发生异常*/
/*获取当前线程的栈顶位置存到t0中*/
__restore_sp_from_tcb_interrupt:
las0,rt_interrupt_from_thread
LOADa0,0(s0)
jalrt_thread_sp_to_thread
jalget_thread_kernel_stack_top
mvt0,a0
__move_stack_context_interrupt:
mvt1,sp//src/*当前栈,当前栈存储的是发生异常时的通用寄存器信息*/
mvsp,t0//switchstack/*将发生异常时的栈的值写回到sp寄存器*/
addisp,sp,-CTX_REG_NR*REGBYTES/*栈指针向下移动CTX_REG_NR*REGBYTES*/
//copycontext
lis0,CTX_REG_NR//cnt/*需要恢复的寄存器的个数加载到s0*/
mvt2,sp//dst/*栈指针加载到t2*/
/*总结就是,当前CPU的中断栈存储了当前线程的通用寄存器的信息,如果发生任务切换,需要把这些信息拷贝到线程的栈里*/
copy_context_loop_interrupt:
LOADt0,0(t1)/*t1的值放到t0*/
STOREt0,0(t2)/*t0的值放到t2*/
addis0,s0,-1/*要恢复的寄存器个数-1*/
addit1,t1,8/*t1的地址加8*/
addit2,t2,8/*t2的地址加8*/
bnezs0,copy_context_loop_interrupt/*如果s0不为0就重复拷贝*/
las0,rt_interrupt_from_thread
LOADs1,0(s0)
STOREsp,0(s1)/*更新from线程的sp指针*/
las0,rt_interrupt_to_thread
LOADs1,0(s0)
LOADsp,0(s1)/*恢复to线程的sp*/
#ifdefRT_USING_USERSPACE
mva0,s1
jalrt_thread_sp_to_thread
jallwp_mmu_switch/*切换mmu,函数内部会判断from线程和to线程是不是在同一个lwp中,不是的话就会切换MMU*/
#endif
spurious_interrupt:
RESTORE_ALL/*恢复寄存器*/
sret/*超级用户模式异常返回指令*/
———————End———————
你可以添加微信:rtthread2020 为好友,注明:公司+姓名,拉进RT-Thread官方微信交流群!
爱我就给我点在看
点击阅读原文
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。
举报投诉
-
RT-Thread
+关注
关注
31文章
1260浏览量
39826
原文标题:RT-Smart riscv64汇编注释
文章出处:【微信号:RTThread,微信公众号:RTThread物联网操作系统】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
在ubuntu 24.04下尝试使用riscv64-linux-musleabi_for_x86_64-pc-linux-gnu工具链编译cv1800大核出现报错的原因?
在ubuntu 24.04下尝试使用riscv64-linux-musleabi_for_x86_64-pc-linux-gnu工具链编译cv1800大核,结果出现如下报错:
/home
发表于 07-16 08:20
使用msys2 mingw64编译nuclei openocd源码出错的原因?
:msys64homeAdministratorbuildnuclei-riscv-openocdbuild/../src/jtag/drivers/mpsse.c:358:(.text+0xc71): undefined reference
发表于 05-29 07:52
谷歌安卓系统即将取消对RISC-V架构的支持
负责安卓Linux核心分支开发的谷歌高级工程师向AOSP提交了一系列补丁,其中显示“已去除ACK对riscv64的支持”。这些补丁详细描述指出“对risc64 GKI内核的支持已停止”。
RISCV soft JTAG调试_v1.2
因为目前软件的限制,RISCV的逻辑不能同时共用JTAG,所以如果想要同时去调试逻辑和RISCV的话,可以通过RISCV的soft Jtag来实现。soft Jtag就是通过GPIO来实现的软件
RISCV soft JTAG调试_v1.1
因为目前软件的限制,RISCV的逻辑不能同时共用JTAG,所以如果想要同时去调试逻辑和RISCV的话,可以通过RISCV的soft Jtag来实现。soft Jtag就是通过GPIO来实现的软件
rtthread d1s编译报错的原因是什么?如何处理?
我使用使用的是现在git上的rtthread的最新的master分支,因为rt-smart已经并入主线,所以我想在windows下用env工具编译。
因为要编译riscv架构的全志D1S,我下载了
发表于 02-22 06:43
ART Pi Smart基于RT-Thread Smart系统的LVGL移植
ART-Pi Smart开发板为RT-Thread联合百问科技出品,使用的是 NXP 公司的 i.MX6ULL 处理器,具备单核 ARM Cortex-A7,最高运行频率可以达到 800MHz。
java怎么注释整个文档
java中可以使用特殊的注释格式来注释整个文档,这种格式被称为JavaDoc注释。JavaDoc注释可以用于生成HTML格式的文档,包含类、方法、字段、参数等的详细说明。下面是
vs中如何快速注释多行代码
在VS中,快速注释多行代码可以通过以下几种方法实现。我将详细介绍每种方法的步骤和应用场景。 方法一:块注释 块注释是一种常见的注释多行代码的方法。它适用于需要
devc怎么注释掉一段代码
在DevC中,要注释掉一段代码,你可以使用注释符号来标记这段代码。注释符号的作用是告诉编译器不要编译这些代码,而是将其视为注释,这样可以方便开发人员在代码中添加解释、说明或者临时禁用某
c语言怎么把代码全部注释掉
要将C语言代码全部注释掉,即不让代码被编译和执行,可以使用注释语句来实现。C语言提供两种注释方式:单行注释和多行注释。 单行
如何配置RT106x/RT107x从(非序列的)NAND启动的应用程序注释或示例?
是否有关于如何配置 RT106x/RT107x 从(非序列的)NAND 启动的应用程序注释或示例?
我找到了一些信息, 但是还没有在一个地方找到所有的信息。 如果它也包含一个例子, 那将会是件好事。
发表于 11-13 06:32
评论