NSCSCC2022 龙芯杯参赛总结

我参加2022年龙芯杯的参赛总结

预计阅读时间: 21 分钟

一转眼已经是八月下旬,暑假只剩一个星期了,当然去年也是这样。上一次写内容还是在年初,基本上半年以来的课外时间基本都投入在龙芯杯和相关的事情上了。龙芯杯就如字面上的意思是由龙芯中科公司和高等学校计算机教育研究委员会共同主办的一个赛事,赛事的主要内容是使用RTL设计一个完整的CPU,并在FPGA上进行验证和展示。2022年是龙芯杯的第六年,今年的比赛也和以往有所不同,新增了LoongArch挑战赛,我就是来吃螃蟹的人(踩大坑

新增赛道

如上面所讲,2022年龙芯杯最大的不同就是除了往年的MIPS指令集的个人赛和团体赛,新增了LoongArch挑战赛赛道。这个新的赛道的赛制,日程和评价方式都和MIPS的赛道有相当大的区别,区别体现在:

  • 指令集区别:这个当然是最主要的区别,LoongArch挑战赛使用的是LA32 Reduced指令集,而以往的个人赛和团体赛都使用MIPS指令集。
  • 日程区别:团体赛和个人赛分为初赛和决赛,2022年初赛在8月5日截止,决赛提交在8月19日截止,线上答题在8月19日,决赛答辩在8月20日。而LoongArch赛道没有区分初赛和决赛(因为本来就没几支队伍),性能测试的提交截止日期是8月14日,没有答辩,ppt展示视频的提交时间是8月18日.
  • 决赛门槛区别:MIPS团体赛的决赛门槛是性能分数,决赛队伍数量一般是较为恒定的,只要性能排名能够达到一定水平,就可以进入决赛。而LoongArch挑战赛虽然没有实质性的决赛,但是仍有一个门槛,就是启动Linux
  • 评价方式区别:MIPS的团体赛最终评分是50%性能,50%系统展示和答辩,LoongArch则是70%性能,30%系统展示和答辩。
  • 官方提供的开发工具区别:MIPS团队赛只有提供功能测试、系统测试等一系列测试,而LoongArch提供了差分测试框架和随机验证的指令流,相比而言LoongArch的仿真验证手段更为先进。
  • 一些细节要求的区别:比如LoongArch挑战赛性能测试样例不同,性能测试不允许修改SoC,也不允许使用自己编译的Linux。以及LoongArch允许研究生参赛。

当然基本上参加比赛要完成内容是相近的,无非就是设计一个CPU,大部分本科生队伍都是遵循传统的5级流水线,在此基础之上增加流水级、增加顺序双发射、提高频率等等。能力强的队伍或者往届学长很强的队伍可以依照已有的框架进行部分的微架构创新,基本就是这样。

LoongArch指令集恰好也是在2022年大踏步进入开源社区,Linux、QEMU、GCC等诸多重要开源软件都在2022年加入了LoongArch的支持。于是接下来讲一讲龙芯这个LoongArch指令集与MIPS有什么不同。

首先是最明显的(大快人心)的变化就是去除了延迟槽。延迟槽是MIPS设计时对那个年代普遍具有的5级流水微架构做的过度设计,这种与微架构绑定的设计显然没有跟上飞速发展的硬件设计水平,在2022年看来,延迟槽这个东西属于无用且严重增加设计复杂度的指令集设计。LoongArch没有沿用这个诟病已久的特性我觉得是理所当然的。

其次是删除了协处理器这个概念,特权资源与RISC-V类似使用特权寄存器(CSR)来提供支持,以及浮点运算也不在由协处理器完成。虽然参加龙芯杯不需要实现硬件浮点部件,且在大部分RISC的实现中,浮点部件因为面积巨大,逻辑复杂,通常也是由专门的一个深流水部件完成,与主流水线是解耦的关系,但是我觉得这个仍然是一个好的设计,同样是去除了MIPS中不必要的微架构绑定,为更复杂的设计提供了预留的空间。

再次是对MIPS中的立即数编码做了修改,由统一16位修改为长20/短12的分离的立即数格式。这样做的好处在RISC-V上就能得到充分的体现,常用指令使用短立即数增大了指令的编码空间,长跳、立即数加载等指令使用专门的20位立即数,避免了使用短立即数带来的代码体积增加的问题。

除了上面这些与MIPS相比的优点,LoongArch与RISC-V相比也有相当大的不同。首先是LoongArch设计之初就考虑了大量扩展,相比于RISC-V的极简主义极致模块化,每个扩展缓慢发展,LA的设计更加偏向于商用和实用主义,其中不乏大量的tradeoff和edge case,指令格式的类型也比RISC-V要多很多。

队伍情况

原本以为LoongArch这么先进的基础设施和先进的指令集,以往的强校都会将有实力的队伍投入LA挑战赛中,结果实际上虽然LA有实力强劲的队伍,也有拿出了复杂的乱序设计的队伍,但是传统强校依然基本将重心放在MIPS的团队赛。我猜测(盲猜,不对请大佬指正)是因为这些学校都有传承了数届的基础设施,MIPS的开发流程也能够做到十分先进高效,往年也有优秀的学长的设计可以参考,做到什么样的程度能拿到什么样的名次都十分清楚,因此不太愿意放弃成熟的基础设施和开发流程转而来新的赛道踩坑。

相比之下虽然我校也曾经在龙芯杯取得过一等奖第二名这样的成绩,然而我校却严重缺乏MIPS的基础设施和学长的经验传承,体现在以下几个方面:

  • 没有差分测试框架。参加过多年的强校都有往届学长移植的差分测试框架,甚至有完善的CI自动化测试基础设施,这些基础设施我校一概没有。
  • 没有学长成功启动过Linux。由于MIPS赛道的决赛门槛并不是支持操作系统,决赛评分中支持Linux也只是保证了一个高的起评分,因此在没有差分测试的情况下,我校的往届学长往往选择做出一个不错的性能,然后靠较为高级的外设和互动来拿到系统展示的分数。没有适配系统的经验对于想要启动Linux来说是非常致命的。
  • 没有成熟的SoC和外设适配代码和流程。在操作系统上适配外设除了CPU以外SoC上也需要做很多的工作,包括地址空间分配,中断号分配,时钟分配等等。因此我校的系统展示基本仅限于裸机程序的展示,而且都没有使用中断,而采用轮询的方法避开SoC的工作。

今年我们的队伍是临时组建的,也就是说组队前基本互不认识(起码我都不认识),所以并不是什么深谙嵌入式开发/熟悉CPU设计的人。我们所有人,包括我都是在组队完成以后才开始接触入门CPU设计的。

比赛结束以后我翻了一下团队赛的演示视频(是的,我也不知道为什么出现在b站上但不是官号)。2022年第六届龙芯杯了,就我个人的感觉而言,我觉得仍然没有队伍所做的工作能够超越哈利橙他们2019年第三届龙芯杯的工作。的确清华大学今年的参赛队伍设计了乱序的CPU性能能够超过nontrivials-mips,但是按照IPC的比例我觉得其实并没有很好的发挥出乱序处理器应有的能力,而他们的系统适配基本沿用2019年的工作。这令我相信是神去了清华而不是清华培养出了神。

我们队伍今年的性能大约只有哈利橙他们的60%(因为没法直接对比),是因为流水cache出现了小概率bug,最后都没有调试出这个问题,因此只能提交主频低,IPC也不高的一个版本。如果流水dcache能够使用的话,我们的性能大约能够小幅超过他们。

参赛过程

以下是回顾参赛的过程,希望对我校或者其他参加龙芯杯的同学或者有志于从事CPU设计的同学有参考价值。

语言选择

我们队伍选择的开发语言是SystemVerilog,主要是考虑到我校的数字逻辑和组成原理都使用的是Verilog,而实际上2005年开始在IEEE的标准里Verilog其实就是SystemVerilog。相比于Verilog,SV主要的优点是有结构体多维数组,在总线等信号很多但是较为固定的地方SV也有提供接口这一个概念供简化代码。

我校的其他队伍和龙芯杯参赛的其他队伍也都有使用一些其他的HDL,例如SpinalHDL和Chisel等。这些高级的硬件描述语言的工作方式通常是由编译器将高级语言编译成为Verilog然后再交由仿真或综合工具生成网表等。此类高级语言通常都能够解决Verilog系的接线和端口声明冗长的问题,有的语言还有更多的语法糖可以实现OOP等更加高级的代码精简方法。

对于参加龙芯杯使用的HDL的选择,其实并没有一个谁好谁坏,我把一些关键的区别总结在下面共有需要的人参考:

Verilog系语言的优势:

  • 有完整原生的全套工具链支持,包括仿真、综合、实现、编辑&代码补全等,虽然并不一定很好用但是一定是有的。
  • 直接了当,生成的电路与代码描述的一致。
  • 相当多开源项目/开源IP核都使用Verilog。

Verilog系语言的劣势:

  • 代码冗余,开发效率可能因此降低,开发者不能讲精力集中在功能设计上。
  • 二义性语法多,如果出现未定义的语法,不同的工具可能行为不一致,非常影响开发。
  • 测试testbench编写麻烦。

Chisel类高级语言的优势:

  • 代码精简,开发效率高。
  • 生成的Verilog保证没有二义性。
  • 可以使用高级语言编写测试样例,较为方便

Chisel类高级语言的劣势:

  • 通常较新,工具链支持可能不足。
  • 可能存在编译器bug,而出现问题的时候由于生成的Verilog可读性较差,debug难度较大。
  • 需要学习一门新的语言。

当然有些优势/劣势并不一定符合你的实际情况,例如你熟悉java/scala语法,那可能Chisel等语言也很容易上手,所以对于语言的选择只要符合自己/自己队伍的情况就可以了,并没有必然的好坏。

前期准备

我校的龙芯杯宣传和备赛都开始得很早,在2021年秋季的数字逻辑设计实验课结束以后就开始了宣传和备赛,我猜测应该在大部分学校里面属于比较早的。我校2022年龙芯杯备赛的时候一开始来了11支队伍,30几个个人参赛,是的你没看错,我也觉得难以置信,而且往年也就是两支队伍+几个人。当然最后提交作品的大概就剩下3支mips+2两支la+8个个人左右(个人赛不太清楚)。

前期的话主要是以学习为主,切忌好高骛远,有的同学在开始动手之前先看了《超标量处理器设计》,然后打算先干一个乱序CPU,然后就没有然后了。我们队伍基本上到2022年3月都是在摸鱼/跟着《自己动手写CPU》抄代码,其他东西的准备也是几乎没有。直到我3月3号提交了ASC初赛的proposal,大家才开始干活(才开始大力push队友)。

4月初我们就分好了工,我负责分支预测,其他队友分别负责主流水线,DCache和总线。于是4月份我就在调研和复现,最终在4月底的时候完成了TAGE的RTL复现。五一假期的时候我们申请了一个教师做队伍内部交流(首次面基),那个时候DCache是说有bug还没有对接,axi已经有一个可用版本,主流水线的单发射版本已经可以进行功能测试了,双发射才开始改。

5月份我主要和主流水线的队友一起适配chiplab和修bug,其他队友可能在摸鱼。5月底的时候就基本完成了双发射CPU的代码编写,这个大约就算是完成了前期的工作吧

接下来就是debugdebugdebug

工作量和难点

对于参加LA赛道的工作量,我觉得最大的其实是适配系统。

虽然说支持Linux操作系统并不是两眼一抓瞎从头做起,官方的框架chiplab里面提供了一个十分完善的单发射五级流水的实例CPU,能够稳定启动Linux,因此理论上只要对着那个CPU增量开发就能够保证适配Linux。但是那个CPU充满了assign式的电路级别写法,十分晦涩难懂,所以我们选择了从头开始编写。然而对于完全没有CPU设计和系统适配经验的队伍来说,就算是有完整的实现可以参考,但是这个部分依然是工作量很大的部分。因为系统软件大量依赖特权资源,而这些特权资源其实并没有一个很好的仿真验证负载来测试,因此对于特权部分的调试我们队伍基本上只能依赖在仿真启动Linux,当然这个耗时久比较的久了。另外的是启动Linux操作系统有上亿条指令(没错,是真的上亿),这个对CPU的正确性要求很高,即便是有了6000万条的随机验证序列,chiplab的仿真SoC中也做了各种随机因素的引入,但是有的问题依然不能够在除了Linux以外的负载上复现。

其次就是DCache的一致性问题。LoongArch其实是一个软件维护一致性的ISA,相比于x86这种硬件维护的已经是大幅降低了硬件的设计难度,但是由于实际操作系统中访存指令的类型混杂,访存和一致性维护指令的混杂,使得DCache数据一致性的设计尤为重要但又十分难调试。我们队伍最终就是栽在了流水DCache的一致性维护上,导致Linux的启动并不稳定,有较低概率出现问题,只好提交一个旧的并不是最优的一个版本。

接下来才是CPU微架构的设计,或者其他的特性的设计。哪怕是设计一个乱序四发射的CPU,我相信除了访存部分,其他部分从设计到编写代码到调试完成,基本都可以在两个人月内完成。而访存部分的调试可能需要花同样多的时间。我们的队伍是以启动系统作为最高优先级,因此微架构上的设计可以说十分简陋和落后,仅仅是一个10级静态顺序双发射。

队伍时间节点

以下是我们队伍2022年参加龙芯杯的一些重要时间节点:

  • 2021年12月:组队
  • 2022年1月-3月:学习阶段,大家各自完成大部分的《自己动手写CPU》的内容
  • 2022年4月:分工完成,我负责分支预测和前端,我完成TAGE的论文复现
  • 2022年5月:完成主流水线并接入chiplab进行功能测试
  • 2022年6月11日:首次仿真启动Linux,无Cache
  • 2022年6月下旬:根据随机验证修复bug
  • 2022年7月初:遇到Verilog二义性问题,花费全队一个星期debug
  • 2022年7月12日:首次板上启动Linux,有ICache
  • 2022年7月底:前端分支预测、写直通法dcache完成
  • 2022年8月6日:前端freeze,首个可以提交的版本完成
  • 2022年8月7-14日:流水写回dcache调试,未成功
  • 2022年8月14日:提交旧版无bug版本
  • 2022年8月18日:最终提交系统展示和文档ppt等

感想

其实原本我校还有另一支队伍也参加了LA赛道,他们使用了Chisel作为开发语言,开始准备的时间比我们还要早很多,寒假结束就已经有一个五级流水的核了。而且他们的微结构比我们先进很多,也使用了记分板的乱序方法(没有重命名),但是他们最终没有调试出Linux,而无缘决赛。说到这个,不得不提我们最终的作品是一个静态10级流水的顺序双发射CPU,对于这个10级静态,我们队里的人和学长/老师都觉得十分浪费。访存所需的逻辑很多,导致基本上需要4-5级的流水来完成,但是作为一个静态的流水线,除了访存以外的指令在后面的访存流水中完全没有事情做,非常的浪费。而且我们的load to use问题解决的也不是很好,采取了保守的方式,而本可以采用更激进而高效的方法。但是其实我认为吧,LA这个赛道如果是第一年参赛,首要目标还是启动Linux吧,这也是我们队伍从一开始的原则。当然这导致我们的微架构没有充足的时间设计,只能草草沿用传统的静态长流水。

今年的龙芯杯也算是最终落下帷幕了,已经得知有乱序队伍今年Linux启动不稳定,打算明年继续了。

成绩

最终我们拿到了一等奖。对于这个成绩我还是十分满意的(好像没有更满意的成绩了)。其实也算有点意料之中吧,因为提交前看到LA群里的其他队伍的情况,很多队伍都还在调试Linux,估计是没有时间好好调性能了,我们虽然最终的流水DCache版本没有敢提交,但是前端是有仔细调优过的,性能也算中规中矩吧。

后续

项目代码整理完毕以后应该会开源出来。

接下来我也会撰写几篇文章描述我们的前端设计和TAGE预测器的详细复现内容,可以关注本博客或者订阅RSS。

Easton Man
Easton Man
文章: 37

一条评论

留下评论

您的邮箱地址不会被公开。 必填项已用 * 标注