目录
单周期CPU的设计
- 本实例所设计CPU的指令格式的拟定;
- 基本功能部件的设计与实现;
- CPU各主要功能部件的设计与实现;
- CPU的封装;
- 对各个单元组合而成的CPU进行指令测试,配合使用模拟仿真,了解指令和数据在各个单元中的传输过程及方向。
至少支持add、sub、and、or、addi、andi、ori、lw、sw、beq、bne和j十二条指令。
- 操作系统:Win 10;
- 开发平台:Vivado 2019.2;
- 编程语言:VerilogHDL硬件描述语言
单周期(Single Cycle)CPU是指CPU从取出1条指令到执行完该指令只需1个时钟周期。电平从低到高变化的瞬间称为时钟上升沿,两个相邻时钟上升沿之间的时间间隔称为一个时钟周期。时钟周期一般也称振荡周期(如果晶振的输出没有经过分频就直接作为CPU的工作时钟,则时钟周期就等于振荡周期。若振荡周期经二分频后形成时钟脉冲信号作为CPU的工作时钟,这样,时钟周期就是振荡周期的两倍。)
-
- MIPS指令格式
MIPS指令系统结构有MIPS-32和MIPS-64两种。本实验的MIPS指令选用MIPS-32。以下所说的MIPS指令均指MIPS-32。MIPS的指令格式为32位,以下为MIPS指的3种格式。
R型指令的op均为0,具体操作由func指定。rs和rt是源寄存器号,rd是目的寄存器号。移位指令中使用sa指定移位位数。
I型指令的低16位是立即数,计算时需扩展到32位,依指令的不同需进行零扩展和符号扩展。
J型指令的低26位是地址,是用于产生跳转的目标地址。
本实验中共需要完成12条指令,其中R指令4条,I指令7条,J指令1条,他们的指令结构如下图所示:
R型指令 | |||||||
指令 | [31:26] | [25:21] | [20:16] | [15:11] | [10: 6] | [5:0] | 功能 |
Add | 000000 | rs | rt | rd | 000000 | 100000 | 寄存器加 |
Sub | 000000 | rs | rt | rd | 000000 | 100010 | 寄存器减 |
And | 000000 | rs | rt | rd | 000000 | 100100 | 寄存器与 |
Or | 000000 | rs | rt | rd | 000000 | 100101 | 寄存器或 |
I型指令 | |||||||
Addi | 001000 | rs | rt | immediate | 立即数加 | ||
Andi | 001100 | rs | rt | immediate | 立即数与 | ||
Ori | 001101 | rs | rt | immediate | 立即数或 | ||
Lw | 100011 | rs | rt | offset | 取数据 | ||
Sw | 101011 | rs | rt | offset | 存数据 | ||
Beq | 000100 | rs | rt | offset | 相等转移 | ||
Bne | 000101 | rs | rt | offset | 不等转移 | ||
J型指令 | |||||||
J | 000010 | address | 跳转 |
单周期CPU指从取出一条指令到执行该条指令只需要一个时钟周期。整个过程可以分为:取指、译码、执行、访问、写回,这五个阶段(并不是所有指令都会完全执行这五个阶段)
- 取指令(IF):根据程序计数器PC中的指令地址,从指令存储器中取出一条指令,同时PC根据指令字长度自动递增产生下一条指令所需要的指令地址,但遇到“地址转移”指令时,则控制器把“转移地址”送入PC,当然得到的“地址”需要做些变换才送入PC。
- 指令译码(ID):对取指令操作中得到的指令进行分析并译码,确定这条指令需要完成的操作,由指令的[15-12]位产生相应的操作控制信号,用于驱动执行状态中的各种操作。
- 指令执行(EXE):根据指令译码得到的操作控制信号,具体地执行指令动作,然后转移到结果写回状态。
- 存储器访问(MEM):所有需要访问存储器的操作都将在这个步骤中执行,该步骤给出存储器的数据地址,把数据写入到存储器中数据地址所指定的存储单元或者从存储器中得到数据地址单元中的数据。
- 结果写回(WB):指令执行的结果或者访问存储器中得到的数据写回相应的目的寄存器中。
CPU的电路包括数据路径(Data path)和控制部件(Control Unit)两大部分。下面先给出单周期CPU的总体设计图,再分别介绍每个路径和控制部件的设计。
-
- PC寄存器
存储下一条要执行指令的地址。对于单周期CPU存储的是当前执行指令的地址。
为实现稳定输出,在时钟信号的上升沿更新,而且需要一个控制信号,在控制信号为0的时候初始化PC寄存器,即全部置零。
Clk:时钟周期,输入信号
Reset:控制信号,输入信号
Result目标地址,可能是跳转地址或者是下一条指令的地址,输入信号
Address:指令地址,输出信号
`timescale 1ns / 1ps
module PC(Clk,Reset,Result,Address);
input Clk;//时钟
input Reset;//是否重置地址。0-初始化PC,否则接受新地址
input[31:0] Result;
output reg[31:0] Address;
//reg[31:0] Address;
initial begin
Address <= 0;
end
//将时钟信号的上升沿和控制信号Reset作为变量
//使得pc在上升沿的时候发生改变或被重置。
always @(posedge Clk or negedge Reset)
begin
if (!Reset) //如果为0则初始化PC,否则接受新地址
begin
Address <= 0;
end
else
begin
Address = Result;
end
end
endmodule
作为PC寄存器的更新信号。
由于每条指令32位,所以增加一个32位加法器,固定与32位的立即数4进行相加,且得到的结果在当前时钟信号的上升沿更新进PC寄存器。
PC_0:前指令地址,输入端口
PCadd4:一条指令地址,输出端口
`timescale 1ns / 1ps
module PCadd4(PC_0,PCadd4);
input [31:0] PC_0;//偏移量
output [31:0] PCadd4;//新指令地址
CLA_32 cla32_for_pcadd(PC_0,4,0, PCadd4, Cout);//在ALU模块
endmodule
依据当前pc,读取指令寄存器中相对应地址Addr[6:2]的指令。
将pc的输入作为敏感变量,当pc发生改变的时候,则进行指令的读取,根据相关的地址,输出指令寄存器中相对应的指令,且设计指令时,要用到给出的指令且尽量合理。
address: 指令地址,输入信号
inst:指令编码,输出信号
`timescale 1ns / 1ps
module INSTMEM(
input [31:0] address,
output [31:0] inst
);
wire [31:0] ram [0:18];
assign ram[5'h00] = 32'b001000_00001_00001_0000000000000000;
//不写的码,仅占位用(andi R1 , R1 , 0x0000)
assign ram[5'h01] = 32'b001000_00001_00001_0001001000110100;
//addi R1 , R1 , 0x1234
assign ram[5'h02] = 32'b001000_00010_00010_0001000100010001;
//addi R2 , R2 , 0x1111
assign ram[5'h03] = 32'b000000_00001_00010_00011_00000_100000;
//add R3 , R1 , R2 0x2345
assign ram[5'h04] = 32'b000000_00001_00010_00100_00000_100010;
//sub R4 , R1 , R2 0x0123
assign ram[5'h05] = 32'b000000_00001_00010_00011_00000_100100;
//and R3 , R1 , R2 0x1010
assign ram[5'h06] = 32'b000000_00001_00010_00100_00000_100101;
//or R4 , R1 , R2 0x1335
assign ram[5'h07] = 32'b001100_00001_00101_0010001000100010;
//andi R5 , R1 , 0x2222
assign ram[5'h08] = 32'b001101_00001_00101_0011001100110011;
//ori R5 , R1 , 0x3333
assign ram[5'h09] = 32'b000010_00000000000000000000001100;
//j 跳转到beq行 0800000C
assign ram[5'h0a] = 32'b000000_00000_00010_00011_00100_000010;
//不用
assign ram[5'h0b] = 32'b000000_00000_00010_00011_00100_000011;
//不用
assign ram[5'h0c] = 32'b000100_00001_00011_0000000000000011;
//beq R1 , R2 , 3 不跳 10230003
assign ram[5'h0d] = 32'b000101_00001_00011_0000000000000010;
//bne R1 , R2 , 2 跳至sw行 14230002
assign ram[5'h0e] = 32'b001101_00001_00011_0000000011101111;
//不用
assign ram[5'h0f] = 32'b001110_00001_00011_0000000011101111;
//不用
assign ram[5'h10] = 32'b101011_00100_00101_0000000000000001;
//sw R5 , 1(R4) AC850001 r5=r4=0x1335
assign ram[5'h11] = 32'b100011_00100_00110_0000000000000001;
// lw R6 , 1(R4) 8C860001
assign ram[5'h12] = 32'b000010_00000000000000000000000001;
//j 跳回开头 08000001
assign inst = ram[address[6:2]];
endmodule
控制器是作为CPU控制信号产生的器件,通过解析op得到该指令的各种控制信号,使其他器件有效或无效。
Func:输入信号
Op :输入信号
Z:零标志信号,对Pcsrc有影响,输入信号
Aluc :控制ALU的计算种类,输出信号
Aluqb:控制ALU的Y端口的输入值,输出信号
Pcsrc :控制目标指令地址,输出信号
Regrt:控制输入寄存器的Wr端口,输出信号
Reg2reg:控制REHFILE更新值的来源
Se:控制扩展模块,输出信号
Wmem:控制数据存储器的写使能信号,输出信号
Wreg:控制寄存器端的写使能信号,输出信号
输入端口 | 输出端口 | |||||||||||
Op | Func | 备注 | Z | Regrt | Se | Wreg | Aluqb | Aluc | Wmem | Pcsrc | Reg2reg | |
[5:0] | [5:0] | [1:0] | [1:0] | |||||||||
000000 | 100000 | add | X | 0 | 0 | 1 | 1 | 00 | 0 | 00 | 1 | |
000000 | 100010 | sub | X | 0 | 0 | 1 | 1 | 01 | 0 | 00 | 1 | |
000000 | 100100 | and | X | 0 | 0 | 1 | 1 | 10 | 0 | 00 | 1 | |
000000 | 100101 | or | X | 0 | 0 | 1 | 1 | 11 | 0 | 00 | 1 | |
001000 | - | addi | X | 1 | 1 | 1 | 0 | 00 | 0 | 00 | 1 | |
001100 | - | andi | X | 1 | 0 | 1 | 0 | 10 | 0 | 00 | 1 | |
001101 | - | ori | X | 1 | 0 | 1 | 0 | 11 | 0 | 00 | 1 | |
100011 | - | lw | X | 1 | 1 | 1 | 0 | 00 | 0 | 00 | 0 | |
101011 | - | sw | X | 1 | 1 | 0 | 0 | 00 | 1 | 00 | 1 | |
000100 | - | beq | 0 | 1 | 1 | 0 | 1 | 01 | 0 | 00 | 1 | |
000100 | - | beq | 1 | 1 | 1 | 0 | 1 | 01 | 0 | 10 | 1 | |
000101 | - | bne | 0 | 1 | 1 | 0 | 1 | 01 | 0 | 10 | 1 | |
000101 | - | bne | 1 | 1 | 1 | 0 | 1 | 01 | 0 | 00 | 1 | |
000010 | - | j | X | 1 | 0 | 0 | 1 | 00 | 0 | 11 | 1 |
`timescale 1ns / 1ps
module CONUNIT(Op,Func,Z,Regrt,Se,Wreg,Aluqb,Aluc,Wmem,Pcsrc,Reg2reg);
input [5:0] Op,Func;
input Z;
//输入指令的op和func字段(对应[31:26][5:0])
//输入的z是alu的运算结果 全为零则z为零
output Regrt,Se,Wreg,Aluqb,Wmem,Reg2reg;
output [1:0] Aluc,Pcsrc;
//regrt决定是读取立即数还是寄存器1/0
//se决定是符号扩展还是0扩展 1/0
//wreg寄存器的写信号
//aluqb选择alu读取的扩展后的 还是qb寄存器输出的0/1
//wmem 数据储存器的写信号1有效
//reg2regd 输入寄存器D的是运算R还是dmem的dout
//aluc 00+ 01- 10& 11|
//pcsrs 0 0pc+4 2分支语句 3j指令
wire R_type=~|Op;
wire I_add=R_type&Func[5]&~Func[4]&~Func[3]&~Func[2]&~Func[1]&~Func[0];
wire I_sub=R_type&Func[5]&~Func[4]&~Func[3]&~Func[2]&Func[1]&~Func[0];
wire I_and=R_type&Func[5]&~Func[4]&~Func[3]&Func[2]&~Func[1]&~Func[0];
wire I_or=R_type&Func[5]&~Func[4]&~Func[3]&Func[2]&~Func[1]&Func[0];
wire I_addi=~Op[5]&~Op[4]&Op[3]&~Op[2]&~Op[1]&~Op[0];
wire I_andi=~Op[5]&~Op[4]&Op[3]&Op[2]&~Op[1]&~Op[0];
wire I_ori=~Op[5]&~Op[4]&Op[3]&Op[2]&~Op[1]&Op[0];
wire I_lw=Op[5]&~Op[4]&~Op[3]&~Op[2]&Op[1]&Op[0];
wire I_sw=Op[5]&~Op[4]&Op[3]&~Op[2]&Op[1]&Op[0];
wire I_beq=~Op[5]&~Op[4]&~Op[3]&Op[2]&~Op[1]&~Op[0];
wire I_bne=~Op[5]&~Op[4]&~Op[3]&Op[2]&~Op[1]&Op[0];
wire I_J=~Op[5]&~Op[4]&~Op[3]&~Op[2]&Op[1]&~Op[0];
assign Regrt=I_addi|I_andi|I_ori|I_lw|I_sw|I_beq|I_bne|I_J;
assign Se=I_addi|I_lw|I_sw|I_beq|I_bne;
assign Wreg=I_add|I_sub|I_and|I_or|I_addi|I_andi|I_ori|I_lw;
assign Aluqb=I_add|I_sub|I_and|I_or|I_beq|I_bne|I_J;
assign Aluc[1]=I_and|I_or|I_andi|I_ori;
assign Aluc[0]=I_sub|I_or|I_ori|I_beq|I_bne;
assign Wmem=I_sw;
assign Pcsrc[1]=I_beq&Z|I_bne&~Z|I_J;
assign Pcsrc[0]=I_J;
assign Reg2reg=I_add|I_sub|I_and|I_or|I_addi|I_andi|I_ori|I_sw|I_beq|I_bne|I_J;
endmodule
给出要读取的两个寄存器编号和要写入的寄存器编号,然后由Qa和Qb端口更新Ra和Rb端口的输入编号分别输入其值。
两个D锁存器构成一个D触发器,我们紧接着为D触发器增加使能端,并利用带有使能的1位触发器,构成一个32位的触发器,然后32个触发器构成了一个寄存器堆,我们写顶层文件增加两个端口用于接收要读取的两个寄存器编号,另一个端口用于接收要写入的寄存器的编号,在时钟上升沿将D写入。
Clk:时钟周期,输入信号
Clrn:清零信号,输入信号
D:寄存器更新值,输入信号
Ra:读取寄存器编号1,输入信号
Rb:读取寄存器编号2或立即数,输入信号
We:写入寄存器编号3,输入信号
Wr:写使能信号,为0的时候不能写入,D值不更新,为1的时候能写入,D值更新(当时钟边沿到来时,将𝐷𝑎𝑡𝑎端口送来的数据写入𝑅𝑤指定的寄存器)入信号
Qa:输出寄存器1的值,输出信号
Qb:输出寄存器2的值,输出信号
`timescale 1ns / 1ps
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;
//调用了一个3t32的译码器
//负责将输入的要写入的寄存器号转换为对应的的寄存器号
wire [31:0]Y_mux;
DEC5T32E dec(Wr,We,Y_mux);
//调用一个reg32
wire [31:0]Q31_reg32,Q30_reg32,Q29_reg32,Q28_reg32,Q27_reg32,Q26_reg32,Q25_reg32,Q24_reg32,Q23_reg32,Q22_reg32,Q21_reg32,Q20_reg32,Q19_reg32,Q18_reg32,Q17_reg32,Q16_reg32,Q15_reg32,Q14_reg32,Q13_reg32,Q12_reg32,Q11_reg32,Q10_reg32,Q9_reg32,Q8_reg32,Q7_reg32,Q6_reg32,Q5_reg32,Q4_reg32,Q3_reg32,Q2_reg32,Q1_reg32,Q0_reg32;
REG32 A(D,Y_mux,Clk,Clrn,Q31_reg32,Q30_reg32,Q29_reg32,Q28_reg32,Q27_reg32,Q26_reg32,Q25_reg32,Q24_reg32,Q23_reg32,Q22_reg32,Q21_reg32,Q20_reg32,Q19_reg32,Q18_reg32,Q17_reg32,Q16_reg32,Q15_reg32,Q14_reg32,Q13_reg32,Q12_reg32,Q11_reg32,Q10_reg32,Q9_reg32,Q8_reg32,Q7_reg32,Q6_reg32,Q5_reg32,Q4_reg32,Q3_reg32,Q2_reg32,Q1_reg32,Q0_reg32);
//因为reg输出的是32个信号 所以要用2个32选1的复用器选择那个是最后的输出
MUX32X32 select1(Q0_reg32,Q1_reg32,Q2_reg32,Q3_reg32,Q4_reg32,Q5_reg32,Q6_reg32,Q7_reg32,Q8_reg32,Q9_reg32,Q10_reg32,Q11_reg32,Q12_reg32,Q13_reg32,Q14_reg32,Q15_reg32,Q16_reg32,Q17_reg32,Q18_reg32,Q19_reg32,Q20_reg32,Q21_reg32,Q22_reg32,Q23_reg32,Q24_reg32,Q25_reg32,Q26_reg32,Q27_reg32,Q28_reg32,Q29_reg32,Q30_reg32,Q31_reg32,Ra,Qa);
MUX32X32 select2(Q0_reg32,Q1_reg32,Q2_reg32,Q3_reg32,Q4_reg32,Q5_reg32,Q6_reg32,Q7_reg32,Q8_reg32,Q9_reg32,Q10_reg32,Q11_reg32,Q12_reg32,Q13_reg32,Q14_reg32,Q15_reg32,Q16_reg32,Q17_reg32,Q18_reg32,Q19_reg32,Q20_reg32,Q21_reg32,Q22_reg32,Q23_reg32,Q24_reg32,Q25_reg32,Q26_reg32,Q27_reg32,Q28_reg32,Q29_reg32,Q30_reg32,Q31_reg32,Rb,Qb);
endmodule
module DEC5T32E(X,En,Y);
input [4:0]X;
input En;
output [31:0]Y;
wire [31:0]Y1,En_32;
assign Y1=1<<X;
assign En_32={32{En}};
assign Y=Y1&En_32;
endmodule
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);
//参数的输入顺序和课本保持一致
//32位寄存器堆 共有32个32位的输出接口 一个32位的使能信号
input [31:0]D,En;
input Clk,Clrn;
output [31:0]Q0,Q1,Q2,Q3,Q4,Q5,Q6,Q7,Q8,Q9,Q10,Q11,Q12,Q13,Q14,Q15,Q16;
output [31:0]Q17,Q18,Q19,Q20,Q21,Q22,Q23,Q24,Q25,Q26,Q27,Q28,Q29,Q30,Q31;
D_FFEC32 q1(D,Clk,En[1],Clrn,Q1);
D_FFEC32 q2(D,Clk,En[2],Clrn,Q2);
D_FFEC32 q3(D,Clk,En[3],Clrn,Q3);
D_FFEC32 q4(D,Clk,En[4],Clrn,Q4);
D_FFEC32 q5(D,Clk,En[5],Clrn,Q5);
D_FFEC32 q6(D,Clk,En[6],Clrn,Q6);
D_FFEC32 q7(D,Clk,En[7],Clrn,Q7);
D_FFEC32 q8(D,Clk,En[8],Clrn,Q8);
D_FFEC32 q9(D,Clk,En[9],Clrn,Q9);
D_FFEC32 q10(D,Clk,En[10],Clrn,Q10);
D_FFEC32 q11(D,Clk,En[11],Clrn,Q11);
D_FFEC32 q12(D,Clk,En[12],Clrn,Q12);
D_FFEC32 q13(D,Clk,En[13],Clrn,Q13);
D_FFEC32 q14(D,Clk,En[14],Clrn,Q14);
D_FFEC32 q15(D,Clk,En[15],Clrn,Q15);
D_FFEC32 q16(D,Clk,En[16],Clrn,Q16);
D_FFEC32 q17(D,Clk,En[17],Clrn,Q17);
D_FFEC32 q18(D,Clk,En[18],Clrn,Q18);
D_FFEC32 q19(D,Clk,En[19],Clrn,Q19);
D_FFEC32 q20(D,Clk,En[20],Clrn,Q20);
D_FFEC32 q21(D,Clk,En[21],Clrn,Q21);
D_FFEC32 q22(D,Clk,En[22],Clrn,Q22);
D_FFEC32 q23(D,Clk,En[23],Clrn,Q23);
D_FFEC32 q24(D,Clk,En[24],Clrn,Q24);
D_FFEC32 q25(D,Clk,En[25],Clrn,Q25);
D_FFEC32 q26(D,Clk,En[26],Clrn,Q26);
D_FFEC32 q27(D,Clk,En[27],Clrn,Q27);
D_FFEC32 q28(D,Clk,En[28],Clrn,Q28);
D_FFEC32 q29(D,Clk,En[29],Clrn,Q29);
D_FFEC32 q30(D,Clk,En[30],Clrn,Q30);
D_FFEC32 q31(D,Clk,En[31],Clrn,Q31);
assign Q0=0;
endmodule
module D_FFEC32(D,Clk,En,Clrn,Q,Qn);//32位的带有使能端的D触发器 用了32个1位的D_FFEC
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
module D_FFEC(D,Clk,En,Clrn,Q,Qn);//带有使能端的1位D触发器
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
module MUX2X1(A0,A1,S,Cout);//在D_FFEC中用到的选择器
input A0,A1,S;
output Cout;
function Select;
input A0,A1,S;
case(S)
0:Select=A0;
1:Select=A1;
endcase
endfunction
assign Cout=Select(A0,A1,S);
endmodule
module D_FF(D,Clk,Q,Qn);//1位D触发器
input D,Clk;
output Q,Qn;
wire Clkn,QO,Qn0;
not i0(Clkn,Clk);
D_Latch d0 (D,Clkn,Q0,Qn0);
D_Latch d1 (Q0,Clk,Q,Qn);
endmodule
module D_Latch(D,En,Q,Qn);//一个d锁存器
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
需要2位控制信号控制运算类型,核心部件是32位加法器ADDSUB_32。
Aluc:控制信号
X:寄存器1的值,输入信号
Y:寄存器2的值或立即数,输入信号
Cout:输入寄存器端口D的计算结果,输出信号
Z:当值为1时代表两个输入信号值相等,当值为0时代表两个输入信号不等,输出信号。
`timescale 1ns / 1ps
module ALU(X,Y,Aluc,Cout,Z);
//00+ 01- 10& 11|
input [31:0]X,Y;
input [1:0]Aluc;
output [31:0]Cout;
output Z;
wire[31:0]out_as,out_and,out_or,out_and_or;
ADDSUB_32 as(X,Y,Aluc[0],out_as);
assign out_and=X&Y;
assign out_or=X|Y;
MUX2X32 and_or_select(out_and,out_or,Aluc[0],out_and_or);//0& 1|
MUX2X32 as_ao_selete(out_as,out_and_or,Aluc[1],Cout);//0+- 1&|
assign Z=~|Cout;
endmodule
32位加法器ADDSUB_32
`timescale 1ns / 1ps
//顶层文件 输入两个数 选择(进位)
//输出两个 结果 进位
module ADDSUB_32(X, Y, Sub, S, Cout);// 32位加法器ADDSUB_32
//sub 0是加法 1是减法
input [31:0] X;
input [31:0] Y;
input Sub;
output [31:0] S;
output Cout;//输出进位信息
//对输入的sub进行扩展 如果是加法的话0的话异或还是原数
//如果是减的话做异或并且让进位信号为1
CLA_32 add0 (X, Y^{32{Sub}}, Sub, S, Cout);
endmodule
module CLA_32(X, Y, Cin, S, Cout);
input [31:0] X, Y;
input Cin;//输入进位信息
output [31:0] S;
output Cout;
//32位分成8个CLA
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
module CLA_4(X,Y,Cin,S,Cout);
input [3:0]X,Y;
input Cin;
output [3:0]S;
output Cout;
wire cout0,cout1,cout2,cout3;
assign cout0=X[0]&Y[0]|((X[0]|Y[0])&Cin);
assign cout1=X[1]&Y[1]|((X[1]|Y[1])&cout0);
assign cout2=X[2]&Y[2]|((X[2]|Y[2])&cout1);
assign Cout=X[3]&Y[3]|((X[3]|Y[3])&cout2);
assign S[0]=X[0]^Y[0]^Cin;
assign S[1]=X[1]^Y[1]^cout0;
assign S[2]=X[2]^Y[2]^cout1;
assign S[3]=X[3]^Y[3]^cout2;
endmodule
数据存储器,通过控制信号,对数据寄存器进行读或者写操作,并且此处模块额外合并了输出DB的数据选择器,此模块同时输出写回寄存器组的数据DB。
由于需要支持取数/存数指令,所以要在指令储存器的基础上增加写入数据的数据写入端口,写使能信号。又因为写操作在时钟信号的上升沿,所以要增加时钟信号。
Clock:时钟周期,输入信号
addr:访存地址,输入信号
datain:输入的值,输入信号
we:写使能信号,We为1时,进行sw指令操作,此时Din端口输入信号实际为rt,Addr端口输入信号为rs和偏移量相加的地址,在时钟周期上升沿将rt的值写入改地址的储存单元;We为0时,进行lw指令操作,此时Addr端口输入信号为rs和偏移量相加的地址,Dout为读取该地址储存器的内容,输入信号
dataout:读取的值,输出信号
`timescale 1ns / 1ps
module DATEMEM(
input [31:0] addr,
input [31:0] datain,
input Clock,
input we,
output [31:0] dataout
);
reg [31:0] ram [31:0];
assign dataout = ram[addr[6:2]];
always @ (posedge Clock) begin
if (we) ram[addr[6:2]] <= datain;
end
integer i;
initial begin
for ( i = 0 ; i <= 31 ; i = i + 1) ram [i] = i * i;
end
endmodule
实现目标地址的选择,四选一多路选择器,主要选择从输入的4个口中的具体选择哪一个口子数据输出
目标地址可能是PC+4,也可能是beq和bne的跳转地址或是J型跳转地址,所以采用一个32位四选一多路选择器,因为只有4个来源,所以只要使用两位的s来进行选择就行。代码功能就是从a1,a2,a3,a4中通过s选一个输出到y,s的内容一般由控制器译码模块决定
A0:PC+4的地址,输入信号
A1:空位,输入信号
A2:beq和bne指令的跳转地址,输入信号
A3:J指令的跳转地址,输入信号
S:对地址进行选择的控制信号,输入信号
Y:目标地址,输出信号
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)
00:select=A0;
01:select=A1;
10:select=A2;
11:select=A3;
endcase
endfunction
assign Y=select(A0,A1,A2,A3,S);
endmodule
R型指令和I行指令的Wr信号不同,所以需要一个5位二选一选择器进行选择。
R型指令Wr选择rd信号,I型指令Wr选择rt信号。
Inst[15:11],:R型指令的rd信号,输入信号
nst[20:16]:I型指令的rt信号,输入信号
Regrt:选择指令的控制信号,输入信号
Wr:Wr信号,输出信号
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)
0:select=A0;
1:select=A1;
endcase
endfunction
assign Y=select(A0,A1,S);
endmodule
在lw指令中,需要将DATAMEM中选中储存器的值保存到REGFILE的寄存器中,而其他会更新REGFILE的指令的更新信号来自于ALU的R输出端。所以需要一个二选一选择器进行选择
Dout: DATAMEM的输出值,输入信号
R:ALU的输出值,输入信号
S:控制信号
Y:写入R寄存器堆D端的信号,输出信号
ALU的Y端输入信号种类根据指令的不同而不同
在执行R型指令时,ALU的Y端输入信号可能来自Qb,在执行I型指令的addi,andi和ori指令时时,ALU的Y端输入信号来自EXT16T32,所以需要一个二选一选择器
A1:来自EXT16T32的信号,输入信号
A1:来自REGFLE中Qb端口的信号,输入信号
S:控制信号
Y:输入ALU进行后续计算的信号,输出信号
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)
0:select=A0;
1:select=A1;
endcase
endfunction
assign Y=select(A0,A1,S);
endmodule
由于有32个寄存器可供选择,所以必须有32位的32选1的多路选择器
在寄存器堆中,因为reg输出的是32个信号 所以要用2个32选1的复用器选择最后的输出
Q0-Q31:输入信号
S:控制信号
Y:输出信号
module MUX32X32(Q0,Q1,Q2,Q3,Q4,Q5,Q6,Q7,Q8,Q9,Q10,Q11,Q12,Q13,Q14,Q15,Q16,Q17,Q18,Q19,Q20,Q21,Q22,Q23,Q24,Q25,Q26,Q27,Q28,Q29,Q30,Q31,S,Y);
input[4:0] S;
input[31:0] Q0,Q1,Q2,Q3,Q4,Q5,Q6,Q7,Q8,Q9,Q10,Q11,Q12,Q13,Q14,Q15,Q16,Q17,Q18,Q19,Q20,Q21,Q22,Q23,Q24,Q25,Q26,Q27,Q28,Q29,Q30,Q31;
output[31:0] Y;
function[31:0] select;
input[31:0] Q0,Q1,Q2,Q3,Q4,Q5,Q6,Q7,Q8,Q9,Q10,Q11,Q12,Q13,Q14,Q15,Q16,Q17,Q18,Q19,Q20,Q21,Q22,Q23,Q24,Q25,Q26,Q27,Q28,Q29,Q30,Q31;
input[31: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(Q0,Q1,Q2,Q3,Q4,Q5,Q6,Q7,Q8,Q9,Q10,Q11,Q12,Q13,Q14,Q15,Q16,Q17,Q18,Q19,Q20,Q21,Q22,Q23,Q24,Q25,Q26,Q27,Q28,Q29,Q30,Q31,S);
endmodule
一个固定左移两位的移位器,用于beq指令的移位
使用32位移位器SHIFTER32,固定左移两位即可
X:指令中的偏移量,输入信号
Sh:偏移量左移后的结果,输出信号
module SHIFTER32_L2(X,Sh);
//用于beq指令的移位
//beq指令的扩展之后左移两位即乘四
input [31:0] X;
output [31:0] Sh;
parameter pa=2'b00;//一个局部的常量
assign Sh={X[29:0],pa};
endmodule
用于j指令的移位器,指令中用以产生跳转的目标地址
跳转的目标地址采用拼接的方式形成,最高4位为PC+4的最高4位,中间26位为J型指令的26位立即数字段,最低两位为0.
Inst[26:0]:指令编码的低26位字段,输入信号。
PCadd4:PC+4的32位字段,输入信号。
sh:32位转移目标地址,输出信号。
module SHIFTER_FOR_J(X,PCADD4,Sh);
//用于j指令的移位器 需要产生的前四位是pc+4的最高四位
//加上指令的第26位 最低位是00
//就是课本上最下边的移位器
input [26:0] X;
input [31:0] PCADD4;
output [31:0] Sh;
parameter pa=2'b00;
assign Sh={PCADD4[3:0],X[26:0],pa};
endmodule
I指令的addi需要对立即数进行符号拓展,andi和ori需要对立即数进行零扩展,所以需要一个扩展模块。
采用一个16位扩展成32位的扩展模块EXT16T32,实现零扩展和符号扩展
select:选择零扩展或是符号扩展的控制模块,输入信号
X:I型指令的立即数字段,输入信号
Y:扩展后的立即数,输出信号。
`timescale 1ns / 1ps
module EXT16T32 (X, Select, Y);
//要移位的信号x 选择是0扩展还是符号扩展的select
input [15:0] X;
input Select;
output [31:0] Y;
wire [31:0] E0, E1;//e0是零扩展 e1是符号扩展
wire [15:0] e = {16{X[15]}};//e是x[15]的符号位*16
parameter z = 16'b0;//用于0扩展
assign E0 = {z, X};
assign E1 = {e, X};
MUX2X32 select_for_ext16t32(E0, E1, Select, Y);//e0是零扩展 e1是符号扩展
endmodule
-
- SingleCycleCPU(整合部件)
- 模块功能
实现CPU的封装,设计输出信号使得在方正时便于观察其波形图
Clk:时钟周期,外部输入信号。
Reset:清零信号,外部输入信号。
见第四部分指令分析
在这部分,我们要完成的是对于CPU的测试,检查结果是否正确,所所以我们要首先完成的是检查输出,我们构造顶层文件,输出所有的值,检查对应的结果并分析指令。
随后当检查结果正确后,我们将对CPU进行封装。
module CPU(Clk,Reset,Inst,Qa,Qb,Addr
,Result,PCadd4,EXTIMM,InstL2,EXTIMML2,D,Y,Dout,mux4x32_2,R,
Z,Regrt,Se,Wreg,Aluqb,Reg2reg,Cout,Wmem,
Aluc,Pcsrc,Wr
);
input Clk,Reset;
output [31:0] Inst,Qb,Qa,Addr;
output [31:0]Result,PCadd4,EXTIMM,InstL2,EXTIMML2,D,Y,Dout,mux4x32_2,R;
output Z,Regrt,Se,Wreg,Aluqb,Reg2reg,Cout,Wmem;
output [1:0]Aluc,Pcsrc;
output [4:0]Wr;
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);
MUX2X5 mux2x5(Inst[15:11],Inst[20:16],Regrt,Wr);
EXT16T32 ext16t32(Inst[15:0],Se,EXTIMM);
SHIFTER_FOR_J shifter1(Inst[26:0],PCadd4,InstL2);
SHIFTER32_L2 shifter2(EXTIMM,EXTIMML2);
REGFILE regfile(Inst[25:21],Inst[20:16],D,Wr,Wreg,Clk,Reset,Qa,Qb);
MUX2X32 mux2x321(EXTIMM,Qb,Aluqb,Y);
ALU alu(Qa,Y,Aluc,R,Z);
DATEMEM datamem(R,Qb,Clk,Wmem,Dout);
MUX2X32 mux2x322(Dout,R,Reg2reg,D);
CLA_32 cla_32(PCadd4,EXTIMML2,0,mux4x32_2, Cout);
MUX4X32 mux4x32(PCadd4,0,mux4x32_2,InstL2,Pcsrc,Result);
//assign NEXTADDR=Result;
//assign ALU_R=R;
endmodule
`timescale 1ns / 1ps
module TEST;
//module CPU(Clk,Reset,Inst,Qb,Qa,Addr
//,Result,PCadd4,EXTIMM,InstL2,EXTIMML2,D,Y,Dout,mux4x32_2,R,
//Z,Regrt,Se,Wreg,Aluqb,Reg2reg,Cout,Wmem,
//Aluc,Pcsrc,Wr
//);
reg Clk,Reset;
wire [31:0] Inst,Qb,Qa,Addr;
wire [31:0]Result,PCadd4,EXTIMM,InstL2,EXTIMML2,D,Y,Dout,mux4x32_2,R;
wire Z,Regrt,Se,Wreg,Aluqb,Reg2reg,Cout,Wmem;
wire [1:0]Aluc,Pcsrc;
wire [4:0]Wr;
CPU test(
.Clk(Clk),
.Reset(Reset),
.Inst(Inst),
.Qa(Qa),
.Qb(Qb),
.Addr(Addr),
.Result(Result),
.PCadd4(PCadd4),
.EXTIMM(EXTIMM),
.InstL2(InstL2),
.EXTIMML2(EXTIMML2),
.D(D),
.Y(Y),
.Dout(Dout),
.mux4x32_2(mux4x32_2),
.R(R),
.Z(Z),
.Regrt(Regrt),
.Se(Se),
.Wreg(Wreg),
.Aluqb(Aluqb),
.Reg2reg(Reg2reg),
.Cout(Cout),
.Wmem(Wmem),
.Aluc(Aluc),
.Pcsrc(Pcsrc),
.Wr(Wr)
);
initial begin
Clk=0;Reset=0;
#10;
Clk=1;Reset=0;
#10;
Reset=1;
Clk=0;
forever #20 Clk=~Clk;
end
endmodule
assign ram[5'h01] = 32'b001000_00001_00001_0001001000110100;
指令含义 addi R1 , R1 , 0x1234
对应的Inst 20211234 对应的Addr 00000004
波形图
由于是立即数相加,而原先的寄存器中没有数据,所以Qa,Qb都是0,最终的运算结果和预期吻合,也相当于赋值操作
assign ram[5'h03] = 32'b000000_00001_00010_00011_00000_100000;
指令含义 add R3 , R1 , R2
对应的Inst 00221802 对应的Addr 0000000c
波形图
经过先前的赋值,R1=0x1234 R2=0x1111 所以R3=0x2345
assign ram[5'h04] = 32'b000000_00001_00010_00100_00000_100010;
指令含义 sub R4 , R1 , R2
对应的Inst 00222022 对应的Addr 00000010
波形图
经过先前的赋值,R1=0x1234 R2=0x1111 所以R4=0x0123,与预期结果吻合。
assign ram[5'h05] = 32'b000000_00001_00010_00011_00000_100100;
指令含义 and R3 , R1 , R2
对应的Inst 00221824 对应的Addr 00000014
波形图
经过先前的赋值,R1=0x1234 R2=0x1111 所以按位与之后R3=0x1111,与预期结果吻合。
assign ram[5'h06] = 32'b000000_00001_00010_00100_00000_100101;
指令含义 or R4 , R1 , R2 0x1335
对应的Inst 00222025 对应的Addr 00000018
波形图
经过先前的赋值,R1=0x1234 R2=0x1111 所以按位或之后R3=0x1335,与预期结果吻合。
assign ram[5'h07] = 32'b001100_00001_00101_0010001000100010;
指令含义 andi R5 , R1 , 0x2222
对应的Inst 3025222 对应的Addr 0000001c
波形图
经过先前的赋值,R1=0x1234 所以与扩展后的立即数,也就是上图中的Y=0x2222相与之后R4=0x0200,与预期结果吻合。
assign ram[5'h08] = 32'b001101_00001_00101_0011001100110011;
指令含义 ori R5 , R1 , 0x3333
对应的Inst 20211234 对应的Addr 00000004
波形图
经过先前的赋值,R1=0x1234 所以与扩展后的立即数,也就是上图中的Y=0x2222相与之后R4=0x0200,与预期结果吻合。
assign ram[5'h09] = 32'b000010_00000000000000000000001100;
指令含义 j指令 跳转两行到beq行
对应的Inst 0800000C 对应的Addr 00000024
波形图
J指令的高4位是pc+4的高四位,然后取偏移量的26位,最后两位补零,经过计算,偏移后的地址,也就是上图中的NXTADDR为原地址加12,Pcsrc的值是3,下一次的地址也就是0x0030,与预期的结果是吻合的。
assign ram[5'h0c] = 32'b000100_00001_00011_0000000000000011;
指令含义 beq R1 , R2 , 3
对应的Inst 10230003 对应的Addr 00000030
波形图
经过先前的赋值,R1=0x1234 R2=0x1111 所以两者不等,不发生跳转,Pcsrc的值是0,下一次的地址还是pc+4.
assign ram[5'h0d] = 32'b000101_00001_00011_0000000000000010;
指令含义 bne R1 , R2 , 2
对应的Inst 14230002 对应的Addr 00000034
波形图
经过先前的赋值,R1=0x1234 R2=0x1111 所以两者不等,发生跳转,Pcsrc的值是2,下一次的地址是0x0040
assign ram[5'h10] = 32'b101011_00100_00101_0000000000000001;
指令含义 sw R5 , R4,1
对应的Inst ac850001 对应的Addr 00000040
波形图
Sw是存数,存进去的数是1,也就是Y。
assign ram[5'h11] = 32'b100011_00100_00110_0000000000000001;
指令含义 lw R6 , R4,1
对应的Inst 8c860001 对应的Addr 00000044
波形图
Lw是取数指令,从地址为R4+1的存储单元取一个数到数据存储器。
在经过了4的测试之后,我们确定自己的代码没有问题,要将CPU封装成形,下面是我们的封装代码,测试文件和代码。
`timescale 1ns / 1ps
module FINAL(Clk,Clrn);
input Clk,Clrn;
wire [31:0] Qb,R,Addr;
wire Wmem;
wire [31:0] Inst,D;
CPU cpu(Clk,Clrn,Inst,D,Addr,Qb,R,Wmem);
DATEMEM datamem(R,Qb,Clk,Wmem,D);
INSTMEM instmem(Addr,Inst);
endmodule
module CPU(Clk,Reset,Inst,D,Addr,Qb,R,Wmem);
input Clk,Reset;
input [31:0] Inst,D;
output [31:0] Qb,R,Addr;
output Wmem;
wire [31:0] Result,Qa;
wire [31:0]PCadd4;
PC pc(Clk,Reset,Result,Addr);
PCadd4 pcadd4(Addr,PCadd4);
wire Z;//alu的输出z
wire Regrt,Se,Wreg,Aluqb,Reg2reg;
wire [1:0] Aluc,Pcsrc;
CONUNIT conunit(Inst[31:26],Inst[5:0],Z,Regrt,Se,Wreg,Aluqb,Aluc,Wmem,Pcsrc,Reg2reg);
wire [4:0]Wr;
MUX2X5 mux2x5(Inst[15:11],Inst[20:16],Regrt,Wr);
wire [31:0] Extout;
EXT16T32 ext16t32(Inst[15:0],Se,Extout);
wire [31:0] InstjL2;
SHIFTER_FOR_J j_shifter(Inst[26:0],PCadd4,InstjL2);
wire [31:0]D;//最后一个mux2x32的输出
REGFILE regfile (Inst[25:21],Inst[20:16],D,Wr,Wreg,Clk,Reset,Qa,Qb);
wire [31:0]Y;//输入alu的第二个值;
MUX2X32 mux2x32_for_qb(Extout,Qb,Aluqb,Y);
ALU alu(Qa,Y,Aluc,R,Z);//R在output定义
wire [31:0]Dout;
MUX2X32 mux2x32_for_dmem_alu(Dout,R,Reg2reg,D);
wire [31:0]ExtoutL2;
SHIFTER32_L2 shifter_l2(Extout,ExtoutL2);
wire Cout;//反正也用不上
wire [31:0]mux4x32_2;
CLA_32 cla_for_last(ExtoutL2,PCadd4,0,mux4x32_2,Cout);
MUX4X32 mux4x32(PCadd4,0,mux4x32_2,InstL2,Pcsrc,Result);
endmodule
`timescale 1ns / 1ps
module FINAL_TEST;
reg Clk;
reg Reset;
wire [31:0] Addr,Inst,Qa,Qb,R,Result,D;
FINAL test(
.Clk(Clk),
.Clrn(Reset)
);
initial begin
Clk=0;Reset=0;
#10;
Clk=1;Reset=0;
#10;
Reset=1;
Clk=0;
forever #20 Clk=~Clk;
end
endmodule
通过仿真测试,以上指令均成功实现,在本次实验过程中,从一开始不会写代码,到深入了解什么是单周期CPU,怎么设计一个CPU,到最后完成整个单周期CPU的实验,经历了一段很长的历程,过程是艰难的,却让我们学到很多,不管是设计指令阶段,从功能级到门级的修改阶段,还是最后的调整仿真阶段,都让我们学会了合作,取舍,关注细节,以及面对各种突发情况的应对能力。
实验过程中,由于各个部件复杂并且环环相扣,所以可以将每个部件都分别进行调试之后再组装在一起,避免因为出现小错误牵一发而动全身,各部件尽量再拆分为更小的部件组合而成,这样在出现问题时能够清晰的找到出错点,并进行改进。