第一次参加龙芯杯,报名的是团队赛,感受到了前排大佬的实力碾压…
一切的开始
这学期有计算机组成原理的课程设计实验,要求实现一个简单的(中断都没有)RISC-V
CPU,考虑到课程设计实验的难度不大,我和几个同学很早便完成了实验。
老师看我们的进度快便推荐我们参加龙芯杯比赛。我们早在四月便完成了报名。
开始动手
当时我们正在上操作系统课程实验,老师突然发了一条消息
虽然说四月报名参加比赛,但我们三个人一直到七月上旬才开始动手😑
我们花了一周的时间讨论并设计我们CPU的微结构,确定为做一个带Cache
的顺序双发射、五级流水线的朴素的CPU。接下来我们用两周的时间完成了初赛作品的设计(ddl永远是第一驱动力)
1 | Git commits by month: |
在初赛提交的晚上,我们临时发现最新的版本有Bug,会导致无法通过系统测试部分功能,并且性能测试有一定的几率出现极低的分数。我们三个人立即开始回滚版本,但是由于没有保存以前综合的bitstream
,只能立即综合,与时间赛跑。这时候,学校的后勤不知道在想什么,没有任何预告的把宿舍的电闸拉了….
最后有惊无险的赶在截止前几分钟把作品提交了上去
准备复赛
初赛提交完成后,我们开始看复赛的要求,我们的基本目标是跑起来一个PMON
,这样复赛演示起评分能有10
分。
首先不得不吐槽一下PMON
,这个上古世纪的东西,只能用GCC4
编译,我一开始编译了一个mipsel
版本的GCC11
尝试用于编译PMON
,结果报出各种花里胡哨的错误,有一些能够手动修改,但是PMON
里面有很多宏定义的东西,涉及类型转换、判断等,这些由于我实力不足,实在没办法修改,只能安装一个GCC4
解决编译工具链的问题后,要考虑阉割功能,首先是Cache
指令,这个实现起来相对复杂,我们全局搜索并删除了它们。
接着是TLB
相关指令,我们考虑到如果复赛不加点功能好像说不过去,于是参照A13
手册实现了简易的TLB
功能。TLB
是一个大坑,它堵在关键路径上:一条TLB
表项就是90
位,并且考虑到PageMask
这个东西,在确定那些位要保留上是一个十分深的逻辑。我们一开始做了32
个表项,没想到连50MHz
都上不去了….最后通过拆分TLB
功能所在的流水级(我们没有加流水级)并且降低表项到4
,勉强保证了75MHz
的频率(初赛时有88MHz
)
接下来是十分无语的问题,我们使用自己编译定制的PMON
,在上电后解压内核时发生了CRC
校验错误😓,此时距离复赛提交的时间不多了,我们怀疑是gzip
版本过于先进,导致PMON
自带的解压代码无法识别(未验证)。我们只好使用IDA
,对着龙芯提供的PMON
二进制进行逆向修改。
我们在跑通PMON
后想着能否更近一步,跑一个uCore
,接着发现uCore
需要使用到32
个TLB
表项,并且需要TLBWR
指令支持(我们只实现了三个基本的TLB
指令)。我把uCore
的源代码进行了魔改,用TLBR
和TLBWI
模拟了TLBWR
指令。但是上电后发现uCore
在卡在等待中断上了,一时间找不到问题的源头,只好放弃了uCore
。此时距离复赛提交只有两天了。
我们马上讨论,准备演示程序自己复刻一个Ping Pong
(Pong - Wikipedia)。我们使用到了两块FPGA
,一块是龙芯实验箱,另一块是计组实验用的Nexys4DDR
,N4DDR
上运行了我计组实现写的带串口的简易RISC-V
CPU,当作手柄和实验箱通信。实验箱上运行着团队大佬手写的汇编Pong
,他用汇编实现了一个线程调度,中断注册等功能的简易操作系统内核,然后在内核上实现了Pong
的主程序。
现场加指令
这一部分听学长讲一般都是很简单的指令,比如说max
或者min
之类的,没想到我们这一次要求加一个filter
指令:记录所有filter
的数据,去除两个最大值、两个最小值,返回剩下的和,难度比往年有所增加。
这个也不是很难,只要维护最大值、最小值以及和总和即可。
答辩
答辩还算顺利。答辩前,我们猜想了几个问题:“为什么没有跑uCore
”,没想到还真被问到了 …
评委只问了三个问题:“为什么没有跑uCore
”、“游戏怎么写的”、“怎么逆向的代码”,感觉和CPU
完全不擦边,估计是我们的作品没什么亮点
对我们而言,就是奔着三等奖去的(过了初赛的保底三等),毕竟我们投入的时间太少,前前后后只有一个月。
总结
这个比赛越来越难打了
- 要想进复赛一定得有一个
Cache
- 得保证充足的时间,理想的情况是在初赛时就要把复赛准备做的东西完成
- 要想拿名次,操作系统至少跑一个
uCore
,最好是Linux
,这个基本上来说是二三等奖的分水岭
=== Update(2021.8.25) ===
今天正好有空,继续完善了一下之前的项目,跑通了uCore
:
- 修复时钟中断产生时没有将
IP[7]
置1
的问题 - 添加
TLBWR
指令:如果不做的话可以手动模拟TLBWR
过程 - 增加特权级(
uCore
代码里面使用了KSU
,但可以偷懒不做Supervisor
,只做UM
即可):这个一定要实现,在处理TLB REFILL
时会根据特权级进行不同的操作。最简单的“取巧做法”就是单让UM
位可写,其它不管
=== Update(2021.9.10) ===
顺手移植了一下RT-Thread
操作系统(My Fork)
这里主要修改了以下几个地方:
- 基于
ls1b
和ls1c
的代码进行精简,创建新的NSCSCC
板级支持包 - 删除
EHB
指令(一个侵入式的修改,将MIPS
通用启动过程中的EHB
指令替换成NOP
) - 自定义编译工具链(
GCC11
+Newlib
,编译时不产生Branch Likely
指令)
目前遗憾的是没能驱动网卡 :cry: