使用Verilog搭建一个单周期CPU
使用Verilog搭建一个单周期CPU
搭建篇
总体结构
其实跟使用logisim搭建CPU基本一致,甚至更简单,因为完全可以照着logisim的电路图来写,各个模块和模块间的连接在logisim中非常清楚。唯一改变了的只有GRF和DM要多一个input PC端口,用来display的时候输出PC值;IFU同理多了一个output PC,用来把PC的值传给GRF和DM。其他的模块我都是直接对着logisim原封不动地用Verilog重新实现了一遍。目前支持指令集{addu、subu、ori、lw、sw、beq、jal、jr、nop、lui、sb、lb、sh、lh、jalr、addi}。
IFU
端口如下图所示,仅多了一个output PC,实现应该是非常简单的。
但是要注意一点,如果进行了初始化(如下),那么一定不能用非阻塞赋值,否则你会发现你的IM根本读不进code.txt里的内容(非阻塞赋值会在initial后进行赋值0,读的code.txt又被清成0了,所以啥也读不到)
initial begin
pc = 32'h00003000;
for(i=0;i<1024;i = i + 1) begin
im[i] = 32'h00000000;//正确写法
//im[i] <= 32'h00000000;//错误写法
end
$readmemh("code.txt",im);
end
GRF
端口多了一个input PC,用来display的时候可以获取到PC值以输出。
这个需要注意的是0号寄存器,他不能被写入,特判一下就可以。也可以和我一样reg [31:0] rf[31:1];
,根本没有0号寄存器自然也写不了他,然后输出寄存器值的时候assign RD1 = (A1 == 0) ? 32'b0 : rf[A1];
,判断是否为输出0号寄存器的值。
EXT
用位拼接写,非常简单,符号扩展就将最高位复制就可以了。
assign ext_imm16 = (EXTOp == 2'b00) ? {{16{1'b0}},imm16[15:0]} :
(EXTOp == 2'b01) ? {{16{imm16[15]}},imm16[15:0]} :
(EXTOp == 2'b10) ? {imm16[15:0],{16{1'b0}}} :
{{16{1'b0}},imm16[15:0]};
但是注意不要在位拼接里出现没位数的数,Verilog会默认成32位,而不是你想象中的1位。错误示范如下:
assign ext_imm16 = (EXTOp == 2'b00) ? {{16{0}},imm16[15:0]} :
(EXTOp == 2'b01) ? {{16{imm16[15]}},imm16[15:0]} :
(EXTOp == 2'b10) ? {imm16[15:0],{16{0}}} :
{{16{0}},imm16[15:0]};
总之写Verilog的时候养成好习惯吧,数字都加上位数和进制,防止在奇怪的地方出错还找不到bug。
ALU
这个就更简单了。没啥写的就说说上次那个奇偶校验跳转的指令吧。
首先Verilog里是有异或运算的:^
,这个就是异或,不要课上用|
和~
手写一个异或出来。然后我们知道缩减运算符^A
就等于A[31]^A[30]^A[29]^...^A[1]^A[0]
。那么A中有奇数个1就相当于^A == 1
,A中有偶数个1就相当于^A == 0
,然后就跟beq一样跳转就可以了(beq的Zero是A == B
,这个指令的Zero是^A
)
DM
这个也比较简单,我依然加了lb、sb、lh、sh指令,在Verilog里写只需要用位拼接来写就可以了,比logisim方便不少。
对写入数据进行处理:(SSel为2'b00即原数据,SSel为2'b01即sb时,SSel为2'b10即sh时)
always @(*) begin
if(SSel == 2'b00) begin
WData = WD;
end
else if(SSel == 2'b01) begin
WData = (A[1:0] == 2'b00) ? {dm[A[11:2]][31:8],WD[7:0]} :
(A[1:0] == 2'b01) ? {dm[A[11:2]][31:16],WD[7:0],dm[A[11:2]][7:0]} :
(A[1:0] == 2'b10) ? {dm[A[11:2]][31:24],WD[7:0],dm[A[11:2]][15:0]} :
{WD[7:0],dm[A[11:2]][23:0]};
end
else if(SSel == 2'b10) begin
WData = (A[1] == 1'b0) ? {dm[A[11:2]][31:16],WD[15:0]} : {WD[15:0],dm[A[11:2]][31:16]};
end
end
对读出数据进行处理:(LSel为2'b00即原数据,为2'b01即lb时,2'b10即lh时)
always @(*) begin
case(LSel)
2'b00:begin
RD = dm[A[11:2]];
end
2'b01:begin
RD = (A[1:0] == 2'b00) ? {{24{dm[A[11:2]][7]}},dm[A[11:2]][7:0]} :
(A[1:0] == 2'b01) ? {{24{dm[A[11:2]][15]}},dm[A[11:2]][15:8]} :
(A[1:0] == 2'b10) ? {{24{dm[A[11:2]][23]}},dm[A[11:2]][23:16]} :
{{24{dm[A[11:2]][31]}},dm[A[11:2]][31:24]};
end
2'b10:begin
RD = (A[1] == 1'b0) ? {{16{dm[A[11:2]][15]}},dm[A[11:2]][15:0]} : {{16{dm[A[11:2]][31]}},dm[A[11:2]][31:16]};
end
default:RD = 32'h00000000;
endcase
end
话说回来我的测评点生成机好像忘记了测lb、sb、lh、sh(逃
MUX
这个一定不要按高老板ppt里的那个写。我一开始按他的写然后de了半天才找到原来是MUX的错误。
高老板写法:
后来改成了三目运算符就AC了。。。现在也没看懂他的是什么原理(也可能是对的?
assign Out = (S0 == 0 && S1 == 0) ? D0 :
(S0 == 1 && S1 == 0) ? D1 :
(S0 == 0 && S1 == 1) ? D2 :
D3 ;
Controller
先写宏定义
`define ADDU 6'b100001
`define SUBU 6'b100011
`define ORI 6'b001101
`define LW 6'b100011
`define SW 6'b101011
`define BEQ 6'b000100
`define JAL 6'b000011
`define JR 6'b001000
`define LUI 6'b001111
`define LB 6'b100000
`define SB 6'b101000
`define LH 6'b100001
`define SH 6'b101001
`define RTYPE 6'b000000
`define ADDI 6'b001000
`define JALR 6'b001001
`define J 6'b000010
之后每一个指令都用一个wire表示,注意用宏定义加`,以及R型指令是Rtype与funct的与。
wire addu,subu,ori,lw,sw,beq,jal,jr,lui,lb,sb,lh,sh,addi,jalr,j;
assign RType = (opcode == `RTYPE);
assign addu = RType&(funct == `ADDU);
assign subu = RType&(funct == `SUBU);
assign ori = (opcode == `ORI );
assign lw = (opcode == `LW);
assign sw = (opcode == `SW);
assign beq = (opcode == `BEQ);
assign jal = (opcode == `JAL);
assign jr = RType&(funct == `JR);
assign lui = (opcode == `LUI);
assign lb = (opcode == `LB);
assign sb = (opcode == `SB);
assign sh = (opcode == `SH);
assign lh = (opcode == `LH);
assign addi = (opcode == `ADDI);
assign jalr = RType&(funct == `JALR);
assign j = (opcode == `J);
之后对每个控制信号根据真值表加指令,两位的和一位的各举了一个例子。
assign NPCOp[0] = beq | jr | jalr;
assign NPCOp[1] = jal | jr | jalr | j;
assign RFWr = addu | subu | ori | lw | jal | lui | lb | lh | addi | jalr ;
datapath
这个和Controller作为mips的子模块,datapath用来把所有除了controller的模块连接起来,然后在mips里与controller连接。
结构图如下
加指令篇
跟P3一样分析即可。eg:加addi(不考虑溢出)
分析数据通路
判断是否需要增加新的通路以实现该指令,如ALU是否要增加计算功能之类的。addi不需要因此直接改控制信号即可。
确定控制信号
对于NPCOp,这不是一个跳转指令,因此NPCOp取00
对于RFWr,要回写到R[rt],因此RFWr为1
对于EXTOp,要进行符号扩展,所以取01
对于ALUOp,加法,所以取00
对于DMWr,不用写入DM,所以取0
对于WRSel,由于写入的是R[rt],所以取01
对于WDSel,由于写入的数据来自ALU的计算结果,所以取00
对于BSel,由于参与ALU计算的第二个数来自EXT,所以取1
对于SSel和LSel,由于不涉及半字或字节,都取00
添加指令信号
先定义ADDI
`define ADDI 6'b001000
再添加wire addi
assign addi = (opcode == `ADDI);
修改控制信号
在addi控制信号为1的地方加上addi。
如RFWr为1,则在 assign RFWr = addu | subu | ori | lw | jal | lui | lb | lh ;
最后或addi。
即变成 assign RFWr = addu | subu | ori | lw | jal | lui | lb | lh | addi;
其他控制信号依次添加即可,加完所有控制信号后,addi的添加完成。
使用Verilog搭建一个单周期CPU的更多相关文章
- Verilog hdl 实现单周期cpu
参考计组实验测试指令 - 简书,添加了一些细节. 1.添加 bne指令 修改 ctrl.v 之后修改mipstestloopjal_sim.asm,mars dump 为 bnetest. ...
- 使用logisim搭建单周期CPU与添加指令
使用logisim搭建单周期CPU与添加指令 搭建 总设计 借用高老板的图,我们只需要分别做出PC.NPC.IM.RF.EXT.ALU.DM.Controller模块即可,再按图连线,最后进行控制信号 ...
- Verilog单周期CPU(未完待续)
单周期CPU:指令周期=CPU周期 Top模块作为数据通路 运算器中有ALU,通路寄存器(R1.R2.R3.R4),数据缓冲寄存器(鉴于书上的运算器只有R0)........... 此为ALU和通用寄 ...
- 单周期CPU设计的理论基础
写在前面:本博客内容为本人老师原创,严禁任何形式的转载!本博客只允许放在博客园(.cnblogs.com),如果您在其他网站看到这篇博文,请通过下面这个唯一的合法链接转到原文! 本博客全网唯一合法UR ...
- 单周期cpu设计代码解读
目录 写在前面 单周期cpu设计代码讲解 概念回顾 Verilog代码讲解 写在前面 欢迎转载,转载请说明出处. 单周期cpu设计代码讲解 概念回顾 一.电子计算机的部件 分为:中央处理器(cpu). ...
- P4-verilog实现mips单周期CPU
最近对学习的掌控可能出现了问题,左支右绌,p2挂了,p2.p3.p4.p5每周在计组花的连续时间少了很多,学习到的东西也少了很多,流水线都还没真正开始写,和别人比落后了一大截,随笔自然就荒废了,我得尽 ...
- 单周期CPU设计
终于有点时间了,恰好多周期的设计也已经完成,其实只想写写多周期的,无奈单周期补上才好,哈哈哈~ —————+—————黄金分割线—————+————— 首先要理解什么叫单周期CPU(与后面多周期CPU ...
- 为什么现在使用多周期CPU,而单周期CPU被弃用?
最初设计的CPU结构简单,内部不复杂.之所以制造它是为了让机器自动跑程序,算数. 早期CPU都是单周期的,人们没考虑那么多,性能啥的.就让CPU每个时钟周期跑一个指令,这些时钟周期等长.这样下来,有的 ...
- 单周期CPU
一个时钟周期执行一条指令的过程理解(单周期CPU): https://blog.csdn.net/a201577F0546/article/details/84726912 单周期CPU指的是一条指令 ...
随机推荐
- GXOI2018 滚粗记
今天考了一次蜜汁省选,滚粗了.想了想,还是写点什么记录一下 8:10 折腾一番总算拿到题目和样例了,一打开dpf,立马感觉到了不对劲. 题目一股浓浓的劣质模拟题的画风,先不说题目质量,单是排版质量都被 ...
- [Luogu P1268] 树的重量 (巧妙的构造题)
题面 传送门:https://www.luogu.org/problemnew/show/P1268 Solution 这是一道极其巧妙的构造题 先做一个约定[i,j]表示从i到j的距离 我们可以先从 ...
- [Luogu P1006]传纸条 (网格DP)
题面 传送门:https://www.luogu.org/problemnew/show/P1006 Solution 挺显然但需要一定理解的网络(应该是那么叫吧)DP 首先有一个显然但重要的结论要发 ...
- active cab inf文件编写
最近做了一个网页下载控件.主要就是实现ActiveX控件功能. 由于自己是第一次做,不熟悉其过程.中间走了很多弯路.现在把走过得路程记录部分,希望对其他人可以有点用. 首先制作一个你自己的DLL文件. ...
- 神州笔记本电脑【K670D】安装 Ubuntu18.04 系列操作
一.使用U盘安装 Ubuntu 前的处理如下: 进入BIOS将SSD设为启动首选项 -> F10保存退出 -> 用方向键高亮ubuntu启动项 -> 按e键进入编辑状态 -> ...
- 庐山真面目之三微服务架构Consul版本实现
庐山真面目之三微服务架构Consul版本实现 一.简介 在上一篇文章<庐山真面目之二微服务架构NGINX版本实现>中,我们已经探讨了如何搭建基于Nginx 网关的微服务 ...
- 剑指Offer-Python(1-5)
1.二维数组的查找 查找,其实就可以挨个进行比较就可以.又由于题目说明(每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序),因此如果利用类似于二分查找的方法,那么比较次数则会更少 ...
- ubutun 服务器中文设置
在连接到服务器时,我们看不到图形界面,按照常规的安装搜狗输入法行不通,查阅相关的资料找到一篇可以解决的 sudo apt-get update 如果出现 E: 无法下载 404 Not Found I ...
- Appium常用操作之「Toast提示信息获取」
坚持原创输出,点击蓝字关注我吧 作者:清菡 博客:Oschina.云+社区.知乎等各大平台都有. 目录 一.什么是 Toast 二.获取 Toast 提示信息的前提 1.针对这种元素,有的时候我们需要 ...
- Service Cloud零基础学习(二)Entitlement & MileStone
https://trailhead.salesforce.com/content/learn/modules/entitlements 很少会有人不买东西,买的多了也很少没有人没有经历过售后流程.我们 ...