可执行MIPS指令的单周期CPU

可执行MIPS指令的单周期CPU

@Marphownio原创

一、任务说明

本项目需要完成一个能够实现指定MIPS指令的单周期CPU。
1.该CPU需能执行13个MIPS指令:add,sub,and,or,slt,addi,andi,ori,slti,sw,lw,j,nop。
2. MIPS指令输入方式不做特殊要求。
3. 项目中设计的存储空间通过数组来得到。
4. 除了PC相对寻址不做要求外,其他寻址模式都必须得到支持。
5. 最后的波形仿真应当采用功能仿真,且所有存储器件中的数据都应当被显示。

二、部件设计及其仿真

1、程序计数器PC

1)代码
module PC(adress_in,adress_out,clk,reset,PCWre);
parameter width=32;//PC位宽为32
input [width-1:0] adress_in;
input clk,reset,PCWre;//pc为1时,正常输出。
output reg [width-1:0] adress_out;

always@(posedge clk,posedge reset)
begin
if (reset)
    begin  adress_out=0;  end// 如果重置,赋值为0
else 
    begin  if (PCWre) begin  adress_out = adress_in;  end//PCWre为1时,pc正常改变,否则保持
           else       begin  adress_out = adress_out; end
    end
end
endmodule
2)部件功能

PC存储着CPU中MIPS指令的地址,通过CPU通过PC来读取存储在Instruction Memeory中的MIPS指令。pc值的改变即代表指令的改变。

3)实现思路

在每个时钟上升沿的时候,如果PCWre的值为1,则PC将新输入的指令地址adress_in赋值给adress_out作为输出;如果PCWre的值为0,adress_out则保持原来的值不变。在reset值为1时,PC初始化,为adress_out赋初值为0,reset为0时则不进行操作。

4)仿真代码
module testbench();
reg clk,reset,PCWre;
reg [31:0]adress_in;
wire [31:0]adress_out;
PC pc(adress_in,adress_out,clk,reset,PCWre);
always #10 clk=~clk;
initial 
begin
clk=0;
reset=0;
PCWre=1;
adress_in=32'b0;
#30 adress_in=32'b1;
#20 adress_in=32'b0;
    PCWre=0;
#20 adress_in=32'b1;
reset=1;
end
endmodule
5)仿真波形图

在这里插入图片描述

6)仿真说明

可以看到,在仿真第一第二个周期时,每次遇到clk时钟的上升沿时,adress_out=adress_in,并且输出;在第三个周期时,由于 PCWre=0,adress_out保持不变;在第四个周期时,由于 reset=1,adress_out清零。部件验证正确。

2、PC地址改变器PCadder

1)代码
module PCADDER(clk,pc,adress,PCsrc,y);
input clk;
input [31:0] pc;
input [25:0] adress;
input PCsrc;
output reg [31:0] y;
always@(*)
begin
y=pc+4;
if (PCsrc) begin y[27:2]=adress;
                 y[1:0]=2'b00;
           end
end
endmodule
2)部件功能

在CPU中,PCadder改变每个时钟周期内pc的值,从而读取不同的MIPS指令,同时可以实现PC的伪寻址。

3)实现思路

在每个时钟上升沿的时候,如果PCsrc的值为0,考虑到Instruction Memeory中的MIPS指令都是以8位分段进行存储的,因此每次为pc值加4后输出;如果PCsrc的值为1,则在pc值加4的基础上,让pc[1:0]=2’b00(即是左移两位),让pc[27:0]等于跳转的26位地址adress,完成PC的伪寻址操作,这一步对应J指令。

4)仿真代码
module testbench();
reg clk,PCsrc;
reg [31:0]pc;
reg [35:0]adress;
wire [31:0]y;
PCADDER pcadder(clk,pc,adress,PCsrc,y);
always #10 clk=~clk;
initial 
begin
clk=0;
PCsrc=0;
adress=26'b1;
pc=32'b0;
#30 pc=32'b0000_0000_0000_0000_0000_0000_0000_0100;
#20 pc=32'b0000_0000_0000_0000_0000_0000_0000_1000;
#20 PCsrc=1;
#20 #20 pc=32'b0000_0000_0000_0000_0000_0000_0001_0000;
PCsrc=0;
end
endmodule
5)仿真波形图

在这里插入图片描述

6)仿真说明

可以看到,在仿真第一第二个周期时,每次遇到clk时钟的下降沿时,y=pc+4,并且输出;在第三个周期时,由于 PCsrc=1,进行pc的伪寻址,由于adress=1,因此伪寻址后,输出y=4;在第四个周期时,由于PCsrc=0,y=pc+4。部件验证正确。

3、寄存器读写单元regfile

1)代码
module REGIFILE(RegWre,clk,reg1_adress,reg2_adress,write_data,reg1_data,reg2_data,write_reg);
input RegWre,clk;
input [4:0] reg1_adress, reg2_adress,write_reg;
input [31:0] write_data;
output [31:0] reg1_data,reg2_data;
reg [31:0] value [31:0];
integer i;
initial 
begin 
for(i=0;i<32;i=i+1)
    value[i]=i;
end                   
assign  reg1_data=value[reg1_adress];
assign  reg2_data=value[reg2_adress];
always@(negedge clk)
begin
if(RegWre==1 && write_reg!=0) begin value[write_reg]=write_data; end
end
endmodule
2)部件功能

寄存器读写单元,内部定义了一个32位深、32位宽的数组value,在CPU中用于存储数据,并可以根据地址读、写对应的数据。

3)实现思路

初始化模块时,为该模块的32个位宽32的数组依次赋值为0~31,以便于仿真时确定代码的正确性。

模块工作时,寄存器可以通过MIPS指令中给定的地址reg1_adress与reg2_adress,输出相应的数据reg1_data与reg2_data。并且在下降沿时,若RegWre=1,且当前写入地址write_reg不等于0,则对寄存器进行写入操作,将输入的数据write_data赋值给value[write_reg];反之,则不进行任何操作。

4)仿真代码
module testbench();
reg clk,RegWre;
reg [4:0]reg1_adress,reg2_adress,write_reg;
reg [31:0]write_data;
wire [31:0]reg1_data,reg2_data;
REGIFILE regfile(RegWre,clk,reg1_adress,reg2_adress,write_data,reg1_data,reg2_data,write_reg);
always #10 clk=~clk;
initial 
begin
clk=0;
RegWre=0;
reg1_adress=0;
#10 reg2_adress=5'b00001;
#10 reg2_adress=5'b00010;
#10 reg2_adress=5'b00100;
#10 reg2_adress=5'b01000;
#10 reg2_adress=5'b10000;
#20
RegWre=1;
write_data=5'b00001;
write_reg=0;
#20
RegWre=1;
write_data=5'b0000;;
write_reg=1;
end
endmodule
5)仿真波形图

在这里插入图片描述

6)仿真说明

在前两个周期时,由仿真文件可以看到,当reg2_adress分别对应1、2、4、8时,reg2_data也相应1、2、4、8;在第三个周期时,由于 RegWre=1,但write_reg=0,因此未进行写入操作;在第四个周期时,RegWre=1,且write_reg=1,可以进行写入操作。部件验证正确。

4、立即数扩展单元Extend

1)代码
module EXTEND(ExtSel,immediate,extendImmediate);
input ExtSel;              // 控制补位,如果为1,进行符号扩展,如果为0,全补0
input [15:0] immediate;      // 16位立即数
output [31:0] extendImmediate;// 进行扩展
assign extendImmediate[15:0] = immediate;
assign extendImmediate[31:16] = ExtSel ? (immediate[15] ? 16'hffff : 16'h0000) : 16'h0000;
endmodule
2)部件功能

在CPU中,根据需要,可为输入的立即数immediate进行符号位扩展或无符号扩展,并将扩展后的立即数输出。

3)实现思路

模块工作时,模块将输入的16位立即数赋值给extendImmediate[15:0] 。若ExtSel=1,则对该立即数进行符号位扩展,将立即数的最高位全部赋值给extendImmediate[31:16];反之,则进行无符号扩展,为extendImmediate[31:16]全部赋值为0。

4)仿真代码
module testbench();
reg ExtSel;             
reg [15:0] immediate;   
wire [31:0] extendImmediate;
EXTEND extend(ExtSel,immediate,extendImmediate);
initial 
begin
#10
ExtSel=0;
immediate=16'b1000_0000_0000_0000;
#10
ExtSel=1;
immediate=16'b1000_0000_0000_0000;
end
endmodule
5)仿真波形图

在这里插入图片描述

6)仿真说明

由仿真文件可以看到,当ExtSel=0时,immediate进行无符号扩展,extendImmediate =32’b11111111111111111000000000000000;当ExtSel=1时,immediate进行符号扩展,extendImmediate=32’b11111111111111111000000000000000。部件验证正确。

5、运算单元ALU

1)代码
module ALU(A_in,B_in,F_out,op);
input [31:0] A_in;
input [31:0] B_in;
input [2:0] op;
output reg [31:0] F_out;
always @(*)begin
       case(op)
            3'b000: begin  F_out=A_in+B_in; end //ADD
            3'b001: begin  F_out=A_in-B_in; end //SUB
            3'b010: begin  F_out=(A_in<B_in)? 32'b1: 32'b0; end  //SLT
            3'b011: begin  F_out=A_in&B_in; end //AND
            3'b100: begin  F_out=A_in|B_in; end //OR
            3'b101: begin  F_out=F_out; end  //NOP 、J
        endcase
end
endmodule
2)部件功能

根据需要,为输入的32位数据iA_in与B_in进行相应的运算,并将运算的结果通过F_out输出,不考虑进位。根据本project需要实现的MIPS指令,该模块可实现+、-、&、|、大小比较5种运算。在CPU中,可以对regfile文件中存储的数值进行运算,也可以对扩展后的立即数与regfile文件中存储的数值进行运算已达到寻址的功能。

3)实现思路

模块工作时,当op发生改变时,通过case语句对op进行判断,以采取相应的运算,其具体对应关系如代码所示。

4)仿真代码
module testbench();
reg [31:0] A_in;
reg [31:0] B_in;
reg [2:0] op;
wire [31:0] F_out;
ALU alu(A_in,B_in,F_out,op);
initial 
begin
A_in=32'b0000_0000_0000_0000_0000_0000_0000_1001;
B_in=32'b0000_0000_0000_0000_0000_0000_0000_0110;
op=3'b000;
#10
op=3'b001;
#10
op=3'b010;
#10
op=3'b011;
#10
op=3'b100;
#10
op=3'b101;
end
endmodule
5)仿真波形图

在这里插入图片描述

6)仿真说明

由仿真文件可以看到,初始化时,A_in=1001,B_in=110。每次改变op的值,则选择不同的运算,并将最终正确的运算结果通过F_out输出。当op=101时,F_out保持不变。部件验证正确。

6、5位二选一部件MUX5

1)代码
module MUX5(control,in1,in0,out);
input control;
input [4:0] in1;
input [4:0] in0;
output [4:0] out;
assign out = control ? in1 : in0;
endmodule
2)部件功能

根据需要,在两个输入in1与in0中选择其中一个进行输出。该部件在CPU中,主要负责对regfile的输入地址write_reg进行选择。

3)实现思路

若control=1,输出in1,反之输出in0。

4)仿真代码
module testbench();
reg control;
reg [4:0] in1;
reg [4:0] in0;
wire [4:0] out;
MUX5 mux5(control,in1,in0,out);
initial 
begin
in1=5'b0_1001;
in0=5'b0_0110;
control=0;
#10
control=1;
end
endmodule
5)仿真波形图

在这里插入图片描述

6)仿真说明

由仿真文件可以看到,初始化时in1=5’b0_1001,in0=5’b0_0110。当control=1时,输出in1;当control=0时,输出in0。部件验证正确。

7、32位二选一部件MUX32

1)代码
module MUX32(control,in1,in0,out);
input control;
input [31:0] in1;
input [31:0] in0;
output [31:0] out;
assign out = control ? in1 : in0;
endmodule
2)部件功能

根据需要,在两个输入in1与in0中选择其中一个进行输出。该部件在CPU中,负责对regfile的输入数据write_data进行选择,也负责对ALU的输入B_in进行选择。

3)实现思路

若control=1,输出in1,反之输出in2。

4)仿真代码
module testbench();
reg control;
reg [31:0] in1;
reg [31:0] in0;
wire [31:0] out;
MUX32 mux32(control,in1,in0,out);
initial 
begin
in1=32'b0000_0000_0000_0000_0000_0000_0000_1001;
in0=32'b0000_0000_0000_0000_0000_0000_0000_0110;
control=0;
#10
control=1;
end
endmodule
5)仿真波形图

在这里插入图片描述

6)仿真说明

由仿真文件可以看到,初始化时in1=32’b0000_0000_0000_0000_0000_0000_0000_1001、
in0=32’b0000_0000_0000_0000_0000_0000_0000_0110。当control=1时,输出in1;当control=0时,输出in0。部件验证正确。

8、ALU译码器Aludecoder

1)代码
module ALUDECODER(funct,op,ALUOp);
input [5:0]funct;
input [2:0]ALUOp;
output reg [2:0]op;
always@(*)
case(ALUOp)
3'b000: begin
         case(funct)
          6'b100000: op=3'b000; //ADD
          6'b100010: op=3'b001; //SUB
          6'b101010: op=3'b010; //SLT
          6'b100100: op=3'b011; //AND
          6'b100101: op=3'b100; //OR
          6'b000000: op=3'b101; //NOP
          default: op=3'b101; //DEFAULT
          endcase
         end
3'b001: begin  op=3'b000; end  //ADDI         
3'b010: begin  op=3'b011; end  //ANDI  
3'b011: begin  op=3'b100; end  //ORI 
3'b100: begin  op=3'b010; end  //SLTI
3'b101: begin  op=3'b000; end  //lw
3'b110: begin  op=3'b000; end  //sw
3'b111: begin  op=3'b101; end  //j
endcase
endmodule
2)部件功能

在CPU中,可以根据ALUOp指令与funct的值,对ALU发出对应的运算指令,从而保障CPU的正常工作。

3)实现思路

通过case语句,为不同ALUOp指令值的情况匹配不同的运算。由于R型指令的op值都为0,ALUOp的值也相同。因此,在ALUOp=3‘b000的情况下,通过case语句,为不同funct值匹配不同的运算。case语句的对应关系如代码所示。

4)仿真代码
module testbench();
reg [5:0]funct;
reg [2:0]ALUOp;
wire [2:0]op;
ALUDECODER aludecoder(funct,op,ALUOp);
initial 
begin
#10
ALUOp=3'b000;
funct=6'b100000;
#10
ALUOp=3'b000;
funct=6'b100010;
#10
ALUOp=3'b000;
funct=6'b101010;
#10
ALUOp=3'b000;
funct=6'b100100;
#10
ALUOp=3'b000;
funct=6'b100101;
#10
ALUOp=3'b000;
funct=6'b000000;
#10
ALUOp=3'b001;
#10
ALUOp=3'b010;
#10
ALUOp=3'b011;
#10
ALUOp=3'b100;
#10
ALUOp=3'b101;
#10
ALUOp=3'b110;
#10
ALUOp=3'b111;
end
endmodule
5)仿真波形图

在这里插入图片描述

6)仿真说明

由波形图可以看出,在不同的funct、ALUOp的组合下,op输出了相应的正确指令。此处不赘述funct、ALUOp与op的对应关系,详细信息请看源代码。该部件验证正确。

9、控制信号单元Controlunit

1)代码
module CONTROUNIT(
    //输入
    input clk,
    input [5:0] op,         // op操作符
     // 控制信号输出
    output reg PCSrc,           // 0时,pc+4,1时,pc伪寻址
    output reg PCWre,           // PC是否更改,如果为0,PC不更改
    output reg ALUSrcB,         // 多路选择器,ALU运算结果与memory数据的选择,1为alu的运算结果,0为memory数据
    output reg ALUM2Reg,        // 多路选择器,1为Q2,0为立即数
    output reg RegWre,          // (RF)写使能信号,为1时写入
    output reg DataMemRW,       // (DM)数据存储器读写控制信号,为1写,为0读
    output reg ExtSel,          // (EXT)控制补位,如果为1,进行符号扩展,如果为0,全补0
    output reg RegOut,          // rt与rd的选择,1为rd,0为rt
    output reg [2:0] ALUOp      // (ALU)ALU操作控制 
);
initial
begin
PCWre = 0;
ALUSrcB = 0;
ALUM2Reg = 0;
RegWre = 0;
DataMemRW = 0;
ExtSel = 0;
PCSrc = 0;
RegOut = 0;
ALUOp = 3'b111;
end
always@(*)
    begin  
      case(op) 
            
            // R型指令add sub,and,or,slt,nop
            6'b000000:
          begin  
                PCWre = 1;
                ALUSrcB = 1;
                ALUM2Reg = 1;
                RegWre = 1;
                DataMemRW = 0;
                ExtSel = 0;
                PCSrc = 0;
                RegOut = 1;
                ALUOp = 3'b000;
          end
             
          // addi
            6'b001000:
              begin   
                  PCWre = 1;
                  ALUSrcB = 1;
                  ALUM2Reg = 0;
                  RegWre = 1;
                  DataMemRW = 0;
                  ExtSel = 1;
                  PCSrc = 0;
                  RegOut = 0;
                  ALUOp = 3'b001;
             end
             
             // andi
         6'b001100:
                begin   
                PCWre = 1;
                ALUSrcB = 1;
                ALUM2Reg = 0;
                RegWre = 1;
                DataMemRW = 0;
                ExtSel = 0;
                PCSrc = 0;
                RegOut = 0;
                ALUOp = 3'b010;
          end
         
            // ori
            6'b001101:
             begin   
                PCWre = 1;
                ALUSrcB = 1;
                ALUM2Reg = 0;
                RegWre = 1;
                DataMemRW = 0;
                ExtSel = 0;
                PCSrc = 0;
                RegOut = 0;
                ALUOp = 3'b011;
             end
           
           // slti
         6'b001010:
          begin   
                PCWre = 1;
                ALUSrcB = 1;
                ALUM2Reg = 0;
                RegWre = 1;
                DataMemRW = 0;
                ExtSel = 0;
                PCSrc = 0;
                RegOut = 0;
                ALUOp = 3'b100;
          end
          
            // sw写
            6'b101011:
          begin   
                PCWre = 1;
                ALUSrcB = 0;
                ALUM2Reg = 0;
                RegWre = 1;
                DataMemRW = 0;
                ExtSel = 1;
                PCSrc = 0;
                RegOut = 0;
                ALUOp = 3'b101;
          end
          
         // lw读
            6'b100011:
          begin   
                PCWre = 1;
                ALUSrcB = 1;
                ALUM2Reg = 0;
                RegWre = 0;
                DataMemRW = 1;
                ExtSel = 1;
                PCSrc = 0;
                RegOut = 0;
                ALUOp = 3'b110;
          end
          
         // j
           6'b000010:
          begin   
                PCWre = 1;
                ALUSrcB = 0;
                ALUM2Reg = 0;
                RegWre = 0;
                DataMemRW = 0;
                ExtSel = 0;
                PCSrc = 1;
                RegOut = 0;
                ALUOp = 3'b111;
          end
     endcase
   end
endmodule
2)部件功能

根据MIPS指令中op值,对CPU中的各个部件发出对应的工作指令。

3)实现思路

通过case语句,为不同op值的情况匹配不同的控制型号组合。

4)指令表
指令功能
PCWre控制pc的改变:若值为1,pc正常改变;若值为0,pc保持
ALUSrcB控制32位2x1选择器:若值为1,选择ALU运算结果;若值为0,选择Data Memory存储的数据
ALUM2Reg控制32位2x1选择器:若值为1,选择reg2_data;若值为0,选择扩展后的立即数extendImmediate
RegWre控制regfile的读写操作:若值为1,写入数据;若值为0,不进行写入
DataMemRW控制数据存储器(DM)的读写操作:若值为1,写入数据;若值为0,读出数据
ExtSel控制立即数扩展操作:若值为1,进行符号扩展;若值为0,进行无符号扩展
PCSrc控制pc的寻址操作:若值为1,pc进行伪寻址;若值为0,pc+4
RegOut控制5位2x1选择器:若值为1,选择;若值为0,选择rt
ALUOp控制ALUdecoder,从而选择不同的运算操作,具体对应关系见代码
5)MIPS指令控制型号组合
MIPS指令PCWreALUSrcBALUM2RegRegWreDataMemRWExtSelPCSrcRegOutALUOp
nop10000000000
add11110001000
sub11110001000
and11110001000
or11110001000
slt11110001000
addi11010100001
andi11010000010
ori11010000011
slti11010000100
sw10010100101
lw11001100110
j10000010111
6)仿真代码
module testbench();
reg clk;
reg [5:0] op;   
wire PCSrc;       
wire PCWre;     
wire ALUSrcB;   
wire ALUM2Reg;     
wire RegWre;     
wire DataMemRW;  
wire ExtSel;
wire RegOut;    
wire [2:0] ALUOp;
CONTROUNIT controunit(clk,op,PCSrc,PCWre,ALUSrcB,ALUM2Reg,RegWre,DataMemRW,ExtSel,RegOut,ALUOp);
always #10 clk=~clk; 
initial 
begin
clk=0;
#10
op=6'b000000;
#10
op=6'b001000; 
#10
op=6'b001100; 
#10
op=6'b001101; 
#10
op=6'b001010; 
#10
op=6'b100011; 
#10
op=6'b101011; 
#10
op=6'b000010; 
end
endmodule
7)仿真波形图

在这里插入图片描述

8)仿真说明

由波形图可以看出,对于不同的op值,程序正确输出了不同的控制信号组合。至于不同的op与各个信号的对应关系,详细信息请看源代码。部件验证正确。

10、指令存储器Imem

1)代码
module IMEM(IADDR,op,rs,rt,rd,sa,funct,immed,target);
parameter width=32;
input [width-1:0] IADDR;
output reg [5:0] op;
output reg [4:0] rs;
output reg [4:0] rt;
output reg [4:0] rd;
output reg [4:0] sa;
output reg [5:0] funct;
output reg [15:0] immed;
output reg [25:0] target;
reg [7:0]RAM[63:0];//32位宽,32位深的存储单位RAM。
initial
begin
RAM[0]=8'b00000000;
RAM[1]=8'b00100010;
RAM[2]=8'b11111000;
RAM[3]=8'b00100000;
RAM[4]=8'b00000000;
RAM[5]=8'b10000011;
RAM[6]=8'b11111000;
RAM[7]=8'b00100010;
RAM[8]=8'b00000000;
RAM[9]=8'b11000101;
RAM[10]=8'b11111000;
RAM[11]=8'b00100100;
RAM[12]=8'b00000000;
RAM[13]=8'b11101000;
RAM[14]=8'b11111000;
RAM[15]=8'b00100101;
RAM[16]=8'b00000001;
RAM[17]=8'b00101010;
RAM[18]=8'b11111000;
RAM[19]=8'b00101010;
RAM[20]=8'b00100001;
RAM[21]=8'b01111111;
RAM[22]=8'b00000000;
RAM[23]=8'b00000100;
RAM[24]=8'b00110001;
RAM[25]=8'b10011111;
RAM[26]=8'b00000000;
RAM[27]=8'b00001000;
RAM[28]=8'b00110101;
RAM[29]=8'b10111111;
RAM[30]=8'b00000000;
RAM[31]=8'b00001100;
RAM[32]=8'b00101001;
RAM[33]=8'b11011111;
RAM[34]=8'b00000000;
RAM[35]=8'b00010000;
RAM[36]=8'b10101100;
RAM[37]=8'b01001111;
RAM[38]=8'b00000000;
RAM[39]=8'b00000100;
RAM[40]=8'b10001110;
RAM[41]=8'b00011111;
RAM[42]=8'b00000000;
RAM[43]=8'b00001000;
RAM[44]=8'b00001000;
RAM[45]=8'b00000000;
RAM[46]=8'b00000000;
RAM[47]=8'b00001101;
RAM[48]=8'b00000000;
RAM[49]=8'b00000000;
RAM[50]=8'b00000000;
RAM[51]=8'b00000000;
RAM[52]=8'b00000000;
RAM[53]=8'b00000000;
RAM[54]=8'b00000000;
RAM[55]=8'b00000000;
RAM[56]=8'b00000000;
RAM[57]=8'b00000000;
RAM[58]=8'b00000000;
RAM[59]=8'b00000000;
RAM[60]=8'b00000000;
RAM[61]=8'b00000000;
RAM[62]=8'b00000000;
RAM[63]=8'b00000000;
end
always@(*)
begin
//输出。
op=RAM[IADDR][7:2];
rs[4:3]=RAM[IADDR][1:0];
rs[2:0]=RAM[IADDR+1][7:5];
rt=RAM[IADDR+1][4:0];
rd=RAM[IADDR+2][7:3];
sa[4:2]=RAM[IADDR+2][2:0];
sa[1:0]=RAM[IADDR+3][7:6];
funct=RAM[IADDR+3][5:0];
immed[15:8]=RAM[IADDR+2];
immed[7:0]=RAM[IADDR+3];
target[25:24]=RAM[IADDR][1:0];
target[23:16]=RAM[IADDR+1];
target[15:8]=RAM[IADDR+2];
target[7:0]=RAM[IADDR+3];
end
endmodule
2)部件功能

在CPU中,根据IADDR(pc)的值,输出对应的MIPS指令,从而使得CPU正常运行。

3)实现思路

模块中定义了一个8位宽、64位深的数组RAM。初始化时,为数组赋初值,即本cpu需要运行的全部MIPS指令。工作时,根据IADDR(pc)的值,就可以找到对应的MIPS指令,并将MIPS指令不同的功能区段,分别通过op、rs、rt、rd、sa、funct、immed、target进行输出。

4)MIPS指令说明
//在本cpu中,依次需要执行以下指令
//add $1,$2 $31
000000 00001 00010 11111 00000 100000
//sub $4, $3, $31
000000 00100 00011 11111 00000 100010
//and $6, $5, $31
000000 00110 00101 11111 00000 100100
//or $7, $8, $31
000000 00111 01000 11111 00000 100101
//slt $9, $10, $31
000000 01001 01010 11111 00000 101011
//addi $11, $31, 4
001000 01011 11111 0000000000000100
//andi $12, $31, 8
001100 01100 11111 0000000000001000
//ori $13, $31, 12	
001101 01101 11111 0000000000001100
//slti $14, $31, 16	
001010 01110 11111 0000000000010000
//sw $2, $15  ,4
101011 00010 01111 0000000000000100
//lw $16, $31  ,8
100011 10000 11111 0000000000001000
//j  adress13
000010 00000000000000000000001101
//nop
000000 00000000000000000000000000
//nop
000000 00000000000000000000000000
//nop
000000 00000000000000000000000000
//nop
000000 00000000000000000000000000

//依次以每8位为一组,初始化到[7:0]RAM[63:0]中

00000000 00100010 11111000 00100000 00000000 10000011 11111000 00100010 00000000 11000101 11111000 00100100 00000000 11101000 11111000 00100101 00000001 00101010 11111000 00101011 00100001 01111111 00000000 00000100 00110001 10011111 00000000 00001000 00110101 10111111 00000000 00001100 00101001 11011111 00000000 00010000 10101100 01001111 00000000 00000100 10001110 00011111 00000000 00001000 00001000 00000000 00000000 00001101 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
5)仿真代码
module testbench();
reg [31:0]IADDR;
wire[5:0] op;
wire [4:0] rs;
wire[4:0] rt;
wire [4:0] rd;
wire [4:0] sa;
wire[5:0] funct;
wire[15:0] immed;
wire[25:0] target;
integer i;
IMEM imem(IADDR,op,rs,rt,rd,sa,funct,immed,target);
initial 
begin
IADDR=0;
i=0;
while(i<16)begin 
      #10 IADDR=IADDR+4;
      i=i+1; end
end
endmodule
6)仿真波形图

在这里插入图片描述

7)仿真说明

由仿真图可以看出,对于不同的IADDR地址,程序正确输出了指令型号以及其对应的op、rs、rt、rd、sa、funct、immed、target。其具体的对应关系请看MIPS指令说明。部件验证正确。

11、数据内存Dmem

1)代码
module DMEM(DataMemRW,data_address,data_in,data_out);
input DataMemRW;
input [31:0]data_address,data_in;
output reg[31:0]data_out;
reg [7:0] memory[255:0];
integer i;
initial
 begin
for(i=0;i<256;i=i+1)
begin
    memory[i]=8'b0000_0000;
    memory[i+1]=8'b0000_0000;
    memory[i+2]=8'b0000_0000;
    memory[i+3]=8'b0000_0010;
end
always@(*) 
begin// 写内存
    if (DataMemRW) begin
           memory[data_address]=data_in[31:24];
           memory[data_address+1]=data_in[23:16];
           memory[data_address+2]=data_in[15:8];
           memory[data_address+3]=data_in[7:0]; end
        // 读内存
        else begin
            data_out[31:24]=memory[data_address];
            data_out[23:16]=memory[data_address+1];
            data_out[15:8]=memory[data_address+2];
            data_out[7:0]=memory[data_address+3];
            end
end
endmodule
2)部件功能

在CPU中,根据输入的data_address,读取数据内存中相应地址的值,写入Regfile;或通过Regfile,将值写入数据内存中相应地址。

3)实现思路

模块中定义了8位宽、256位深的二维数组memory。初始化模块时,为该模块的memory数组依次赋值为2,以便于仿真时确定代码的正确性。

模块工作时,若DataMemRW=1,则进行写入操作,将输入data_in的值赋值给memory;若DataMemRW=0,则进行读出操作,将memory的值通过data_out输出。

4)仿真代码
module testbench();
reg DataMemRW;
reg [31:0]data_address,data_in;
wire[31:0]data_out;
DMEM deme(DataMemRW,data_address,data_in,data_out);
initial 
begin
DataMemRW=0;
data_address=0;
#10 data_address=32'b0000_0000_0000_0000_0000_0000_0000_0001;
#10 data_address=32'b0000_0000_0000_0000_0000_0000_0000_0010;
#10 data_address=32'b0000_0000_0000_0000_0000_0000_0000_0100;
#10 data_address=32'b0000_0000_0000_0000_0000_0000_0000_1000;
#10 data_address=32'b0000_0000_0000_0000_0000_0000_0001_0000;
#10 data_address=32'b0000_0000_0000_0000_0000_0000_0010_0000;
#10 data_address=32'b0000_0000_0000_0000_0000_0000_0100_0000;
#10
DataMemRW=1;
data_in=32'b0000_0000_0000_0000_0000_0000_0001_0000;
data_address=32'b0000_0000_0000_0000_0000_0000_0000_0001;
end
endmodule
5)仿真波形图

在这里插入图片描述

6)仿真说明

由仿真文件可以看到:当DataMemRW=0时,模块执行读出操作,data_address分别对应0、1、2、4、8时,data_out也相应输出存储的数值2;当DataMemRW=1时,模块正确执行写入操作,写入数据data_in=00000000000000000000000000010000,写入数据的地址data_address=00000000000000000000000000000001。部件验证正确。

三、单周期CPU

1、CPU的功能

该CPU可以在单周期内实现给定的MIPS指令功能:add,sub,and,or,slt,addi,andi,ori,slti,sw,lw,j,nop13条MIPS指令;可以实现寄存器寻址,立即数寻址,基址偏移量寻址(寄存器内容与常数相加)、伪直接寻址(在跳转指令中,指令中的26位目标地址值与PC的高四位拼接,形成30位的存储器“字地址”)四种寻址方式;可以执行R(register)类型、I(immediate)类型、 J(jump)类型三种指令。

2、顶层文件top

1)代码
module TOP(clk,reset);
input clk,reset;
wire [31:0]pc_current,out3,q2,F_out,out1,q1,memdata1;
wire [4:0]rs,rt,rd,out2;
wire [5:0]funct,op;
wire [15:0]immediate;
wire [25:0]adress;
wire PCSrc,PCWre,ALUSrcB,ALUM2Reg,RegWre,ExtSel,RegOut,DataMemRW;
wire [2:0]ALUOp;
MIPS mips(clk,reset,rs,rt,rd,funct,op,memdata1,immediate,adress,F_out,pc_current,DataMemRW,q2);
IMEM imem(pc_current,op,rs,rt,rd,sa,funct,immediate,adress);
DMEM dmem(DataMemRW,F_out,q2,memdata1);
endmodule
2)电路图

在这里插入图片描述

3)模块实现

顶层模块由三个部分构成:MIPS、IMEM、DMEM。定义两个输入clk与reset。

3、MIPS模块

1)代码
module MIPS(clk,reset,rs,rt,rd,funct,op,memdata1,immediate,adress,F_out,pc_current,DataMemRW,q2);
input clk,reset;
input [4:0]rs,rt,rd;
input [5:0]funct,op;
input [31:0]memdata1;
input [15:0]immediate;
input [25:0]adress;
output [31:0] F_out,pc_current;
output DataMemRW;
output [31:0]q2;
wire PCSrc,PCWre,ALUSrcB,ALUM2Reg,RegWre,DataMemRW,ExtSel,RegOut;
wire [2:0]op_in;
DATAPATH datapath(clk,reset,PCSrc,PCWre,ALUSrcB,ALUM2Reg,RegWre,ExtSel,RegOut,op_in,rs,rt,rd,immediate,
adress,memdata1,F_out,pc_current,q2);
CONTROLER controler(clk,funct,op,op_in,PCSrc,PCWre,ALUSrcB,ALUM2Reg,RegWre,DataMemRW,ExtSel,RegOut);
endmodule
2)电路图

在这里插入图片描述

3)模块实现

MIPS模块由两个部分构成:controler以及datapath。定义输入clk、reset、rs、rt、rd、funct、op、memdata1、immediate、adress;定义输出F_out、pc_current、DataMemRW、q2。

4、datapath模块

1)代码
module DATAPATH(clk,reset,PCSrc,PCWre,ALUSrcB,ALUM2Reg,RegWre,ExtSel,RegOut,op_in,rs,rt,rd,immediate,adress,
memdata1,F_out,pc_current,q2);
input clk,reset,PCSrc,PCWre,ALUSrcB,ALUM2Reg,RegWre,ExtSel,RegOut;
input [4:0]rs,rt,rd;
input [15:0]immediate;
input [25:0]adress;
input [2:0]op_in;
input [31:0]memdata1;
wire [4:0]out2;
output [31:0]q2;
output [31:0] F_out,pc_current;
wire [31:0]pc_current,extendImmediate,q1,q2,pc_new,out3,out1,F_out;
PC pc(pc_new,pc_current,clk,reset,PCWre);
PCADDER pcadder(clk,pc_current,adress,PCSrc,pc_new);
REGIFILE regiflie(RegWre,clk,rs,rt,out3,q1,q2,out2);
EXTEND extend(ExtSel,immediate,extendImmediate);
ALU alu(q1,out1,F_out,op_in);
MUX5 mux51(RegOut,rd,rt,out2);//rt与rd的选择,1为rd,0为rt
MUX32 mux321(ALUM2Reg,q2,extendImmediate,out1); //立即数与Q2的选择,1为Q2,0为立即数
MUX32 mux322(ALUSrcB,F_out,memdata1,out3); //ALU运算结果与memory数据的选择,1为alu的运算结果,0为memory数据
endmodule
2)电路图

在这里插入图片描述

3)模块实现

datapath模块由八个部分构成:PC、PCADDER、REGIFILE、EXTEND、ALU、MUX5、MUX32(2)。定义输入clk、reset、PCSrc、PCWre、ALUSrcB、ALUM2Reg、RegWre、ExtSel、RegOut、rs、rt、rd、immediate、adress、op_in、memdata1;定义输出q2、 F_out、pc_current。

5、controler模块

1)代码
module CONTROLER(clk,funct,op,op_in,PCSrc,PCWre,ALUSrcB,ALUM2Reg,RegWre,DataMemRW,ExtSel,RegOut);
input clk;
input [5:0] funct,op;
output [2:0]op_in;
output PCSrc,PCWre,ALUSrcB,ALUM2Reg,RegWre,DataMemRW,ExtSel,RegOut;
wire [2:0]ALUOp;
CONTROUNIT controunit(clk,op,PCSrc,PCWre,ALUSrcB,ALUM2Reg,RegWre,DataMemRW,ExtSel,RegOut,ALUOp);
ALUDECODER aludecoder(funct,op_in,ALUOp);
endmodule
2)电路图

在这里插入图片描述

3)模块实现

controler模块由两个部分构成:controunit、aludecoder。定义输入clk、funct、op;定义输出op_in、PCSrc、PCWre、ALUSrcB、ALUM2Reg、RegWre、DataMemRW、ExtSel、RegOut。

6、仿真

1)testbench
module testbench_PJ();
reg clk,reset;
TOP top(clk,reset);
initial
begin reset=1;
      clk=0;
      #5 reset=0;
end
always #25 clk=~clk;
endmodule
2)仿真波形图

在这里插入图片描述

2)仿真说明

仿真开始时,首先对CPU进行初始化,使reset=1,然后再使reset=0。然后按照预先存储在Instruction Memory内部的指令顺序,依次对指令进行读取(从仿真截图中clk的第一个上升沿开始为第一个周期):

1、第一周期(add):
//add $1,$2 $31
000000 00001 00010 11111 00000 100000

执行操作为: rd <- rs + rt ,其中rs=$1,rt=$2, rd=$31。对应仿真文件图:A_in+B_in=F_out,即1+2=3。然后将F_out的值赋值到wate_data,即存储在了regfile地址为31的数组中。验证CPU运行正确。

2、第二周期(sub):
//sub $4, $3, $31
000000 00100 00011 11111 00000 100010

执行操作为: rd <- rs - rt,其中rs=$4,rt=$3,rd=$31。对应仿真文件图:A_in-B_in=F_out,即4-3=1。然后将F_out的值赋值到wate_data,即存储在了regfile地址为31的数组中。验证CPU运行正确。

3、第三周期(and):
//and $6, $5, $31
000000 00110 00101 11111 00000 100100

执行操作为: rd <- rs & rt,其中rs=$6,rt=$5, rd=$31。对应仿真文件图:A_in&B_in=F_out,即110&101=100。然后将F_out的值赋值到wate_data,即存储在了regfile地址为31的数组中。可验证CPU运行正确。

4、第四周期(or):
//or $7, $8, $31
000000 00111 01000 11111 00000 100101

执行操作为: rd <- rs | rt ,其中rs=$7,rt=$8, rd=$31。对应仿真文件图:A_in|B_in=F_out,即0111|1000=1111。然后将F_out的值赋值到wate_data,即存储在了regfile地址为31的数组中。验证CPU运行正确。

5、第五周期(slt):
//slt $9, $10, $31
000000 01001 01010 11111 00000 101010

执行操作为: if (rs < rt) rd=1; else rd=0;其中rs=$9,rt=$10, rd=$31。对应仿真文件图:(A_in<B_in),F_out=1,即10>9,F_out=1。然后将F_out的值赋值到wate_data,即存储在了regfile地址为31的数组中。验证CPU运行正确。

6、第六周期(addi):
//addi $11, $31, 4
001000 01011 11111 0000000000000100

执行操作为: rt <- rs + (sign-extend)immediate ;其中rs=$11,rt=$31。对应仿真文件图:A_in+extendimmediate=F_out,即11+4=15。然后将F_out的值赋值到wate_data,即存储在了regfile地址为31的数组中。验证CPU运行正确。

7、第七周期(andi):
//andi $12, $31, 8
001100 01100 11111 0000000000001000

执行操作为: rt <- rs & (zero-extend)immediate ;其中rs=$12,rt=$31。对应仿真文件图:A_in&extendimmediate=F_out,即1100&1000=1000。然后将F_out的值赋值到wate_data,即存储在了regfile地址为31的数组中。验证CPU运行正确。

8、第八周期(ori):
//ori $13, $31, 12	
001101 01101 11111 0000000000001100

执行操作为: rt <- rs | (zero-extend)immediate ;其中rt=$31,rs=$13。对应仿真文件图:A_in|extendimmediate=F_out,即1101|1100=1101。然后将F_out的值赋值到wate_data,即存储在了regfile地址为31的数组中。验证CPU运行正确。

9、第九周期(slti):
//slti $14, $31, 16	
001010 01110 11111 0000000000010000

执行操作为: if (rs <(sign-extend)immediate) rt=1;else rt=0 ;其中rs=$14,rt=$31。对应仿真文件图:A_in<extendimmediate; F_out=1,即1110<10000;F_out=1。然后将F_out的值赋值到wate_data,即存储在了regfile地址为31的数组中。验证CPU运行正确。

10、第十周期(sw):
//sw $2, $15 ,4
101011 00010 01111 0000000000000100

执行操作为:memory[rs + (sign-extend)immediate] <- rt ;rt=$15,rs=$2。对应仿真文件图:memory[A_in+extendimmediate]=memory[4+2]=memory[F_out]=memory[daya_adress]=memory[6]<-data_in=15,即将F_out的值作为memory的地址,将$15的值赋值到memory[F_out]。验证CPU运行正确。

11、第十一周期(lw):
//lw $16, $31  ,8
100011 10000 11111 0000000000001000

执行操作为: rt <- memory[rs + (sign-extend)immediate] ;rt=$31,rs=$16。对应仿真文件图: write_data=memory[A_in+extendimmediate]=memory[16+8]=memory[F_out]=memory[24]=data_out=2,即将F_out的值作为memory的地址,将memory[F_out]的值2通过data_out赋值到$31。验证CPU运行正确。

12、第十二周期(j):
//j  adress13
000010 00000000000000000000001101

执行操作为: PC <- {(PC+4)[31…28],address,0,0}。此时由于adress=13,pc=44,因此在伪寻址后,新pc的值为52。与仿真图中daress_in相符合,验证CPU运行正确。

13、第十三周期(nop):
//nop
000000 00000000000000000000000000

不执行任何操作,只有pc变化,由仿真波形图,验证CPU运行正确。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值