0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

RT-Smart riscv64汇编注释

冬至子 来源:HAHABO 作者:HAHABO 2023-10-12 17:26 次阅读

以rt-smart在全志D1上的代码为例,主要注释了rt-smart在riscv64上的系统初始化和异常处理的代码

启动

代码路径

libcpurisc-vt-headc906startup_gcc.S

/*

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程序入口 /
//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();
}

异常处理

异常处理流程图

1.jpg

异常处理上半部分

/ libcpurisc-vt-headc906interrupt_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 / 进行中断处理 /
中断处理
/ libcpurisc-vt-headc906trap.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。
bit04 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:%sn",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:%sn",id,msg);
}
rt_kprintf("scause:0x%p,stval:0x%p,sepc:0x%pn",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,真正的线程切换动作在异常处理函数中完成。

异常处理下半部分

/* 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 / 超级用户模式异常返回指令 /

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 寄存器
    +关注

    关注

    31

    文章

    5342

    浏览量

    120328
  • 中断处理
    +关注

    关注

    0

    文章

    94

    浏览量

    10974
  • RT-Thread
    +关注

    关注

    31

    文章

    1288

    浏览量

    40116
  • RISC-V
    +关注

    关注

    45

    文章

    2277

    浏览量

    46156
收藏 人收藏

    评论

    相关推荐

    RT-Smart的资料合集

    1、RT-Smart的启动过程在熟悉 RT-Smart 架构的过程中,研究其启动过程的是必不可少的,那么在系统正常运行之前,需要做哪些准备工作呢。本文将以 32 位 RT-Smart 的源代码为
    发表于 03-22 15:06

    请问rt-smart支持64位的编译嘛?

    我看入门指南上写目前rt-smart仅支持32位的系统,所以有办法编译成64位吗?
    发表于 04-15 09:47

    D1哪吒开发板rt-smart内核固件的烧写与运行步骤

    前BSP需要简单的修改才能保证编译通过,这里注意讲解 rt-smart 内核固件的烧写与运行搭建d1-allwinner-nezha 的 rt-smart 最小系统后,在ubuntu RISCV64
    发表于 06-17 11:06

    如何在RT-Thread Smart下使用gcc交叉编译工具链呢

    前言RT-Thread Smart的BSP rt-smartspd1-allwinner-nezha,也全志D1s的哪吒开发板,基于RISCV64平台,需要CV
    发表于 06-17 11:13

    在虚拟机Ubuntu18.04中配置RT-Smart RISC-V64报unknown arch错误?

    尝试在linux中进行RT-Smart开发,在tools目录下运行完配置脚本后,在userapps目录下运行smart-env.sh时发现参数为riscv64一直报unknown arch但是参数为arm时则正常,向大佬们求助。
    发表于 02-10 14:05

    RT-Smartriscv64上的系统初始化和异常处理的代码注释

    RT-Smart riscv64汇编注释rt-smart在全志D1上的代码为例,主要注释
    发表于 02-10 16:43

    rt-smartriscv64上的系统初始化和异常处理的代码注释

    rt-smart在全志D1上的代码为例,主要注释rt-smartriscv64上的系统初始化和异常处理的代码启动代码路径libcpu\\risc-v\\t-head\\c906
    发表于 02-15 11:04

    怎么解决rt-smart在适配riscv64虚拟地址和物理地址映射问题?

    )--------------Backtrace--------------riscv64-unknown-linux-musl-addr2line -e rtthread.elf -a -f 00000000500248b0目前关于RT-SMART的资料较少,官网文档中
    发表于 03-16 11:29

    riscv64裸机编程实践与分析

    在启动之前都需要有一段汇编代码,从这段汇编代码上就可以体现一些架构设计的特点。往往做嵌入式底层开发都需要关注这段汇编代码的含义,这样在使用的时候才能全面的了解启动时做了什么事情,在后续的程序中遇到问题也能复盘推演。 本文就针对
    的头像 发表于 12-31 10:54 4490次阅读
    <b class='flag-5'>riscv64</b>裸机编程实践与分析

    树莓派上rt-smart的应用编程入门

    我们从现在开始会逐步连载RT-Thread Smart(简称rt-smart,甚至有时会称为smart os)的介绍文章,旨在让大家认识,接触到sm
    的头像 发表于 05-13 14:10 3167次阅读
    树莓派上<b class='flag-5'>rt-smart</b>的应用编程入门

    第一届RISC-V中国峰会最新内容:RV64上的微内核操作系统rt-smart介绍

    RT-Thread自开始就在推进对RISCV架构的支持,同时也包括社区、态合作伙伴在RISCV方面的贡献。本文主要对RV64上的微内核操作系统rt-
    的头像 发表于 06-23 11:49 2457次阅读
    第一届RISC-V中国峰会最新内容:RV<b class='flag-5'>64</b>上的微内核操作系统<b class='flag-5'>rt-smart</b>介绍

    rt-smart移植分析:从树莓派3b入手

    移植rt-smart到最新的板子上具体需要注意哪些细节,哪些才是移植rt-smart的关键点?本文从树莓派3b上移植rt-smart的角度,从头分析rt-sm...
    发表于 01-25 18:48 0次下载
    <b class='flag-5'>rt-smart</b>移植分析:从树莓派3b入手

    丝滑的在RT-Smart用户态运行LVGL

    开发流程 1、RT-Smart 环境搭建 下载 RT-Smart 用户态应用代码: 1 git clone https: //github.com/RT-Thread/userapps.git
    的头像 发表于 11-22 20:20 1268次阅读

    RT-Smart riscv64汇编注释

    rt-smart在全志D1上的代码为例,主要注释rt-smartriscv64上的系统初始化和异常处理的代码仓库地址https://gitee.com/rtthread/
    的头像 发表于 02-08 21:40 1160次阅读

    riscvrt-smart中的板级初始化

    本文章的代码来自于rt-smart中针对qemu-virt-riscv的bsp 仓库地址 https://gitee.com/rtthread/rt-thread/tree/rt-smart
    的头像 发表于 02-09 17:45 967次阅读