Project5&6 Verilog开发流水线CPU

摘要

本文是笔者在使用Verilog开发流水线CPU过程中的一些记录与反思,希望能给大家开发流水线CPU带来一点帮助。内容主要分为冲突的覆盖性组合分析、带转发的数据通路与信号设计、初步代码编写、设计测试代码与调试过程以及一些思考五个部分,在设计与开发过程中会向读者频繁地传达流水线设计的工程化方法。


正文

虽然笔者在完成Project0~4的过程中并未对流水线有过深的认识,但是本文的内容基于完成Project4 Verilog开发单周期CPU,所以正文中会直接使用许多前期已经使用过的术语。
一些写在前面的说明:笔者设计的是MIPS指令集的常用指令,设计为五级流水线,使用的是集中式译码,使用延迟槽和控制冒险前移,数据冒险处理风格为Detector型。

冲突的覆盖性组合分析

虽然是先做了无转发数据通路的设计再开始的冲突分析,但是前思后想认为还是应该先讲讲这个。
在Project5阶段,指令集的大小为10,直接对每一对指令进行分析即可。但是到了Project6阶段,指令集的大小骤然增加为50条,一条一条地分析指令颇为耗时,所以笔者选择了将指令根据族别和功能进行分类,最终确实方便了编写控制单元和冒险单元的代码。
按照指令族别可以分为,load型、store型、Reg-Reg型、Reg-Imm型、Branch型、Jump型、Move型(mfhi/mflo/mthi/mtlo)。
而按照指令功能(是否涉及写回、有无符号等)又可以将每个指令族里的指令分得更细(JAL型、JR型、MF型、MT型),也可以将Reg-Reg型、Reg-Imm型、JAL型(jal/jalr)、MF型(mfhi/mflo)看作是Write-Reg型指令等等。为了方便分析,在本文中认为jalr既属于JAL型也属于JR型指令,而j指令则不必考虑数据冒险。
注意到Project6中需要支持乘除单元的指令,乘法需要5个周期,除法需要10个周期,但其只会对乘除单元的指令造成数据冒险,所以在分析指令冲突的时候可以将涉及乘除法寄存器的指令拿出额外考虑。
经过分类之后就可以进行轻松许多的数据冒险设计了。
首先需要确定 Tuse Tnew 的概念, Tuse 即指令进入流水线IF/ID寄存器最晚需求某个寄存器值的周期时间, Tnew 即在某个阶段的指令产生寄存器的写值(供应值)最少还需要的周期时间,数据冒险就在于指令间的供需匹配
对于 Tnew=0 的指令,其供应值已经产生,作为”供应者”的指令需要及时地给”需求者”的指令提供最新的数据,便于”需求者”进行自身的计算。这是转发
但是如果出现 Tnew>Tuse 的情况,这里出现了一个时间上的逆向需求,”供应者”无法及时地给”需求者”提供数据,这时候”需求者”就需要等待”供应者”的回应。这是暂停
对于暂停情况,考虑指令对之间 TnewTuse 的值可以算出要暂停的周期数,在本Project中这个周期数是不超过2的,暂停的做法也很简单,将当前ID阶段的指令锁住,清除EX阶段指令即可(相当于向EX阶段插入一条nop指令),实现上也不需要计算上述的值,只要知道现在是否需要暂停即可。
暂停信号可以写为stall = stall_ld_calr || stall_ld_st || stall_ld_bjr || stall_calr_bjr这五种情况,其中stall_ld_bjr需要同时考虑EX阶段和MEM阶段,但实际上这些情况之间是存在包含关系的,利用吸收律进行化简之后就只剩下三种情况和Busy信号的暂停了,这里不给出详细的做法,留给读者思考。
对于转发情况,WB阶段的指令可以通过GPR内部转发完成W→D的需求,而其他的情况就需要进行转发,常见的转发有M→DM→EW→E三种,但是特殊的情况会用到其他的转发,比如下面的例子:

lw $7, 4($0)
sw $7, 8($0)

sw指令进行分析,没有时间上的逆向需求,所以可以进行转发,但是这里的转发就应该是W→M了,如果再进一步讨论,可以发现对于sw rd, offset(rs)rd的转发都可以放在W→M进行,出于对之前Project的设计,笔者并没有选择改为W→M
关于为什么可以改为W→M,可以考虑lw-lw-swswrd转发,W→M会改写W→E的数据,所以数据还是最新的。
按照这样说似乎也可将一些W→E的转发改为M→D的转发,然而还是出于对之前Project的设计,这里不提倡修改,只需要保证最近的转发才是有效的即可(可以思考一下add-add-add的)。

带转发的数据通路与信号设计

由于Project 5的通路已经设计好,这一阶段剩下的主要就是进行一些简单的通路、信号归并,产生一些新的通路、信号,使得操作变得更易控制。
考虑指导书中的图,可以发现MEM级主要的转发源是ALUOut和PC+8,WB级主要的转发源是Result,所以可以设置一个EXOut表示ALUOut或PC+8便于转发。
lui指令的rs默认为0,将操作改成GPR[rt]←GPR[rs]+(Imm || 0^16)不会影响结果,反而可以把操作并入EX阶段的ALU中。
ID阶段有一些Link型指令(jal/jalr/bltzal)的效果都是将PC+8写入GPR,所以可以增加一个LinktoReg的信号来辨识Result是否为PC+8。
可以设计一个NPC部件对PC+4、BranchAddr、JumpAddr进行筛选。
注意到Project 6还需要设计关于比较部件、乘除部件、数据存储器的信号,这是指导书的图不能给出的,但是根据其他部件的信号设计方式,可以很容易得出这些部件所需的信号,此外还要对MF型指令设计多选器的信号等等。
自己进行这样的设计对于下一个Project进行自动机状态的设计是大有裨益的。

初步代码编写

Project 5是建立流水线CPU的主要部分,需要一定的时间进行代码的编写。而Project 6基于Project 5,但是需要增加和扩展信号的内容,相对于Project 5的工作,Project 6的工作主要是批量地引入信息、修改部件端口的属性、增加相应部件。
IF阶段注意IM的地址(将PC的第12位取反),EX阶段注意ALU的实现(见下)和乘除部件的设计,MEM阶段注意传入的信号。
如果之前对于加指令的实验操作做得还不错,这里都是很轻松的。

有符号数的无符号运算

这里存在一个坑点,Verilog HDL是一种硬件描述语言,而不是一种程序设计语言,运算有无符号是取决于整个表达式的,Verilog的扩散性设计(如果一个运算式中任何一个运算数是无符号的,那么整个运算也会是无符号的)会使得一些情况下有符号数在计算过程中变成无符号数,这里只举两个例子。

assign C_Signed = $signed(B) >>> A[4:0];
assign C =  Op == 4'b0110 ? C_Signed :
            Op == 4'b1011 ? ($Signed(A) < $Signed(B) : {31'b0, 1'b0} : 0) : 0;

考虑 C 的第一行为什么这样写,由于第一个冒号之后的值是无符号的,所以C_Signed也是无符号的。
考虑 $signed(A)<$signed(B)?{31'b0,1'b1}:0 ,这个表达式是无符号的,但是不取决也不影响 $signed(A)<$signed(B) 的符号,就算会影响其的符号,也是影响 $signed(A)<$signed(B) 值( 0 1)的符号,而对于 $signed(A)<$signed(B) 这个表达式, $signed(A) $signed(B) 都是有符号的,所以运算时是有符号的,值是无符号的。

设计测试代码与调试过程
测试指令的内容

对于存取型指令,需要设计一些指令测试功能,需要避免地址异常。
对于运算型指令,需要设计一些指令测试数据冒险。
对于逻辑型指令,需要设计一些指令测试功能与数据冒险,例如lui-lw的转发还是较为难设计的(由于DM地址太小= =)。
对于跳转型指令,需要设计一些指令测试延迟槽的功能与数据冒险。
对于分支型指令,需要设计一些指令测试分支功能与数据冒险。
对于乘除型指令,需要测试乘除型指令在数字为正负零的情况下的功能,以及Move型指令的功能,还需要测试乘除型指令与非乘除型指令的并行效果
构造一组还是很轻松的=_=,构造多组还是写代码随机生成比较好,注意存取地址的自然对齐,注意JAL&JR型的设计可以使用一些特殊的构造方式测试递归效果。

调试过程中遇到的BUG们
  1. 初始状态时出现一条高阻态的指令传入流水线寄存器,从而影响寄存器值。解决办法:在reset信号有效的时候清除流水线寄存器的内容,或者将reset信号当作暂停的一种。推荐前者,前者与之后的Project耦合性高。
  2. NextPC出现错误。解决办法:分析NPC部件的输入源,从而分析地址,分析信号,从错误的地方出发逐步查错(或二分查错)。(比如lw-jr的数据冒险)

似乎第2条的做法涵盖了大部分的查BUG方法=_=,笔者想不起还有什么问题了。

一些思考

最后给出笔者在开发过程中对一些事实的认知与思考,如有偏差欢迎批评指正。

设计风格与工程化处理

笔者的做法是利用工程化方法进行分析,再经过精简压缩,使用Detector型的风格进行编程,需要对流水线有一定的理解(为此抱着黑书读了一晚上),少写什么内容比较不容易找出来。如果习惯使用批量处理,可以按照Planer型的风格进行编程,难度降低,码量增加。

延迟槽

使用延迟槽之后,分支或跳转指令不会立即执行,而是先执行延迟槽中的指令,再到达新的地址。
这样做使流水线的设计难度降低,提高流水线的吞吐量,硬件无需处理编译调度指令。
但是槽里放入分支或跳转指令的不确定性仍是一个问题,而且微体系架构暴露给指令集是一种兼容性差的做法,实际上延迟槽的填充率也不是很高,相比之下分支预测反而是较为实用的做法。
注意在下一个Project中,延迟槽的指令产生异常返回的地址是上一条指令的地址(然而MARS不会这样做)。

Busy信号是否可以这样用

乘除相关的指令只利用到了暂停,却没有使用到转发机制,如果将Busy信号转化为Counter信号,得知乘除单元还需运行的周期数,也可增加流水线的吞吐量,只是设计上需要更加谨慎。

转发的数据来源

在Project5&6的指导书上都写有:为了解决数据冒险而设计的转发数据来源必须是某级流水线寄存器,不允许从功能部件的输出开始。
对于这种机制,可以认为是不增加计算(或读取)单元的操作延迟,更好地保证流水线的吞吐量。

潜在的转发

在Write-Reg型指令中存在一些指令,它们可以在ID级就得到写值,例如本Project中的lui,slt,slti,sltiu,sltu。若紧随其后的是Branch型或JR型指令,则不需要暂停,直接E→D转发即可,可以发现这确实是理论上存在的六种转发中漏掉的那一种。
做到ID级计算写值是否会延长周期长度?可以发现这五条指令的值就是EXT部件或CMP部件的输出,实际并没有增加流水线的负担。

不可预测行为

本Project大概存在五种不可预测行为,jalr $31, $31GPR[rs]当作地址时没有自然对齐、在延迟槽中放入分支或跳转指令、整数除零、乘除后并未立即从乘除寄存器取数,在本Project中需要正常执行这些指令,产生任意结果均可。在下一个Project中需要考虑是否产生异常与中断。

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在编写流水线 CPUVerilog 代码时,需要遵循一些基本步骤。这些步骤包括: 1. 确定 CPU 的体系结构:首先,你需要确定 CPU 的体系结构,包括它的功能、指令集、寄存器和内存等。 2. 设计 CPU 的控制单元:控制单元负责根据当前的指令来控制 CPU 的工作流程。 3. 设计 CPU 的数据路径:数据路径由各种功能块组成,如 ALU、寄存器、内存等,负责执行 CPU 的各种操作。 4. 编写 Verilog 代码:根据你设计的 CPU 体系结构和控制单元、数据路径,使用 Verilog 语言编写对应的代码。 5. 使用仿真工具进行测试:使用仿真工具对你编写的 Verilog 代码进行测试,确保代码的正确性。 希望这些信息能对你有所帮助。 ### 回答2: 使用Verilog编写流水线CPU需要以下几个步骤: 1. 确定CPU的架构和指令集:首先,需要确定CPU的整体架构,例如单周期、多周期或者流水线结构。接着,需要确定指令集的编码方式和指令的格式,包括操作码、操作数的位置等。 2. 设计流水线的各个阶段:流水线是由多个阶段组成的,每个阶段都负责CPU中的一个特定任务,例如取指、译码、执行、访存和写回等。根据CPU架构的要求,通过Verilog编写每个阶段的代码。 3. 设计寄存器和数据通路:流水线需要一些寄存器来存储中间结果,并通过数据通路连接各个阶段。在设计过程中,需要确定寄存器的数目和位置,以及数据通路中的信号传输方式,包括选择信号传输方式(并行或串行)、数据宽度等。 4. 编写控制逻辑:流水线中的每个阶段需要相应的控制信号来控制各个模块的工作,例如时钟、使能信号、选择信号等。根据具体的CPU架构,编写相应的控制逻辑,确保各个阶段按照正确的顺序进行。 5. 进行仿真和调试:编写完整的流水线代码后,通过Verilog仿真工具进行仿真,并使用仿真结果进行调试。根据仿真结果,发现和解决潜在的问题,例如数据冒险、控制冒险等。 6. 实现和测试:在进行仿真和调试之后,可以将代码烧录到FPGA或者ASIC芯片上进行实际测试。通过测试结果,验证CPU的功能和性能是否符合设计要求,如果出现问题则进行修改和优化。 总的来说,使用Verilog编写流水线CPU需要对CPU架构和指令集有深入的了解,并采用适当的设计方法和技术。通过不断的仿真和测试,不断改进代码,最终实现一个高效稳定的流水线CPU

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值