Loading

P5 && P6-流水线CPU(verilog实现)(已更新)

先说一下笔者的最终情况:p5考到第三次才过(AK),最后一次考试p6一遍过(AK),期末课设部分83分(听说过p6的上限分数是84),理论期末考试92分,总体来说成绩比较普通,但还可以接受,数字而已。p7因为懒惰寒假没有自己补上,所以这篇文章算是我的计组冒险终章了。以后的话,不排除总结一些理论相关知识的可能,只不过不一定是以博客的形式了,因为作为内容的创作者,我认为自己的内容质量很一般。

仅凭阅读本文,您必然不可能搭建出流水线CPU,但是,您的收获可能有:了解实现CPU时的一些细节;知道教程中讲的一些东西用人话怎么说明白,通过一些实例深入理解教程,纠正自己对于流水线CPU的一点误解;知道如何去手动造数据、通过其他途径获得测试数据和debug思路,知道课上测试可能考察的一些指令的类型以及实现方法。本篇文章相对于前几篇文章可能使用价值不高,关于流水线的verilog代码实现,可以去看吴佬的博客,除此之外,关于课上可能出现的奇怪指令,可以看这位学姐的博客

没错,像P3P4一样,我又遭遇了重写风波+自然死亡。

为何重写?一是第一版搭建时间过于长了,从刚开P5,到前天,我一直都是在忙着设计和修改第一版,由于一些基础概念没弄明白,教程中有一些东西没看懂,设计思路支离破碎,主观的加入了一些毫无必要的“优化”,端口命名和导线命名又犯了P4中出现的错误而变得十分麻烦,以至于mips顶层模块端口极多,非常复杂,连线难度巨大。在debug过程中,更是发现自己只会使用假的单步调试法,而不会“单周期”调试法,所以迟迟无法发现自己的bug。在向lcy大佬和讨论区诸多大佬的讨教经验之后,决定重新来过。至此,世界上多了一位重写P5的愚者。还好,没弄多久就造出来一个命名、接口个数、模块封装和连线都比较舒适的全新版流水线CPU。

2020/2/11更新

由于期末时时间太紧张+自己试验进度屡屡受挫+考试更新后莫名没保存上+寒假春训进度推进太快,到现在才把坑给填上,在这里表示抱歉。

由于P6是在P5的基础上进行的改造,几乎只是规模上的扩大,基本实现方法是一样的,所以本文笔者将P5和P6合在一起总结一下,且主要讲P5。由于笔者的设计风格并不好,所以本文没有给出具体的代码实现,防止带歪读者,设计风格问题本文也没有给出详细的探讨(其实这个问题很重要)。想了解的同学可以关注微信公众号“心中有13树”以及多多留意课程讨论区,里面有很多关于设计风格的文章,值得一读。

由于笔者水平实在有限,所以即使是笔者写在文中感觉比较稳的部分,也难免有表达不当甚至理解不到位之处,还请各位在评论区批评指正。

P5的要求是我们要用verilog实现一个流水线CPU,支持10条指令(和P4似乎就差一个jalr跳转指令),P6是支持50条指令。

P5相对于P4绝对是难度上的飞升,一个原因是P5实验开始时,可能很多老师的理论课还没有讲到或者没有讲完流水线部分,所以如果自己属于进度最快的那批的话,就得尽快理解流水线CPU的工作机制以及暂停转发的原理,这一点是困难的;另一个原因,是我们要求的流水线CPU必须是全速转发的,也就是说,能不暂停就尽量不暂停(比如lw后面加计算指令冲突的就是必须要暂停的,而两个计算指令冲突则是可以全速转发避免暂停的),这就导致了我们不能用无脑5周期暂停法去实现它(不过在课上测试的时候实在不会写转发可以用5周期暂停法委曲求全,数据如果良心的话有概率过),所以转发机制就会相当地复杂,思考量和代码量刷的一下子就上来了,最后好不容易写出来了也很难去遍历所有可能的冲突情况去构造测试样例。即使P5再困难,我们也必须去克服它,不然这门课就挂科了(滑稽)。

鉴于本实验难度确实较大,所以在搭建P5CPU之前,请确保自己学了(不一定是学会)流水线CPU的相关内容(参考课件或者黑书都是可以的),会分析典型的数据冒险;确保自己在仿真时能够正确地使用工具进行高效的debug;确保自己研读过高老板的工程化方法的课件……有了上述知识,我们就可以考虑进行试验了。笔者这篇文章意在把教程以及课件中一些不是人话的东西翻译成人话,以便于理解。理解能力较强的大可自己放手看课件与教程去做。如果确认要阅读此文,也请先夯实流水线理论课知识以及至少浏览一遍工程化方法教程。

为了方便各位正确理解笔者在说什么,先放一张自己对于流水线CPU的分级图:

以下讨论基于这张图里的命名,即仅说一个字母时代表两级流水线寄存器之间的部分,两个字母连起来是代表对应位置的流水线寄存器。可能与有些读者的习惯不太一样,所以请按照本命名阅读文章之后再翻译成您所习惯的命名方式。

首先,我们需要进行数据通路的设计,在这里,我们仍然需要使用在P4中使用过的列表的方法,在不考虑控制单元以及任何冒险的情况下,把各个指令的数据通路列出来,建议用excel表格。表格的格式,请参考工程化方法课件。与P4不同的是,我们这里将流水线分为了F,D,E,M,W这5级,并且每两级之间要用流水线寄存器分隔开。注意GRF的特殊性(物理上是1个,逻辑上是2个),要考虑好回写数据的来源。注意D级的各种小部件,这些和控制冒险有关。关于流水线寄存器,它并不是什么很玄的东西,其实就是定义若干的register,当时钟上升沿来了的时候将数据写入register而已。流水线寄存器在表格中写起来的困难之处在于,由于以后我们还要加转发,所以我们一下子想不清楚要把哪些必要的值传下去。如果纠结于这个问题,我推荐大家先不要花费太多时间空想,现在就暂时只把它作为单周期的时候用到什么值传下去就好了,千万别在这个时候就纠结转发相关的问题,不然进度会非常慢(比如笔者就由于太纠结转发,用了一天多才填完数据通路表,没写一行代码)。有必要说的是,各位在列表格的时候,仔细考虑一下接口命名,良好的命名能够在P5中发挥巨大的作用,少出很多奇怪的连线bug。事实上,接口问题是P5的一个很重要的问题。很多时候我们会因为不知道接口需要有哪些,而不能理解我们为啥要造这么一个模块。只有分析好了接口,我们才能有的放矢的去实现功能。如果实在想不好怎么命名,可以参考一下学长学姐的命名,不建议采用高老板课件中的命名。

基本的插入流水线寄存器的数据通路大表格写好之后,考虑控制器。事实上,如果使用分布式译码(每个流水级有专属的控制器)的话,P5和P4的控制器基本一样,可以把P4的复制粘贴一下,大家看缺哪条指令就把哪条指令加进去,把控制信号加进去就行了。考虑到P5之后的任务都是在P5上扩展,所以控制信号(比如ALU的选择计算功能的那个信号)位数可以多一点,5位肯定够用,4位的话就得看个人设计情况了,有的人够有的人不够。注意,在顶层中,每级流水线不会用到控制器的所有接口,所以没用到的那些就不要在顶层写出来了,不然导线多得能烦死。当然,除了分布式,大家也可以考虑集中式译码,由于笔者没有尝试过,所有对集中式译码不做过多评论。

接下来就是P5最难的部分:冲突单元设计。在设计冲突单元之前,我们仍然需要先列一个表格,表格的内容是各种指令(更本质的来讲,是各类指令)在不同级时可能出现的数据冒险。在这里,我使用的是教程中的Tuse和Tnew分析法。重点再强调一下,Tuse是当某条指令还在D级(也就是还没写入DE级流水线寄存器的时候)时,至少再等多少个周期(也可以说是还得再来几个上升沿)就必须用到数据,Tnew是当某条能写入数据的指令,再过多少个周期才能产生最新数据(此处的产生是已经写入到流水线寄存器,如果只是刚算出来还没写入流水线寄存器的话我们不认为是产生,原因考虑时钟周期)当Tuse>=Tnew时,是可以来得及转发的;当Tuse<Tnew时,是来不及转发的,必须暂停。如果考虑不明白这个方法的原理以及这个结论的话,请大家仔细思考我们理论课中所说的“数据必须在时钟沿来之前准备好”这句话。如果大家在这里真的遇到了理解上的困难,希望大家能向老师或者大佬请教,请他们用一两条典型的指令去手动给你模拟一下冲突产生原因以及转发过程,这样就不会死钻牛角尖而没有收获了。至于表格的细节部分,请参考我们的教程部分以及高老板课件。

稍微再说一点有些多余但能让大家更好地理解的东西:我们为啥要列这个表格?简单来说,就是我们想知道什么时候暂停, 什么时候转发,我从哪里转发,我转发到哪里。一旦我们判定了某个时候该转发时,在实现上我们的做法是:生成一个控制信号,这个控制信号接到一个多路选择器上,多路选择器选择我们要把哪个作为某个部件的输入。比如我们可爱的ALU,它可能的数据来源是什么呢?D级从GRF中读出来顺着往下流水的,EM级流水线寄存器转发回来的(连续计算指令导致),MW级流水线寄存器转发回来的(load后加计算指令导致)等等。比如我们现在在D级正有指令addu,在E级正有指令subu,addu读取的某一个寄存器和subu写入的寄存器是同一个寄存器,发生数据冒险,并且可以分析addu的Tuse=1,subu的Tnew=1,所以我们可以通过转发解决这个问题。当下一个时钟上升沿来的时候,E级的计算结果写入了EM级寄存器,D级的数据流入DE级流水线寄存器,并且进入了E级进行了计算,只不过它的结果还没有被存储(形象地说,ALU的计算结果正“顶”在EM级流水线寄存器的左端,但是它目前还没顶进去)。这个时候,我们在ALU的两个输入端都加上多路选择器。假如ALU下面那个输入端口的是EM级流水线寄存器结果需要到达的目的地,我们就让这个结果连到ALU下面的端口对应的多选器上,并且产生一个选择EM流水线寄存器转发结果(而不是D级流水数据或是MW转发数据)的信号,这样ALU就是使用正确的值在运算了。由于E级本身是个组合逻辑,所以在转发之后它最终稳定的结果是正确的。当下一个时钟上升沿来的时候,这个正确结果就流水下去了,我们转发的目的就达成了。相信我这样说了之后,大家就更能理解转发机制的工作了。其他的转发情况的分析与实现是类似的,请大家仿照例子自行思考。暂停比较简单,可以完全参考课件,注意暂停时清空哪些寄存器。

上一段文字中提到了我们选择转发数据的多路选择器,这些多路选择器的控制信号就是多种多样的转发信号了。为了设计这些多选器,我们也需要列表格去分析多选器可能会有哪些数据来源(比如刚才说的ALU输入端的多选器的可能来源)。这个部分请大家参照高老板课件,多路选择器命名也可以参考高老板的命名方法(MFRSD,MFRSE等等)。

怎么转发可能大家已经都知道了,那么,如何实现检测冲突呢?我们先想一想冲突的原因:要写的寄存器和下面一条指令要读的寄存器是同一个。所以,我们可以汇总每一级中的要写的寄存器和要读的寄存器(rs,rt,rd),将它们作为冲突单元的输入,然后在冲突单元中把所有可能的冲突都列举一遍并生成相应的多选器信号就好了。在这里列举时我使用的暴力转发法,各位可以在教程中自行学习,暴转思路真的很流畅!

控制冒险?注意D级各种小部件的实现,注意延迟槽。参考课件

到这里,当我们写完了各个流水级的模块代码,写完流水线寄存器的代码,写完冲突单元的代码,写完多选器们的代码,以及copy完P4的控制器代码,这个CPU的所有单元就都有了,只需要在顶层进行连线就可以了。在这里,我们推荐把每个流水级的模块打包成一个整体模块,比如我们可能取址级有一些PC+4和多选器之类的小东西,统统封装为F模块,这样可以减少顶层连线。当封装完毕,连线也完毕之后,可以说流水线CPU就搭过了。接下来就是测试与debug了。

如何测试呢?先提一句,评测机是不够的,即使是所谓的中强测,也是很不完备的,我们仍需要构造更全面的样例进行测试。首先是进行功能测试,测试每条指令执行结果对不对。在这里,大家可以在测试代码中每一条指令就后面跟上5个nop,这样保证无冲突。功能测试能测出来很多明显的sb连线错误。在功能测试没问题之后,就是进行暂停转发测试了。在这里,笔者完全不赞同自己写花里胡哨的一个测试程序去测,这样根本测不全。那应该怎么测?先静态debug:看好你的冲突单元,看看你枚举过的那些冲突类型,先静态观察一下是不是写够了,可以和同学们交流一下看看是不是写够所有情况了,要是漏了情况就肯定错了。如果没漏情况,我们开始造数据测试:把你列举的每一种情况,都写一段小程序去单独地测冲突。比如E级写回的rd寄存器与D级使用的rs寄存器的冲突,涉及到的指令(比如写rd寄存器的addu,subu,以及P6时的很多计算指令balabala,读rs寄存器的所有指令)我们都得分别地测冲突。在这里,大家就可以考虑优化自己的冲突单元,看看是不是可以将指令分类,这样我们就可以按照指令种类从中选一个代表元而不是每个指令都得枚举出来测冲突了(比如分成calculate_r,calculate_i,b,j,jal,jr,jalr,load,store这么几种,过P6足够)。手动生成测试样例是很麻烦的,但是为了覆盖性测试,我们必须手动来一遍以保证全面性,等覆盖完了,可以写评测机或者借大佬写的评测机再测几千组数据。另外,关注过我写过的其他文章的读者可能还知道6系流传着P5的一份覆盖性很好的测试数据 ,大家也可以找一下那套数据进行测试。但为了大家真正学到东西,希望大家能自己先手动造数据,然后再通过其他数据来提高自己CPU的“置信度”。不然的话,即使过了P5,你也可能因为自己没有手动造数据这个过程而在没有现成的数据支撑的情况下挂掉本来应该白给的P6。自己写测试用例生成器乃至评测机是可以的,不过你可能需要一些前置知识,可以参考我写的某篇博客来初步了解(但我提供的远远不够)。

通过以上讲解,P5要求的流水线CPU就基本完成了,之后能不能过就看各位的本事了。P5正考可能挂的概率比较高,但是后来会有大佬在讨论区总结题型,甚至可能把课上的指令的行为抄下来分享出来(不知道是否会被查水表),所以之后的P5课上可能就会有同学以非常夸张的速度完成。

关于P6,乘除模块以及DM扩展请看教程,注意Instruction memory不要再从0x3000开始了。P6主要是加指令,都比较好加,然后就是加入乘除之后的数据冒险。我们可以把乘除指令和ALU的一些计算指令一起处理,就可以几乎不改动冲突单元了。其实P6大家可以多看看应届讨论区,里面的内容应该会更好更有针对性。要想写得漂亮,加指令的时候改动少,那么就得注意自己的设计风格。请大家留意设计风格之planner VS detector,选择适合自己的将代码进行优化,防止自己课上手忙脚乱出错。

最后,希望大家能够AKP5 && P6,冲向P7P8。笔者由于只通过了P6,所以只能分享到这儿了。愿大家都能在计组中挥洒汗水与泪水,收获成功的喜悦!奥利给!

posted @ 2019-12-05 16:49  BUAA-Wander  阅读(5647)  评论(2编辑  收藏  举报