文章目录
1. 实验内容
-
本实例所设计CPU的指令格式的拟定;
-
基本功能部件的设计与实现;
-
CPU各主要功能部件的设计与实现;
-
CPU的封装;
-
仿真测试及板级测试。对各个单元组合而成的CPU进行指令测试,配合使用模拟仿真,了解指令和数据在各个单元中的传输过程及方向。
2. 实验要求
-
设计的CPU能够执行20条整数指令,每条指令的编码长度均为32位;
-
指令类型应有:计算类型、访问存储器类型、条件转移类型和无条件转移类型;
-
操作数应有:寄存器操作数、立即数;
-
测试进程应不少于10条,将设计成果仿真测试并起先板级验证。
3. 实验原理
3.1 概述
单周期(Single Cycle)CPU是指CPU从取出1条指令到执行完该指令只需1个时钟周期。
一条指令的执行过程包括:取指令→分析指令→执行指令→保存结果(如果有的话)。对于单周期CPU来说,这些执行步骤均在一个时钟周期内完成。
3.2 单周期CPU的总体电路
本实验所设计的单周期CPU的总体电路结构如图所示:本实验所设计的CPU主要组成部分有:运算器(ALU)、控制器(CONUNIT)、寄存器堆(REGFILE)、取指电路及相关基础部件(如选择器)等构成。
3.3 MIPS指令格式
MIPS指令系统结构有MIPS-32和MIPS-64两种。本实验的MIPS指令选用MIPS-32。以下所说的MIPS指令均指MIPS-32。
MIPS的指令格式为32位。下图给出了MIPS指令的3种格式。
本实验只选取了20条典型的MIPS指令来描述CPU逻辑电路的设计方法。下表列出了本实验的所涉及到的20条MIPS指令。
R型指令的Op均为0,具体操作由func指定。rs和rt是源寄存器号,rd是目的寄存器号。移位指令中使用sa指定移位位数。
I型指令的低16位是立即数,计算时需扩展到32位,依指令的不同需进行零扩展和符号扩展。
J型指令的低26位是地址,是用于产生跳转的目标地址。
3.4 模块分析
根据实验原理中的单周期CPU总体结构图,我们可以清楚的知道单周期CPU的设计应包括PC,INSTMEM,CONUNIT,REGFILE,ALU,DATAMEM, EXT16T32这几个核心模块,其中ALU模块需要用到32位加法器CLA_32。此外还需要一个固定左移两位的移位器SHIFT,左移处理模块SHIFTER_COMBINATION,一个PC+4的固定加法器,一个四选一多路选择器MUX4X32,两个32位二选一多路选择器MUX2X32,一个5位二选一多路选择器MUX2X5,一个数据扩展器EXT16T32。其中为了运行整个CPU还需要加入一个顶层模块CPU来调用这些模块,所以自然地,这些模块为顶层模块的子模块。
3.5 下一条指令地址的选择
CPU的电路包括数据路径(Data
path)和控制部件(CONUNIT)两大部分。下面介绍路径的设计。
下一条指令的地址有4种情况:
-
程序不转移时下一条指令的地址为PC+4;
-
执行beq和bne指令发生转移时,下一条指令的地址是PC加4,再加上符号扩展的偏移量左移2位的和;
-
执行jr指令时转移的目标地址就是rs寄存器中的内容;
-
执行j和jal指令时转移的目标地址是指令中的低26位地址左移2位,再与PC+4的高4位拼接在一起。
下一条指令地址的产生和选择电路如图所示:
在图中,控制器(CONUNIT)根据op、func和Z(对于beq和bne指令)信号产生相应的转移控制选择信号Pcsrc。
4. 基本功能部件的设计与实现
一个CPU主要由ALU(运算器)、控制器、寄存器堆、取指部件及其它基本功能部件等构成。
在本实验中基本功能部件主要有:32位2选1多路选择器、32位4选1多路选择器、5位2选1多路选择器、D触发器、移位器及32位加/减法器等。
4.1 32位2选1选择器的设计与实现
多路选择器是逻辑电路设计中最重要的基本逻辑电路之一,也是基于数据选择通路CPU的重要部件。32位2选1选择器的逻辑框图如图所示。
输入模块代码:
module MUX2X32(A0,A1,S,Y);
input [31:0]A0,A1;
input S;
output [31:0]Y;
function [31:0]select;
input [31:0]A0,A1;
input S;
case(S)
1'b0:select=A0;
1'b1:select=A1;
endcase
endfunction
assign Y = select(A0,A1,S);
endmodule
通过RTL Schematic查看综合后的RTL层电路,如图所示:
4.2 32位4选1选择器的设计与实现
32位4选1选择器的逻辑框图如图所示:
32位4选1选择器的模块设计步骤可参考32位2选1选择器的设计步骤,下面给出32位4选1选择器的模块代码。
module MUX4X32(A0, A1, A2, A3, S, Y);
input [31:0] A0, A1, A2, A3;
input [1:0] S;
output [31:0] Y;
function [31:0] select;
input [31:0] A0, A1, A2, A3;
input [1:0] S;
case(S)
2'b00: select = A0;
2'b01: select = A1;
2'b10: select = A2;
2'b11: select = A3;
endcase
endfunction
assign Y = select (A0, A1, A2, A3, S);
endmodule
4选1选择器的PTL仿真图为:
4.3 5位2选1选择器的设计与实现
5位2选1选择器的逻辑框图如图4-19所示。
5位2选1选择器的模块设计步骤可参考32位2选1选择器的设计步骤,下面给出5位2选1选择器的模块代码。
module MUX2X5(A0,A1,S,Y);
input [4:0]A0,A1;
input S;
output [4:0] Y;
function [4:0] select;
input [4:0] A0,A1;
input S;
case(S)
1'b0:select=A0;
1'b1:select=A1;
endcase
endfunction
assign Y=select(A0,A1,S);
endmodule
RTL仿真图为:
4.4 16位-32位扩展器的设计与实现
采用一个16位扩展成32位的扩展模块EXT16T32,实现零扩展和符号扩展。所以还需要一个32位2选1选择器。
module EXT16T32(X,Se,Y);
input [15:0]X;
input Se;
output [31:0]Y;
wire [31:0]E0,E1;
wire [15:0]e={16{X[15]}};
parameter z=16'b0;
assign E0={z,X};
assign E1={e,X};
MUX2X32 i(E0,E1,Se,Y);
endmodule
RTL仿真图为:
4.5 32位移位器的设计与实现
移位器的功能是将一个数进行逻辑左移、逻辑右移或算术右移。其设计步骤可参考32位2选1选择器的设计步骤,下面给出其模块Verilog实现代码:
module SHIFTER(X,Sa,Arith,Right,Sh);
input [31:0]X;
input [4:0]Sa;
input Arith,Right;
output [31:0]Sh;
wire [31:0]T4,T3,T2,T1,T0,S4,S3,S2,S1;
wire a=X[31]&Arith;
wire [15:0]e={16{a}};
parameter z=16'b0000000000000000;
wire [31:0]L1u,L1d,L2u,L2d,L3u,L3d,L4u,L4d,L5u,L5d;
assign L1u={X[15:0],z[15:0]};
assign L1d={e,X[31:16]};
MUX2X32 M1l(L1u,L1d,Right,T4);
MUX2X32 M1r(X,T4,Sa[4],S4);
//1
assign L2u={S4[23:0],z[7:0]};
assign L2d={e[7:0],S4[31:8]};
MUX2X32 M2l(L2u,L2d,Right,T3);
MUX2X32 M2r(S4,T3,Sa[3],S3);
//2
assign L3u={S3[27:0],z[3:0]};
assign L3d={e[3:0],S3[31:4]};
MUX2X32 M3l(L3u,L3d,Right,T2);
MUX2X32 M3r(S3,T2,Sa[2],S2);
//3
assign L4u={S2[29:0],z[1:0]};
assign L4d={e[1:0],S2[31:2]};
MUX2X32 M4l(L4u,L4d,Right,T1);
MUX2X32 M4r(S2,T1,Sa[1],S1);
//4
assign L5u={S1[30:0],z[0]};
assign L5d={e[0],S1[31:1]};
MUX2X32 M5l(L5u,L5d,Right,T0);
MUX2X32 M5r(S1,T0,Sa[0],Sh);
endmodule
RTL仿真图为:
4.6 26位-32位移位器的设计与实现
跳转的目标地址采用拼接的方式形成,最高4位为PC+4的最高4位,中间26位为J型指令的26位立即数字段,最低两位为0。Verilog代码实现为:
module SHIFTER_COMBINATION(X,PCADD4,Sh);
input [25:0] X;
input [31:0] PCADD4;
output [31:0] Sh;
parameter z=2'b00;
assign Sh={PCADD4[31:28],X[25:0],z};
endmodule
RTL仿真图为:
4.7 32位加/减法器的设计与实现
32位加/减法器的功能是完成32位的加法/减法运算。由于:
故可以用加法器实现减法操作。下面给出其模块Verilog实现代码:
module ADDSUB_32(X,Y,Sub,S);
input [31:0]X,Y;
wire Cout;
input Sub;
output [31:0]S;
CLA_32 adder0(X,Y^{32{Sub}},Sub,S,Cout);
endmodule
RTL仿真图为:
下面给出其模块CLA_32 Verilog实现代码:
module CLA_32(X,Y,Cin,S,Cout);
input[31:0]X,Y;
input Cin;
output[31:0]S;
output Cout;
wire Cout0,Cout1,Cout2,Cout3,Cout4,Cout5,Cout6;
CLA_4 add0(X[3:0],Y[3:0],Cin,S[3:0],Cout0);
CLA_4 add1(X[7:4],Y[7:4],Cout0,S[7:4],Cout1);
CLA_4 add2(X[11:8],Y[11:8],Cout1,S[11:8],Cout2);
CLA_4 add3(X[15:12],Y[15:12],Cout2,S[15:12],Cout3);
CLA_4 add4(X[19:16],Y[19:16],Cout3,S[19:16],Cout4);
CLA_4 add5(X[23:20],Y[23:20],Cout4,S[23:20],Cout5);
CLA_4 add6(X[27:24],Y[27:24],Cout5,S[27:24],Cout6);
CLA_4 add7(X[31:28],Y[31:28],Cout6,S[31:28],Cout);
endmodule
CLA_4 Verilog实现代码:
module CLA_4(X,Y,Cin,S,Cout);
input [3:0]X,Y;
output Cout;
input Cin;
output [3:0]S;
and i0(Y_3,X[3],Y[3]);
or i1(X_3,X[3],Y[3]);
and i2(Y_2,X[2],Y[2]);
or i3(X_2,X[2],Y[2]);
and i4(Y_1,X[1],Y[1]);
or i5(X_1,X[1],Y[1]);
and i6(Y_0,X[0],Y[0]);
or i7(X_0,X[0],Y[0]);
not i01(Y_31,Y_3);
nand i02(Y_32,X_3,Y_2);
nand i03(Y_33,X_3,X_2,Y_1);
nand i04(Y_34,X_3,X_2,X_1,Y_0);
nand i05(Y_35,X_3,X_2,X_1,X_0,Cin);
nand i00(Cout,Y_31,Y_32,Y_33,Y_34,Y_35);//Cout的输出门级电路实现
not i_2(Y__3,Y_3);
and i21(Y_21,Y__3,X_3);
not i22(Y_22,Y_2);
nand i23(Y_23,X_2,Y_1);
nand i24(Y_24,X_2,X_1,Y_0);
nand i25(Y_25,X_2,X_1,X_0,Cin);
nand i26(Y_26,Y_22,Y_23,Y_24,Y_25);
xor i20(S[3],Y_21,Y_26);//S3的输出门级电路实现
not i_1(Y__2,Y_2);
and i11(Y_11,Y__2,X_2);
not i12(Y_12,Y_1);
nand i13(Y_13,X_1,Y_0);
nand i14(Y_14,X_1,X_0,Cin);
nand i15(Y_15,Y_12,Y_13,Y_14);
xor i10(S[2],Y_11,Y_15);//S2的输出门级电路实现
not i_0(Y__1,Y_1);
and i51(Y_51,Y__1,X_1);
not i52(Y_52,Y_0);
nand i53(Y_53,X_0,Cin);
nand i54(Y_54,Y_52,Y_53);
xor i50(S[1],Y_51,Y_54);//S1的输出门级电路
not i41(Y__0,Y_0);
and i42(Y_4,Y__0,X_0);
xor i40(S[0],Y_4,Cin);//S0的输出门级电路
endmodule
5. 运算器(ALU)的设计与实现
运算器ALU是CPU组成的核心部件之一,其实现方法主要有2种:一种是以加法器为核心,通过改变进位函数实现;另一种是运算部件并行多路选择实现。本实验采用运算部件并行多路选择实现。综合表3-1的指令,ALU只需完成9种运算即可,具体完成的功能如表所示。
ALU的逻辑框图如图所示。在图中各信号的功能如下:
X:操作数,32位,输入;
Y:操作数,32位,输入;
Aluc:4位操作码,输入;
R:运算结果,32位,输出;
Z:零标志,1位;当运算结果为0时,该位为1,否则为0;
ALU模块的Verilog程序参考代码:
module ALU(X,Y,Aluc,R,Z);
input[31:0]X,Y;
input[3:0]Aluc;
output[31:0]R;
output Z;
wire[31:0]d_as,d_and,d_or,d_xor,d_lui,d_sh,d;
ADDSUB_32 as32(X,Y,Aluc[0],d_as);
assign d_and=X&Y;
assign d_or=X|Y;
assign d_xor=X^Y;
assign d_lui={Y[15:0],16'h0};
SHIFTER shift(Y,X[10:6],Aluc[3],Aluc[1],d_sh);
MUX6X32 select(d_and,d_or,d_xor,d_lui,d_sh,d_as,Aluc[3:0],R);
assign Z=~|R;
endmodule
32位6选1选择器的代码如下:
module MUX6X32(d_and,d_or,d_xor,d_lui,d_sh,d_as,Aluc,d);
input [31:0]d_and,d_or,d_xor,d_lui,d_sh,d_as;
input [3:0]Aluc;
output [31:0]d;
function [31:0]select;
input [31:0]d_and,d_or,d_xor,d_lui,d_sh,d_as;
input [3:0]Aluc;
case(Aluc)
4'b0000:select=d_as;
4'b0001:select=d_as;
4'b0010:select=d_and;
4'b0011:select=d_or;
4'b0100:select=d_xor;
4'b0110:select=d_lui;
4'b0101:select=d_sh;
4'b0111:select=d_sh;
4'b1111:select=d_sh;
4'b1101:select=d_sh;
endcase
endfunction
assign d=select(d_and,d_or,d_xor,d_lui,d_sh,d_as,Aluc);
endmodule
RTL仿真图为:如图所示,在ALU的实现中,运用了32位加/减法器,32位移位器,32位6选1选择器,可以实现所有的功能。
6. 寄存器堆(REGFILE)的设计与实现
寄存器堆(REGFILE)是CPU组成的重要存储部件,也是数据通路中的重要部件,其主要功能是对数据进行存储。在本实验中将为REGFILE构建32×32的寄存器组,即共有32个寄存器,每个寄存器的位宽都是32位。32×32的REGFILE逻辑结构如图所示:
图中的寄存器组有1个数据输入端口,2个数据输出端口,因此可以从该寄存器组中同时输出2个数据。由于没有设置读信号,故在本实验中当Ra(或Rb)为0时,并不是选择输出Register的内容,而是直接输出0。图6-1中各信号引脚的功能:
Ra:寄存器rs输出端口地址,5位,输入;
Rb:寄存器rt输出端口地址,5位,输入;
Wr:数据写入的寄存器号,5位,输入;
We:写信号,1位,输入;
D:数据,32位,输入;
Qa:A端口的输出数据,32位,输出;
Qb:B端口的输出数据,32位,输出;
Clk:时钟信号,输入。
实现REGFILE的Verilog程序参考代码:
module REGFILE(Ra,Rb,D,Wr,We,Clk,Clrn,Qa,Qb);
input [4:0]Ra,Rb,Wr;
input [31:0]D;
input We,Clk,Clrn;
output [31:0]Qa,Qb;
wire [31:0]Y;
wire [31:0]Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0;
DEC5T32E dec(Wr,We,Y);
REG32 reg32(D,Y,Clk,Clrn,Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0);
MUX32X32 select1(Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0,Ra,Qa);
MUX32X32 select2(Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0,Rb,Qb);
endmodule
5-32译码器的代码实现为:
module DEC5T32E(I,En,Y);
input [4:0] I;
input En;
output [31:0] Y;
reg [31:0] Y;
always@(En or I)
begin
if(En)
begin
case(I)
5'b00000:Y=32'b00000000000000000000000000000001;
5'b00001:Y=32'b00000000000000000000000000000010;
5'b00010:Y=32'b00000000000000000000000000000100;
5'b00011:Y=32'b00000000000000000000000000001000;
5'b00100:Y=32'b00000000000000000000000000010000;
5'b00101:Y=32'b00000000000000000000000000100000;
5'b00110:Y=32'b00000000000000000000000001000000;
5'b00111:Y=32'b00000000000000000000000010000000;
5'b01000:Y=32'b00000000000000000000000100000000;
5'b01001:Y=32'b00000000000000000000001000000000;
5'b01010:Y=32'b00000000000000000000010000000000;
5'b01011:Y=32'b00000000000000000000100000000000;
5'b01100:Y=32'b00000000000000000001000000000000;
5'b01101:Y=32'b00000000000000000010000000000000;
5'b01110:Y=32'b00000000000000000100000000000000;
5'b01111:Y=32'b00000000000000001000000000000000;
5'b10000:Y=32'b00000000000000010000000000000000;
5'b10001:Y=32'b00000000000000100000000000000000;
5'b10010:Y=32'b00000000000001000000000000000000;
5'b10011:Y=32'b00000000000010000000000000000000;
5'b10100:Y=32'b00000000000100000000000000000000;
5'b10101:Y=32'b00000000001000000000000000000000;
5'b10110:Y=32'b00000000010000000000000000000000;
5'b10111:Y=32'b00000000100000000000000000000000;
5'b11000:Y=32'b00000001000000000000000000000000;
5'b11001:Y=32'b00000010000000000000000000000000;
5'b11010:Y=32'b00000100000000000000000000000000;
5'b11011:Y=32'b00001000000000000000000000000000;
5'b11100:Y=32'b00010000000000000000000000000000;
5'b11101:Y=32'b00100000000000000000000000000000;
5'b11110:Y=32'b01000000000000000000000000000000;
5'b11111:Y=32'b10000000000000000000000000000000;
endcase
end
else
Y=32'b00000000000000000000000000000000;
end
endmodule
32位32选1选择器代码实现为:
module MUX32X32(Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0,S,Y);
input [31:0]Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0;
input [4:0]S;
output [31:0]Y;
function [31:0]select;
input [31:0]Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0;
input [4:0]S;
case(S)
5'b00000:select=Q0;
5'b00001:select=Q1;
5'b00010:select=Q2;
5'b00011:select=Q3;
5'b00100:select=Q4;
5'b00101:select=Q5;
5'b00110:select=Q6;
5'b00111:select=Q7;
5'b01000:select=Q8;
5'b01001:select=Q9;
5'b01010:select=Q10;
5'b01011:select=Q11;
5'b01100:select=Q12;
5'b01101:select=Q13;
5'b01110:select=Q14;
5'b01111:select=Q15;
5'b10000:select=Q16;
5'b10001:select=Q17;
5'b10010:select=Q18;
5'b10011:select=Q19;
5'b10100:select=Q20;
5'b10101:select=Q21;
5'b10110:select=Q22;
5'b10111:select=Q23;
5'b11000:select=Q24;
5'b11001:select=Q25;
5'b11010:select=Q26;
5'b11011:select=Q27;
5'b11100:select=Q28;
5'b11101:select=Q29;
5'b11110:select=Q30;
5'b11111:select=Q31;
endcase
endfunction
assign Y = select(Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0,S);
endmodule
32个寄存器的代码实现如下:
module REG32(D,En,Clk,Clrn,Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0);
input[31:0]D,En;
input Clk,Clrn;
output[31:0]Q31,Q30,Q29,Q28,Q27,Q26,Q25,Q24,Q23,Q22,Q21,Q20,Q19,Q18,Q17,Q16,Q15,Q14,Q13,Q12,Q11,Q10,Q9,Q8,Q7,Q6,Q5,Q4,Q3,Q2,Q1,Q0;
wire [31:0]Qn31,Qn30,Qn29,Qn28,Qn27,Qn26,Qn25,Qn24,Qn23,Qn22,Qn21,Qn20,Qn19,Qn18,Qn17,Qn16,Qn15,Qn14,Qn13,Qn12,Qn11,Qn10,Qn9,Qn8,Qn7,Qn6,Qn5,Qn4,Qn3,Qn2,Qn1,Qn0;
D_FFEC32 q31(D,Clk,En[31],Clrn,Q31,Qn31);
D_FFEC32 q30(D,Clk,En[30],Clrn,Q30,Qn30);
D_FFEC32 q29(D,Clk,En[29],Clrn,Q29,Qn29);
D_FFEC32 q28(D,Clk,En[28],Clrn,Q28,Qn28);
D_FFEC32 q27(D,Clk,En[27],Clrn,Q27,Qn27);
D_FFEC32 q26(D,Clk,En[26],Clrn,Q26,Qn26);
D_FFEC32 q25(D,Clk,En[25],Clrn,Q25,Qn25);
D_FFEC32 q24(D,Clk,En[24],Clrn,Q24,Qn24);
D_FFEC32 q23(D,Clk,En[23],Clrn,Q23,Qn23);
D_FFEC32 q22(D,Clk,En[22],Clrn,Q22,Qn22);
D_FFEC32 q21(D,Clk,En[21],Clrn,Q21,Qn21);
D_FFEC32 q20(D,Clk,En[20],Clrn,Q20,Qn20);
D_FFEC32 q19(D,Clk,En[19],Clrn,Q19,Qn19);
D_FFEC32 q18(D,Clk,En[18],Clrn,Q18,Qn18);
D_FFEC32 q17(D,Clk,En[17],Clrn,Q17,Qn17);
D_FFEC32 q16(D,Clk,En[16],Clrn,Q16,Qn16);
D_FFEC32 q15(D,Clk,En[15],Clrn,Q15,Qn15);
D_FFEC32 q14(D,Clk,En[14],Clrn,Q14,Qn14);
D_FFEC32 q13(D,Clk,En[13],Clrn,Q13,Qn13);
D_FFEC32 q12(D,Clk,En[12],Clrn,Q12,Qn12);
D_FFEC32 q11(D,Clk,En[11],Clrn,Q11,Qn11);
D_FFEC32 q10(D,Clk,En[10],Clrn,Q10,Qn10);
D_FFEC32 q9(D,Clk,En[9],Clrn,Q9,Qn9);
D_FFEC32 q8(D,Clk,En[8],Clrn,Q8,Qn8);
D_FFEC32 q7(D,Clk,En[7],Clrn,Q7,Qn7);
D_FFEC32 q6(D,Clk,En[6],Clrn,Q6,Qn6);
D_FFEC32 q5(D,Clk,En[5],Clrn,Q5,Qn5);
D_FFEC32 q4(D,Clk,En[4],Clrn,Q4,Qn4);
D_FFEC32 q3(D,Clk,En[3],Clrn,Q3,Qn3);
D_FFEC32 q2(D,Clk,En[2],Clrn,Q2,Qn2);
D_FFEC32 q1(D,Clk,En[1],Clrn,Q1,Qn1);
assign Q0=0;
assign Qn0=0;
endmodule
带有异步清零的32位D触发器的代码实现:
module D_FFEC32(D,Clk,En,Clrn,Q,Qn);
input[31:0]D;
input Clk,En,Clrn;
output[31:0]Q,Qn;
D_FFEC d0(D[0],Clk,En,Clrn,Q[0],Qn[0]);
D_FFEC d1(D[1],Clk,En,Clrn,Q[1],Qn[1]);
D_FFEC d2(D[2],Clk,En,Clrn,Q[2],Qn[2]);
D_FFEC d3(D[3],Clk,En,Clrn,Q[3],Qn[3]);
D_FFEC d4(D[4],Clk,En,Clrn,Q[4],Qn[4]);
D_FFEC d5(D[5],Clk,En,Clrn,Q[5],Qn[5]);
D_FFEC d6(D[6],Clk,En,Clrn,Q[6],Qn[6]);
D_FFEC d7(D[7],Clk,En,Clrn,Q[7],Qn[7]);
D_FFEC d8(D[8],Clk,En,Clrn,Q[8],Qn[8]);
D_FFEC d9(D[9],Clk,En,Clrn,Q[9],Qn[9]);
D_FFEC d10(D[10],Clk,En,Clrn,Q[10],Qn[10]);
D_FFEC d11(D[11],Clk,En,Clrn,Q[11],Qn[11]);
D_FFEC d12(D[12],Clk,En,Clrn,Q[12],Qn[12]);
D_FFEC d13(D[13],Clk,En,Clrn,Q[13],Qn[13]);
D_FFEC d14(D[14],Clk,En,Clrn,Q[14],Qn[14]);
D_FFEC d15(D[15],Clk,En,Clrn,Q[15],Qn[15]);
D_FFEC d16(D[16],Clk,En,Clrn,Q[16],Qn[16]);
D_FFEC d17(D[17],Clk,En,Clrn,Q[17],Qn[17]);
D_FFEC d18(D[18],Clk,En,Clrn,Q[18],Qn[18]);
D_FFEC d19(D[19],Clk,En,Clrn,Q[19],Qn[19]);
D_FFEC d20(D[20],Clk,En,Clrn,Q[20],Qn[20]);
D_FFEC d21(D[21],Clk,En,Clrn,Q[21],Qn[21]);
D_FFEC d22(D[22],Clk,En,Clrn,Q[22],Qn[22]);
D_FFEC d23(D[23],Clk,En,Clrn,Q[23],Qn[23]);
D_FFEC d24(D[24],Clk,En,Clrn,Q[24],Qn[24]);
D_FFEC d25(D[25],Clk,En,Clrn,Q[25],Qn[25]);
D_FFEC d26(D[26],Clk,En,Clrn,Q[26],Qn[26]);
D_FFEC d27(D[27],Clk,En,Clrn,Q[27],Qn[27]);
D_FFEC d28(D[28],Clk,En,Clrn,Q[28],Qn[28]);
D_FFEC d29(D[29],Clk,En,Clrn,Q[29],Qn[29]);
D_FFEC d30(D[30],Clk,En,Clrn,Q[30],Qn[30]);
D_FFEC d31(D[31],Clk,En,Clrn,Q[31],Qn[31]);
endmodule
带有使能端D触发器的代码实现:
module D_FFEC(D,Clk,En,Clrn,Q,Qn);
input D,Clk,En,Clrn;
output Q,Qn;
wire Y0,Y_C;
MUX2X1 m0(Q,D,En,Y0);
and i0(Y_C,Y0,Clrn);
D_FF d0(Y_C,Clk,Q,Qn);
endmodule
2选1选择器的代码实现;
module MUX2X1(A0,A1,S,Y);
input A0,A1,S;
output Y;
not i0(S_n,S);
nand i1(A0_S,A0,S_n);
nand i2(A1_S,A1,S);
nand i3(Y,A0_S,A1_S);
endmodule
D锁存器的代码实现:
module D_FF(D,Clk,Q,Qn);
input D,Clk;
output Q,Qn;
wire Clkn,Q0,Qn0;
not i0(Clkn,Clk);
D_Latch d0(D,Clkn,Q0,Qn0);
D_Latch d1(Q0,Clk,Q,Qn);
endmodule
D触发器的代码实现如下:
module D_Latch(D,En,Q,Qn);
input D,En;
output Q,Qn;
wire Sn,Rn,Dn;
not i0(Dn,D);
nand i1(Sn,D,En);
nand i2(Rn,En,Dn);
nand i3(Q,Sn,Qn);
nand i4(Qn,Q,Rn);
endmodule
7. 控制器(CONUNIT)的设计与实现
实现CONUNIT的Verilog程序参考代码:
module CONUNIT(
input [5:0]Op,
input [5:0]Func,
input Z,
output Regrt,
output Se,
output Wreg,
output Aluqb,
output [3:0]Aluc,
output Wmem,
output [1:0]Pcsrc,
output Reg2reg,
output shift,
output j
);
wire i_add = (Op == 6'b000000 & Func == 6'b100000)?1:0;
wire i_sub = (Op == 6'b000000 & Func == 6'b100010)?1:0;
wire i_and = (Op == 6'b000000 & Func == 6'b100100)?1:0;
wire i_or = (Op == 6'b000000 & Func == 6'b100101)?1:0;
wire i_xor = (Op == 6'b000000 & Func == 6'b100110)?1:0;
wire i_sll = (Op == 6'b000000 & Func == 6'b000000)?1:0;
wire i_srl = (Op == 6'b000000 & Func == 6'b000010)?1:0;
wire i_sra = (Op == 6'b000000 & Func == 6'b000011)?1:0;
wire i_jr = (Op == 6'b000000 & Func == 6'b001000)?1:0;
//R
wire i_addi = (Op == 6'b001000)?1:0;
wire i_andi = (Op == 6'b001100)?1:0;
wire i_ori = (Op == 6'b001101)?1:0;
wire i_xori = (Op == 6'b001110)?1:0;
wire i_lw = (Op == 6'b100011)?1:0;
wire i_sw = (Op == 6'b101011)?1:0;
wire i_beq = (Op == 6'b000100)?1:0;
wire i_bne = (Op == 6'b000101)?1:0;
wire i_lui = (Op == 6'b001111)?1:0;
//I
wire i_j = (Op == 6'b000010)?1:0;
wire i_jal = (Op == 6'b000011)?1:0;
assign Wreg = i_add|i_sub|i_and|i_or|i_xor|i_sll|i_srl|i_sra|i_addi|i_andi|i_ori|i_or|i_xori|i_lw|i_lui|i_jal;
assign Regrt = i_addi|i_andi|i_ori|i_xori|i_lw|i_sw|i_lui|i_beq|i_bne|i_j|i_jal;
assign Reg2reg = i_add|i_sub|i_and|i_or|i_xor|i_sll|i_srl|i_sra|i_addi|i_andi|i_ori|i_xori|i_sw|i_beq|i_bne|i_j|i_jal;
assign Aluqb = i_add | i_sub | i_and | i_or | i_xor | i_sll | i_srl | i_sra | i_beq | i_bne |i_j;
assign Se = i_addi | i_lw | i_sw | i_beq | i_bne;
assign Aluc[3] = i_sra;
assign Aluc[2] = i_xor |i_lui | i_sll | i_srl | i_sra |i_xori;
assign Aluc[1] = i_and | i_or | i_lui | i_srl | i_sra | i_andi | i_ori;
assign Aluc[0] = i_sub | i_ori | i_or | i_sll | i_srl |i_sra| i_beq | i_bne;
assign Wmem = i_sw;
assign Pcsrc[0] = (i_beq&Z) | (i_bne&~Z) | i_jal | i_j;
assign Pcsrc[1] = i_j | i_jr | i_jal;
assign shift=i_sll | i_srl | i_sra;
assign j=i_jal | i_jr;
endmodule
在控制器的Verilog实现代码中,首先对指令的功能进行译码,以“i_”开头的变量表示批令的功能,例如i_add为1表示该指令的功能是加法(add),其余的以此类指。然后根据指令的功能产生相应的控制信号。
8.指令存储器的设计与实现
由于我们的主要目的是对所设计的CPU进行测试,故可以不使用IP核设计指令存储器,直接使用通用代码实现。指令存储器的设计实现了20条指令的设计,可以较好地验证单周期CPU具有的功能。实现INSTMEM的Verilog程序参考代码:
module INSTMEM(Addr,Inst);//指令存储器
input[31:0]Addr;
//状态为'0',写指令寄存器,否则为读指令寄存器
output[31:0]Inst;
wire[31:0]Rom[31:0];
assign Rom[5'h00]=32'h20010008;//addi $1,$0,8 $1=8
assign Rom[5'h01]=32'h3402000C;//ori $2,$0,12 $2=12
assign Rom[5'h02]=32'h00221820;//add $3,$1,$2 $3=20
assign Rom[5'h03]=32'h00412022;//sub $4,$2,$1 $4=4
assign Rom[5'h04]=32'h00222824;//and $5,$1,$2 $5=8
assign Rom[5'h05]=32'h00223025;//or $6,$1,$2 $6=12
assign Rom[5'h06]=32'h14220002;//bne $1,$2,2
assign Rom[5'h07]=32'hXXXXXXXX;
assign Rom[5'h08]=32'hXXXXXXXX;
assign Rom[5'h09]=32'h10220002;// beq $1,$2,2
assign Rom[5'h0A]=32'h0800000D;// J 0D
assign Rom[5'h0B]=32'hXXXXXXXX;
assign Rom[5'h0C]=32'hXXXXXXXX;
assign Rom[5'h0D]=32'hAD02000A;// sw $2 10($8) memory[$8+10]=10
assign Rom[5'h0E]=32'h8D04000A;//lw $4 10($8) $4=12
assign Rom[5'h0F]=32'h00221826;//xor $3,$1,$2
assign Rom[5'h10]=32'h00021900;//sll $3,$2,4
assign Rom[5'h11]=32'h00021902;//srl $3,$2,4
assign Rom[5'h12]=32'h00021903;//sra $3,$2,4
assign Rom[5'h13]=32'h30470009;//andi $7,$2,9
assign Rom[5'h14]=32'h382300EF;//xori $3,$1,0xef
assign Rom[5'h15]=32'h3C011234;//lui $1,0x1234
assign Rom[5'h16]=32'h0C00001A;//Jal 1A
assign Rom[5'h17]=32'h0800001A;// J 1A
assign Rom[5'h18]=32'hXXXXXXXX;
assign Rom[5'h19]=32'hXXXXXXXX;
assign Rom[5'h1A]=32'h03E00008;//Jr 16
assign Rom[5'h1B]=32'hXXXXXXXX;
assign Rom[5'h1C]=32'hXXXXXXXX;
assign Rom[5'h1D]=32'hXXXXXXXX;
assign Rom[5'h1E]=32'hXXXXXXXX;
assign Rom[5'h1F]=32'hXXXXXXXX;
assign Inst=Rom[Addr[6:2]];
endmodule
9.数据存储器的设计与实现
由于我们的主要目的是对所设计的CPU进行测试,故可以不使用IP核设计数据存储器,直接使用通用代码实现。由于需要支持取数/存数指令,所以要在指令储存器的基础上增加写入数据的数据写入端口,写使能信号。又因为写操作在时钟信号的上升沿,所以要增加时钟信号。
当We为1时,进行sw指令操作,此时Din端口输入信号实际为rt,Addr端口输入信号为rs和偏移量相加的地址,在时钟周期上升沿将rt的值写入改地址的储存单元。
当We为0时,进行lw指令操作,此时Addr端口输入信号为rs和偏移量相加的地址,Dout为读取该地址储存器的内容。
实现DATAMEM的verilog程序代码如下:
module DATAMEM(Addr,Din,Clk,We,Dout);
input [31:0]Addr,Din;
input Clk,We;
output [31:0]Dout;
reg [31:0]ram[0:31];
integer i;
initial begin
for ( i = 0 ; i <= 31 ; i = i + 1)
ram [i] = i;
end
always @ (posedge Clk) begin
if (We) ram[Addr[6:2]] <= Din;
end
assign Dout = ram[Addr[6:2]];
endmodule
10. PC寄存器的设计与实现
PC寄存器用于给出指令在指令储存器中的地址。为实现稳定输出,在时钟信号的上升沿更新,而且需要一个控制信号,在控制信号为0的时候初始化PC寄存器。主要的代码实现为:
module PC(Clk,Reset,Result,Address);
input Clk;//时钟
input Reset;//是否重置地址。0-初始化PC,否则接受新地址
input[31:0] Result;
output reg[31:0] Address;
initial begin
Address <= 0;
end
always @(posedge Clk or negedge Reset)
begin
if (Reset==0) //如果为0则初始化PC,否则接受新地址
begin
Address <= 0;
end
else
begin
Address = Result;
end
end
endmodule
11. CPU的封装
当CPU的所有功能部件分别设计完成后,就要将其进行封装,以构成一个完整的CPU。本实验封装后CPU如图所示。
Clk:时钟周期,外部输入信号。
Reset:清零信号,外部输入信号。
CPU封装的基本原理就是根据单周期CPU的总体电路图,将各功能部件连接起来。CPU封装的Verilog代码如下:
module CPU(Clk,Reset,Addr,Inst,Qa,Qb,ALU_R,NEXTADDR,D);
input Clk,Reset;
output [31:0] Inst,NEXTADDR,ALU_R,Qb,Qa,Addr,D;
wire [31:0]Result,PCadd4,EXTIMM,InstL2,EXTIMML2,D1,X,Y,Dout,mux4x32_2,R;
wire Z,Regrt,Se,Wreg,Aluqb,Reg2reg,Cout,Wmem,shift,j;
wire [3:0]Aluc;
wire [1:0]Pcsrc;
wire [4:0]Wr,Wr1;
PC pc(Clk,Reset,Result,Addr);
PCadd4 pcadd4(Addr,PCadd4);
INSTMEM instmem(Addr,Inst);
CONUNIT conunit(Inst[31:26],Inst[5:0],Z,Regrt,Se,Wreg,Aluqb,Aluc,Wmem,Pcsrc,Reg2reg,shift,j);
MUX2X5 mux2x5_1(Inst[15:11],Inst[20:16],Regrt,Wr1);
MUX2X5 mux2x5_2(Wr1,31,j,Wr);
EXT16T32 ext16t32(Inst[15:0],Se,EXTIMM);
SHIFTER_COMBINATION shifter1(Inst[25:0],PCadd4,InstL2);
SHIFTER shifter2(EXTIMM,2,0,0,EXTIMML2);
REGFILE regfile(Inst[25:21],Inst[20:16],D,Wr,Wreg,Clk,Reset,Qa,Qb);
MUX2X32 mux2x32_1(EXTIMM,Qb,Aluqb,Y);
MUX2X32 mux2x32_2(Qa,Inst,shift,X);
ALU alu(X,Y,Aluc,R,Z);
DATAMEM datamem(R,Qb,Clk,Wmem,Dout);
MUX2X32 mux2x32_3(Dout,R,Reg2reg,D1);
MUX2X32 mux2x32_4(D1,PCadd4,j,D);
CLA_32 cla_32(PCadd4,EXTIMML2,0,mux4x32_2,Cout);
MUX4X32 mux4x32(PCadd4,mux4x32_2,Qa,InstL2,Pcsrc,Result);
assign NEXTADDR=Result;
assign ALU_R=R;
endmodule
封装后的RTL图为:
测试代码为:
module test();
reg Clk,Reset;
wire [31:0] Inst,NEXTADDR,ALU_R,Qb,Qa,Addr,D;
wire [31:0]Result,PCadd4,EXTIMM,InstL2,EXTIMML2,Y,Dout,mux4x32_2,R;
wire Z,Regrt,Se,Wreg,Aluqb,Reg2reg,Cout,Wmem;
wire [3:0]Aluc;
wire [1:0]Pcsrc;
wire [4:0]Wr;
CPU u(Clk,Reset,Addr,Inst,Qa,Qb,ALU_R,NEXTADDR,D);
initial begin
Clk=0;
Reset=0;
#5
Reset<=1;
end
always #5 Clk=~Clk;
endmodule
仿真波形为:
12. 指令代码分析
当CPU设计完成后,还需要对其进行测试,为此需要再设计一个指令存储器和一个数据存储器,同时再设计相应的测试程序。
为方便对指令进行分析,在测试代码中进行CPU的封装。
1.assign
Rom[5’h00]=32’h20010008;//addi $1,$0,8 $1=8
指令含义:addi $1,$0,8
结果:$1=8
2.assign Rom[5’h01]=32’h3402000C;//ori $2,$0,12
指令含义:ori $2,$0,12
结果:$2=12
由于代码过多,不再进行一一分析,选取重要指令进行分析:
2.assign Rom[5’h06]=32’h14220002;//bne $1,$2,2
指令含义:bne $1,$2,2 ($1 和$2 不相等,则跳到 8+当前 PC 地址的值)
结果:PC 地址跳到 0x24
4.assign Rom[5’h0A]=32’h0800000D;// J0D
指令含义:J 0D 直接跳到0D指令
结果:PC寄存器的值更新为0x34
5.assign Rom[5’h0D]=32’hAD02000A;// sw $2 10($8)
指令含义:sw $2 10($8) memory[$8+10]=12
结果:DATAMEM打开,$2的数值写入到$8+10的存储单元中
6.assign Rom[5’h0E]=32’h8D04000A;//lw $4 10($8) $4=12
指令含义:lw $4 10($8) $4=memory[$8+10]=12
结果:DATAMEM关闭,读取$8+10存储单元中的值到$4中
7.assign Rom[5’h16]=32’h0C00001A;//Jal 1A
指令含义:Jal 1A ,跳到目标地址且PC+4存储在ra寄存器($31)中
结果:PC寄存器值更新为0x68
8.assign Rom[5’h1A]=32’h03E00008;//Jr 16
指令含义:Jr 16,跳到目标地址,目标地址为且第16条指令的返回地址
结果:PC寄存器值更新为0x5c
9.assign Rom[5’h17]=32’h0800001A;// J 1A
指令含义:J 1A,跳到目标地址
结果:PC寄存器值更新为0x68
13. 实验结论与体会
通过此处单周期CPU的实验,较好地实现了MIPS32的20条指令,了解到CPU处理程序的复杂过程。与verilog门级电路相比,算法实现的难度相对较低,且代码易读性更高。
参考:https://blog.csdn.net/Accelerato/article/details/86546751
源代码下载链接:https://download.csdn.net/download/qq_45288566/12254660