保存上下文

处理异常的时候需要保存寄存器内容(上下文的一部分),需要将这些内容保存下来。但是硬件不负责这些内容的保存,因此需要用软件代码来保存这些寄存器的值。riscv采用sw指令将各个通用寄存器以此压栈。

除了通用寄存器之外,还需要保存其他上下文内容:

  • 触发异常时的PC和处理器状态,riscv中的mepc和mstatus寄存器。异常相应机制把它们保存在相应的系统寄存器中,我们还需要将他们从系统寄存器读出,然后保存在堆栈上。
  • 异常号,riscv的mcause寄存器。我们还需要将他们保存在堆栈上。
  • 地址空间,PA4时考虑

这些内容构成了完整的上下文信息, 异常处理过程可以根据上下文来诊断并进行处理, 同时, 将来恢复上下文的时候也需要这些信息。

接下来代码会调用C函数__am_irq_handle()(在abstract-machine/am/src/$ISA/nemu/cte.c中定义), 来进行异常的处理。

学到了一个东西:函数指针

重新组织Context结构体

  • 实现这一过程的新指令(我理解为保存上下文中的新指令

  • 理解上下文形成过程,重新组织abstract-machine/am/include/arch/$ISA-nemu.h 中定义的Context结构体的成员,使得这些成员的定义顺序和 abstract-machine/am/src/$ISA/nemu/trap.S中构造的上下文保持一致。

  • 并且在重新组织Context结构体时仍然需要正确地处理地址空间信息的位置, 否则你可能会在PA4中遇到难以理解的错误.

实现之后, 你可以在__am_irq_handle()中通过printf输出上下文c的内容, 然后通过简易调试器观察触发自陷时的寄存器状态, 从而检查你的Context实现是否正确.

这里观察abstract-machine/am/src/$ISA/nemu/trap.S中的行为,先是顺序的往内存栈中压入普通寄存器的内容,然后再向其中压入csr寄存器的内容,到这里应该能猜出Context结构的成员的顺序了。

但是为什么是这种顺序呢?

我从互联网上看到有其他人的回答是这样的,结构体是一段连续存储的地址空间,而trap.S中向内存中逐个写入的操作其实就是向一段连续的地址内写入数据的操作,如果这个地址开头正好是结构体的地址,并且写入顺序和结构体的定义顺序一致,那么就相当于向一个结构体写数据。

必答题

首先要知道__am_irq_handle()是被trap.S调用的,在调用之前会执行一条指令:

mv a0, sp

这其实就是汇编函数调用时的传参。

汇编函数调用以及传参[从汇编语言的寄存器来看函数参数传递 - 金色旭光 - 博客园 (cnblogs.com)](https://www.cnblogs.com/goldsunshine/p/14560301.html#:~:text=3|5函数调用传参总结 1 传值调用 直接拷贝一份 数值,到被调用函数,被调用函数中的数值和调用函数中的数值在内存中是两份相互独立的; 2 传地址调用 是将 数值的地址 拷贝一份到被调用函数中,数值在内存中只有一份,被调用函数通过该地址还能找到数值,可以修改这个数值。)

例如

#include<stdio.h>
int main()
{
int a = 10;
return 0;
}

在汇编之后

	.file	"space.c"
.option pic
.text
.align 1
.globl main
.type main, @function
main:
addi sp,sp,-32
sd s0,24(sp)
addi s0,sp,32
li a5,10
sw a5,-20(s0)
li a5,0
mv a0,a5
ld s0,24(sp)
addi sp,sp,32
jr ra
.size main, .-main
.ident "GCC: (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0"
.section .note.GNU-stack,"",@progbits

其中的头三条指令就是将main函数压栈,作为栈底,最后也是在main函数中退出。

传值/传地址

传值调用和传地址调用最大区别就在于调用函数处理实参的方式,传值调用,就是将数值当做实参写入寄存器,被调用函数从寄存器中取出数值;传地址调用是将数值的地址当作实参写入寄存器,被调用函数中从寄存器取出地址。

到这里就明白了,其实该指令就是函数调用传递参数的过程,只不过是用汇编语言编写的。

__am_irq_handle()函数的参数就是Context *c,那么就可以理解了,通过trap.Smv a0, sp传递进来的就是结构体指针c的地址。

事件分发

__am_irq_handle()函数会根据c中的mcause给当前的异常打包编号,编好号之后调用回调函数user_handler(ev, c),第一个参数就是异常编号,第二个参数就是上下文c。然后nanos-liteinit_irq()执行异常的具体行为。

恢复上下文

代码将会一路返回到trap.S__am_asm_trap()中, 接下来的事情就是恢复程序的上下文。

这里我漏了一个地方,riscv需要在软件层面实现PC+4。

也就是在处理具体异常的时候,根据不同异常的不同要求,分别实现是否PC+4。

这里也就出现了两个地方可以供我修改,一个是abstract-machine/am/src/riscv/nemu/cte.c,一个是/home/groot/ysyx-workbench/nanos-lite/src/irq.c。但是说了软件,就不能在abstract-machine中实现,而要在nanos-lite中实现这一功能。

实现的过程就是在do_event中处理异常的时候,根据异常的不同,决定是否将c->mepc+4。

必答题yield()的实现过程

->nanos-lite main()

---> abstact-machine yield()

--------->nemu ecall()

​ // 经过这一步之后pc值被改变, 改变为异常处理函数的入口.也就是在cte_init()中注册的地址: __am_asm_trap.

------------->nemu isa_raise_intr()

​ // 这里就是上下文保存+异常处理+恢复上下文的函数了

-----------------> abstract-machine __am_asm_trap

---------------------> abstract-machine __am_irq_handle()

------------------------->nanos-lite do_event()

---------------------> abstract-machine __am_irq_handle()

-----------------> abstract-machine __am_asm_trap

​ // 经过这一步之后pc值被还原为异常之前的pc值(或者由软件确认的是否+4的pc值)

------------->nemu mret()

---> abstact-machine yield()

->nanos-lite main()

在这里我遇见了另一个问题:如何实现DiffTest中异常行为和nemu的异常行为一致。

需要知道的一点是Spike的异常处理是一套完整的异常处理流程,而不是像nemu是一个简陋版。所以二者的行为肯定有不一样的地方,所以我应该在nemu中模仿spike的行为,将mstatus寄存器的值按照Spike中的行为处理,而不是完全按照手册处理。

一生一芯/NEMU PA3.1随笔的更多相关文章

  1. 64位的单周期 RISC-V 模拟器

    分享一个我最近完成过的小项目--64位的单周期 RISC-V 模拟器,这个项目我最近参与一生一芯计划过程中完成的一个小项目. 需要用到的相关知识:Verilog.Verilator.计算机组成原理.汇 ...

  2. 动手学TCP——CS144实验感想

    在Stanford CS144的课程实验Lab0~Lab4中,我们动手实现了一个自己的TCP协议,并且能够真的与互联网通信!此外,感谢Stanford开源本实验并提供了大量的优质测试用例,使得我们仅仅 ...

  3. Linux下学习FPGA

    声明(叠甲):鄙人水平有限,本文章仅供参考. 1.环境 推荐使用 Ubuntu20.04这是我使用多个版本中最好用的一个,相关安装教程可以自行上网搜索这不再赘述,但要补充的一点的是源推荐使用中科大的源 ...

  4. RDMA调研报告&一点随笔

    计算所科研实践随笔 被淹没在论文海里的两个星期. 早上7:10分起床,草草洗漱,7:30出发,开始漫长的1小时通勤.从地铁站的安检口起,队便排的极长,让人看得头皮发麻.下到了轨道旁稍好,但每趟呼啸而来 ...

  5. 龙芯fedora28日常生存指南

    2021-01-30 v0.0.5 从0.0.1开始改了非常多,一月余时间的花费渴望为其他人提供一点帮助,能够快速上手. 这主要是这一年来我从3B1500到3A4000再到福珑2的日常使用记录,是之前 ...

  6. <小李飞刀>系列 随笔

    1.多情剑客无情剑 古龙的作品在电视上只看过电影版的陆小凤传奇,对古龙的作品也没有过系统的了解,初读时听到了李寻欢的名字,突然感觉可惜.觉得如此早就读到这种级别的小说有些暴殄天物,不过也算是以白纸状态 ...

  7. AI人工智能系列随笔

    初探 AI人工智能系列随笔:syntaxnet 初探(1)

  8. 【置顶】CoreCLR系列随笔

    CoreCLR配置系列 在Windows上编译和调试CoreCLR GC探索系列 C++随笔:.NET CoreCLR之GC探索(1) C++随笔:.NET CoreCLR之GC探索(2) C++随笔 ...

  9. C++随笔:.NET CoreCLR之GC探索(4)

    今天继续来 带大家讲解CoreCLR之GC,首先我们继续看这个GCSample,这篇文章是上一篇文章的继续,如果有不清楚的,还请翻到我写的上一篇随笔.下面我们继续: // Initialize fre ...

  10. C++随笔:从Hello World 探秘CoreCLR的内部(1)

    紧接着上次的问题,上次的问题其实很简单,就是HelloWorld.exe运行失败,而本文的目的,就是成功调试HelloWorld这个控制台应用程序. 通过我的寻找,其实是一个名为TryRun的文件出了 ...

随机推荐

  1. 开源项目丨一文详解一站式大数据平台运维管家ChengYing如何部署Hadoop集群

    课件获取:关注公众号"数栈研习社",后台私信 "ChengYing" 获得直播课件 视频回放:点击这里 ChengYing开源项目地址:github 丨 git ...

  2. 开发者工具箱-鸿蒙RDB数据库封装与使用实践

    鸿蒙RDB数据库封装与使用实践 最近项目又要搞数据存储,鸿蒙的RDB用起来还挺啰嗦,干脆自己封装了个工具类,省得每次都写一堆重复代码.这里随手记下,万一以后自己忘了还能翻出来看看. 一.SQL基础知识 ...

  3. HyperWorks二维网格划分及拓扑改进

    Step 01:载入模型 Exercise_3a.hm. Step 02:2D 网格划分. (1) 进入 automesh 面板. 图 3-13 设置 automesh 面板网格控制参数 (2) 指定 ...

  4. 2021成都.NET开发者Connect线下活动

    2021年4月11日,在成都市天府新区南湖公园,2021成都.NET开发者Connect线下活动圆满结束,来自成都地区东南西北的25位.NETer汇聚一堂,面基交流,吃饭畅聊,并进行了合影留念. 首先 ...

  5. .net Core 中 Swagger 配置 xml 文档 文档

    //// 配置 xml 文档                //var BaseDirectory = AppContext.BaseDirectory;                //var x ...

  6. Google play安装不上Chrome

    背景 今天我的(安卓-小米)手机上突然要用到chrome,于是打开了Google Play商店下载. 发现一个问题,每次下载到百分百后,安装进度就会一闪而过,进而重新下载. 我尝试安装chrome b ...

  7. 斐讯n1进入u盘启动

    前言 我将n1刷完电视系统后,看了几天电视,发现还行吧. 过了几天,突然想玩游戏,发现插入u盘重启,依然进入电视,并不进入u盘的游戏机系统. 提供以下脚本,局域网下其他远程设备执行即可. window ...

  8. SciTech-EECS-Control System-PID(Proportional,Integral,Derivative): 控制系统 的 PID控制算法: 比例:瞬态Error(快速逼近)+积分:稳态Error(稳定终值)+微分:平滑(减少逼近时震荡) 的 控制算法

    参考文献 NI: PID控制器及其原理解释 https://www.ni.com/zh-cn/shop/labview/pid-theory-explained.html 本文介绍PID控制算法的工作 ...

  9. 算法练习(19)-单源最短路径dijkstra算法

    如上图,先初始化1个图,每条边上的红色数字为路径权重:(Node,Edge的定义参见算法练习(17)-图的广度优先遍历/深度优先遍历) Graph init() { List<Node> ...

  10. 电脑公网IP查询--九五小庞

    点击此链接,查看所在电脑 公网IP: http://www.whatismyip.com.tw/