单周期CPU设计与实现原理分析


单周期CPU设计与实现原理分析

一.单周期CPU的设计思路

①基础模块:
a.Instruction Memory指令存储器,用作CPU指令的存储与输出
b.ALU算术逻辑单元,用作CPU内部各项逻辑运算
c.PC程序计数器,负责管理CPU指令地址,并保存当前指令地址
d.Control Uint控制单元,用作控制各个指令相对应的控制信号
e.Data Memory数据存储器,用作存储ALU的计算结果或寄存器组中的数据,或者输出其所存储的数据
f.Register File 寄存器组,根据指令存储和读取相对应地址中的数据。
g.Sign, zero Extension 位宽扩展单元,用作将16-bit位宽的立即数扩展为32-bit位宽的有符号数或无符号数
②顶层模块:
a.SCPU模块,将各基础模块的逻辑输出进行逻辑连接,实现数据和信号的联通

二.单周期CPU的模块实现

① Instruction Memory指令存储器的设计

指令存储器的功能是存储读入的所有32-bit位宽的指令,根据程序计数器PC中的指令地址进行取指令操作并对指令类型进行分析,通过指令类型对所取指令的各字段进行区分识别,最后将对应部分传递给其他模块进行后续处理。
1.设计流程图
这里写图片描述
2.设计核心
这里写图片描述
读取指令:指令存储器中每个单元的位宽为8-bit,也就是存储每条32-bit位宽的指令都需要占据4个单元,所以第n(n大于或等于0)条指令所对应的起始地址为4n,且占据第4n,4n+1,4n+2,4n+3这四个单元。取出指令就是将这四个单元分别取出,因为指令的存储服从高位指令存储在低位地址的规则,所以4n单元中的字段是该条指令的最高8位,后面以此类推,并通过左移操作将指令的四个单元部分移动到相对应的位置,以此来得到所存指令。

② ALU算术逻辑单元的设计

ALU算术逻辑单元的功能是根据控制信号从输入的数据中选取对应的操作数,根据操作码进行运算并输出结果与零标志位。
1.设计流程图
这里写图片描述
2.设计核心
a.ALU逻辑运算部分:在其中任意操作数或者操作码发生变化时,根据模块内操作码的输入,选择对应逻辑运算并执行计算出结果,至于逻辑运算的具体实现参照上述表2记录。
这里写图片描述
b.ALU输出部分:若ALU所得输出结果result为0,则将zero标志位设为1,否则设为0。
这里写图片描述
c.操作数选择部分:两者输入其实都是二选一多路选择器模块,唯一的区别就在于A操作数的选择存在一个5-bit位宽的sa偏移量输入,所以在输入端口上有一点不同。
这里写图片描述
这里写图片描述

③ PC程序计数器的设计

PC程序计数器用于存放当前指令的地址,当PC的值发生改变的时候,CPU会根据程序计数器PC中新得到的指令地址,从指令存储器中取出对应地址的指令。在单周期CPU的运行周期中,PC值的变化是最先的,而且是根据PCSrc控制信号的值选择指令地址是要进行PC+4或者跳转等操作。若PC程序计数器检测到Reset输入信号为0时,则对程序计数器存储的当前指令地址进行清零处理。
1.设计流程图
这里写图片描述
这里写图片描述
上图是PC指令计数器的关键部分设计流程图,主要实现当前指令的存储和指令的跳转;而下图则是指令地址计算模块,本质上是一个三选一的多路选择器,根据不同的PCSrc控制信号选择不同的跳转方式得到下一个指令地址。
2.设计核心
a.PC当前指令地址记录和跳转部分:是CPU指令运行的总控中心,用以确定当前需要执行的指令的地址,并且在CLK上升沿或者Reset下降沿到来的时候来实现对下一个地址的跳转或者对当前地址的重置。
这里写图片描述
b.PC跳转地址计数部分:通过Control Unit控制模块分析指令得到的PCSrc信号量来确定下一步指令地址的跳转方式,然后将其转变为逻辑操作实现对下一个指令地址的计算。这其中需要注意以下几点:
(1)在进行PC值的操作时,要注意I型指令中beq与bne的立即数为16位,经过扩展后传入程序计数器中时为32位,但J型指令j的立即数为26位,所以这两个立即数不能混用,需要分别设置两个输入端口;
(2)在进行跳转时,因为指令存储单元总是8-bit位宽,每条指令占据的单元数总是4个,所以指令地址总是4的倍数,为了可以记录更多的地址,因此无论是beq指令、bne指令还是j指令在操作时都需要左移2位。但是在处理j指令产生的立即数时,因为指令中的立即数已经直接取出了要跳转地址的第2位到第27位作为addr的值,已经直接实现了右移,所以在得到的新地址的最后两位我们需要重新为其添上两个0进行2位左移来还原正确的新地址(如下图所示);
(3)在CPU的内部通路图中,PC+4,跳转与数据选择器三个模块是分开的,但这样操作会需要更多的逻辑连接,增加顶层模块逻辑连接的复杂度,且它们在功能上和设计上相近,本质上属于同个功能板块,所以设计成一个综合的PC跳转地址计数模块可以更加简化我们的CPU设计。
这里写图片描述

④ Control Unit控制单元

控制单元通过输入的zero零标志位与当前指令中对应的指令部分来确定当前整个CPU程序中各模块的工作和协作情况,根据CPU运行逻辑,事先对整个CPU中控制信号的控制,以此来达到指挥各个模块协同工作的目的。
1.设计流程图
这里写图片描述
2.设计核心
控制信号的逻辑控制实现:控制单元的信号输出是根据实验原理中给出的各个CPU指令与控制信号的关系表实现的,并通过化简真值表实现更高效地信号控制。
这里写图片描述

⑤ Data Memory数据存储器

数据存储器是CPU的数据管理中心,在时钟下降沿到来的时候负责将CPU执行过程中得到的数据写入到对应地址的存储单元内,同时根据控制信号与输入的取值地址输出对应的数据以供CPU执行指令所用。
1.设计流程图
这里写图片描述
2.设计核心
a.内存数据读取:在数据读取时,先判断mRD控制信号是否为1,如果为1则表明程序需要从内存读取数据,于是可以根据输入的地址(假设数据所对应的地址为n,则该数据所占据的存储单元地址分别为n,n+1,n+2,n+3)将四个单元内的数据取出,再通过左移操作还原整个数据。
这里写图片描述
b.内存数据写入:与指令存储器相类似,数据存储器的每个存储单元也为8-bit位宽,所以要将需要存储的32-bit位宽的数据分成四个部分,分别存储到4个对应的存储单元内。在数据存储过程中,我们同样服从高位数据储存在低位地址中的规则,只需注意在读取的时候同样服从该规则即可实现正确的读取。每当时钟信号CLK的下降沿到来时进行判断是否进行数据的写入,如果mWR控制信号为1,则表明需要向数据内存写入数据,所以数据的读取和写入的条件不同。
这里写图片描述
c.内存数据的地址:需要执行操作的数据的地址是由ALU模块的输出值决定的,主要与lw和sw两个指令有关。
这里写图片描述

⑥ Register File 寄存器组的设计

寄存器组中的每个寄存器位宽32-bit,是存放ALU计算所需要的临时数据的,与数据存储器不同,可能会在程序执行的过程中被多次覆盖,而数据存储器内的数据一般只有sw指令才能进行修改覆盖。寄存器组会根据操作码opCode与rs,rt字段相应的地址读取数据,同时将rs,rt寄存器的地址和其中的数据输出,在CLK的下降沿到来时将数据存放到rd或者rt字段的相应地址的寄存器内。
1.设计流程图
这里写图片描述
2.设计核心
a.读取操作部分:寄存器组在读取时各一个读取结果是rs寄存器中的值,第二个读取的结果是rt寄存器中的值,同样也是为了保持0号寄存器的值为0,所以当所取寄存器为0号寄存器时,值自动取为0。
这里写图片描述
b.写入操作部分:在CLK的下降沿到来时进行数据写入,要注意WriteReg不为0且WE为1才能写入,这是为了防止误写入和保持0号寄存器的值为0。
这里写图片描述
c.写入地址选择部分:写入地址WriteReg根据控制信号WriteDst控制信号的值确定它是rt字段的地址还是rd字段的地址,本质上是一个二选一多路选择器。
这里写图片描述

⑦ Sign, zero Extension 位宽扩展单元的设计

位宽扩展单元根据输入的ExtSel控制信号将一个16-bit位宽的立即数扩展为32-bit位宽的有符号数或无符号数。
1.设计流程图
这里写图片描述
2.设计核心
扩展控制部分:根据ExtSel确定是否为符号扩展,若ExtSel为0则进行0扩展,若为1,则进行符号扩展,符号扩展时,扩展位由待扩展立即数的二进制表示数的首位决定。
这里写图片描述

⑧ 顶层模块的设计

顶层模块作用为连接各个模块单元的控制信号与数据,实现模块间逻辑连接,进行CLK和清零标志位的输入,并输出Basys3板所需要的数据。
这里写图片描述

三.Basys3板设计思路

Basys3板是单周期MIPs指令CPU的运行载体,它与CPU的时钟信号连接;并且将Reset开关与CPU中的清零信号相联系;最后将CPU运行的结果输出。Basys3板的设计可以分为四个单元:
a.数码管显示控制单元
b.二进制数的译码单元
c.显示数据的选择单元
d.时钟分频器

四.Basys3板功能单元实现

① 数码管显示控制单元的设计

Basys3板上的4个7段数码管不能同时显示4个不同的数字,需要通过循环调整其使能端的高低电平来让它们快速交替显示数字,从而达到视觉上同时显示四个不同数字的错觉。数码管显示控制单元通过一个计数器来控制显示顺序。外部CLK的上升沿到来则计数器加1,当计数器值为100,000时计数器清零,并且四个数码管的使能端分别变化。
1.设计流程图
这里写图片描述
2.设计核心
a.初始化:需要使能端的高低电平初始化,否则计数器第一次开始的时候无法使用case语句进行高低电平的变换,需要额外设置,这会增加代码复杂度。
这里写图片描述
b.数码管循环点亮:因为Basys3板的7段数码管是共阳极的,所以高电平为无效,低电平为有效电平,这里我们初始化最左边一位数码管为低电平,等到程序开始第一个上升沿到来就直接开始进入循环,从最右边一位开始显示。
这里写图片描述

② 二进制数译码单元的设计

译码单元将CPU运算的结果转换成7段数码管中各个数码管显示所需的高低电平信号,该单元的输入为4-bit位宽的二进制数。其中,七段数码管的八个电平控制输出中最高位是小数点的显示信号,但小数点在这次实验中没有用到,所以默认全部设置为1。
这里写图片描述

③ 数据选择单元的设计

显示数据的选择单元在CPU时钟的上升沿或者下降沿到来时根据当前使能端的高低电平与SW开关的信号确定当前数码管应显示的二进制数字。
1.设计流程图
这里写图片描述
2.设计核心
数据选择:先根据使能端高低电平来确定需要选择数字在结果中的位置,再进行显示数据的选择。其中newAddress是下一个指令地址,Reg10Out是rs寄存器的值,Reg20Out是rt寄存器的值,WriteData是DB总线的值,CurPC是当前指令地址,instcode是当前指令(rs和rt寄存器的地址实际上是指寄存器端口),ALU_Out是ALU模块的计算所得值。由实验原理可知,显示数据与SW信号的对应关系如下:
SW_in = 00:显示 当前 PC值:下条指令PC值
SW_in = 01:显示 RS寄存器地址:RS寄存器数据
SW_in = 10:显示 RT寄存器地址:RT寄存器数据
SW_in = 11:显示 ALU结果输出 :DB总线数据。
这里写图片描述

④ 按键控制的设计

需要设计时钟分频器因为显示管使能端变化的频率应该明显高于CPU的时钟频率,才能够正常显示CPU运算的结果。在设计中,它们相差的倍数为1000倍,并且通过计数器来实现时钟信号的分频。
1.设计流程图
这里写图片描述
2.设计核心
a.消抖操作:因为Basys3板采用的是机械按键,在按下按键时按键会出现人眼无法观测但是系统会检测到的抖动变化,这可能会使短时间内电平频繁变化,导致程序接收到许多错误的触发信号而出现许多不可知的错误。消抖操作是每当检测到CLK上升沿到来时检测一次当前电平信号并记录,同计数器开始计数,若在计数器达到5000之前电平发生变化,则将计数器清零,若达到5000,则将该记录电平取反输出。
b.初始下降沿触发:因为程序开始时已经运行第一条指令,为避免跳过第一条指令计算值的写入,我们的输入需要从下降沿开始,因此我们给按键信号取反后再输入。
这里写图片描述

五.实验板实现尚存的一点问题

在按键控制设计中我们已经提到,我们的按键设置是对按键脉冲取反的,即按键一次我们会获得一个先是下降沿后是上升沿的电平脉冲,这导致我们每次按键,都相当于是执行完前一个周期指令的后半个周期,然后再执行后一个周期指令的前半个周期,原理上每次快半个周期。

经过多次反复的实验验证,在初始情况下,将复位键置为高电平,程序等同于已经执行一个上升沿,而由于wire和reg类型各自的差异,个人始终没有找到方法对时钟控制电平(即对myCLK变量)进行初始化设置,到目前也还没有找到如何避开这个上升沿的方法,大家可以作为解决问题的其中一个突破口。

综上而言,单周期CPU的设计过程并不算复杂,相信大家只要能稍微花点时间理解一下Verilog的层次结构设计,可以很快完成这一实验。

  • 16
    点赞
  • 118
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值