(verilog)基于MIPS指令集的单周期CPU设计

实验目的:

    本实验通过设计和实现一个基于MIPS指令集的单周期CPU,深入理解MIPS指令集架构和单周期CPU的工作原理,理解MIPS指令集的格式,编码以及寻址模式,包括R型,I型和J型指令的特点,利用硬件描述语言Verilog建立单周期CPU内部组成的模型,包括寄存器堆,算术逻辑单元,数据存储器和程序计数器,并描述他们在执行指令过程中的作用和数据流动。

关于计算机体系结构处理器部分内容可参考B站视频:

https://www.bilibili.com/video/BV1gP4y1o7fo/?spm_id_from=333.1007.top_right_bar_window_default_collection.content.click

实验原理:

MIPS指令:

MIPS(Microprocessor without Interlocked Pipeline Stages)是一种精简指令集计算机(RISC)架构,它以其简洁的指令集、高效的流水线设计和高性能而著称。MIPS指令集的设计哲学强调了指令的简单性和规则性,这有助于简化处理器的设计和提高指令的执行速度。

MIPS指令集的所有指令均为固定字长,通常为32位(4字节),分为三类:R型指令、I型指令和J型指令。

R型指令:R型指令用于寄存器之间的操作,如加减乘除等,指令格式中的操作码为000000。这种指令涉及到寄存器之间的操作,使用3个寄存器进行操作。这些指令的操作码字段为6位,rs、rt、rd、shamt和funct字段的长度为5位。其中,rs、rt分别表示源寄存器1和源寄存器2,rd表示目标寄存器,shamt字段用于移位操作,funct字段指定具体的操作类型。

表1 R型指令格式

6位操作码

5位rs

5位rt

5位rd

5位shamt

6位功能码

000000

$rs

$rt

$rd

shamt

funct

I型指令:I型指令用于取数和存数操作,立即数可以是有符号数或无符号数,立即数(immediate value)是指在指令中直接给出的数值,而不是从寄存器或内存中读取的数据。操作码根据不同指令而不同。这种指令涉及到立即数的操作,使用2个寄存器和一个立即数进行操作。这些指令的操作码字段为6位,rs、rt字段的长度为5位,immediate字段的长度为16位。其中,rs表示源寄存器,rt表示目标寄存器,immediate表示立即数。此外I型指令还包括计算类指令。I型计算类指令是指立即数计算类指令,其操作码为6位,rs字段代表第一个操作数寄存器,rt字段代表第二个操作数寄存器,立即数字段表示立即数操作数。这类指令执行时,先将第一个操作数从寄存器中取出,再将立即数或者第二个操作数从寄存器中取出,经过计算后将结果存储到目标寄存器中。

表2 I型指令格式

6位操作码

5位rs

5位rt

16位立即数

lw:6’d43

sw:6’d4

addi:6’d08

ori:6’d13

$rs

$rt

immediat

分支指令:涉及到条件分支的操作,使用1个寄存器和一个偏移量进行操作。这些指令的操作码字段为6位,rs、rt字段的长度为5位,offset字段的长度为16位。其中,rs表示源寄存器,offset表示偏移量,根据rs中的值进行条件分支操作。

表3 J型指令格式

6位操作码

5位rs

5位rt

16位立即数

000100

$rs

$rt

immediat

单周期CPU工作原理:

单周期(Single Cycle)CPU是指CPU从取出1条指令到执行完该指令只需1个时钟周期。一条指令的执行过程包括:取指令→指令译码→执行指令→存储器访问→结果写回。
下图是包含控制单元的简单数据通路:

以执行R型指令(add $t1,$t2,$t3)为例:

1)从指令存储器中取出指令,PC自增。

2)从寄存器堆中读出寄存器$t2和$t3。同时,主控制单元计算出各控制信号的状态。

3)ALU根据funct字段(指令的5:0位)确定ALU的功能,对从寄存器堆读出的数据进行操作。

4)将ALU的结果写入寄存器堆,根据指令的15:11位选择目标寄存器($t1)。

单周期CPU的具体实现:

本实验所设计的单周期CPU整体框架主要包括程序计数器(PC),指令存储器(InstMem),寄存器堆(Register_Files),数据存储器(DataMem),算数逻辑单元(ALU)以及控制单元(ControlUnit),此外还有四个多选器用于控制数据流向,加法器用于地址运算,扩展单元用于将指令的[15:0]位扩展至32位用于数据偏移计算。单周期CPU设计的整体数据通路如下图:

程序计数器PC:

PC为时序逻辑,在时钟上升沿到来时,将地址传递出去

module PC (
    input sys_clk,
    input sys_rst_n,
    input [31:0] Address_in,
    
    output reg [31:0] Address
);

    initial begin
        Address <= 32'b0;
    end
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n)
        Address <= 32'b0;
        else Address <= Address_in;
    end
endmodule

指令存储器InstMem:

这部分参考了https://blog.csdn.net/Accelerato/article/details/86546751的代码,方便后续的仿真验证,在这一模块设置32x32的rom将地址与指令对应;

module InstMeM (
    input [31:0] Address,
    
    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
    assign Rom[5'h05]=32'h00223025;//or $6,$1,$2
    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]=12
    assign Rom[5'h0E]=32'h8D04000A;//lw $4 10($8) $4=12
    assign Rom[5'h0F]=32'h10440003;//beq $2,$4,3
    assign Rom[5'h10]=32'hXXXXXXXX;
    assign Rom[5'h11]=32'hXXXXXXXX;
    assign Rom[5'h12]=32'hXXXXXXXX;
    assign Rom[5'h13]=32'h30470009;//andi $2,9,$7
    assign Rom[5'h14]=32'hXXXXXXXX;
    assign Rom[5'h15]=32'hXXXXXXXX;
    assign Rom[5'h16]=32'hXXXXXXXX;
    assign Rom[5'h17]=32'hXXXXXXXX;
    assign Rom[5'h18]=32'hXXXXXXXX;
    assign Rom[5'h19]=32'hXXXXXXXX;
    assign Rom[5'h1A]=32'hXXXXXXXX;
    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[Address[6:2]];

endmodule

寄存器堆Register_Files:

RegisterFile中定义了一个含有32个寄存器的寄存器组,设计逻辑包括两部分:一方面,读寄存器组为组合逻辑,当输入一个5位长的寄存器编号时,输出对应寄存器中的数值。另一方面,写寄存器组是时序逻辑,在时钟下降沿到来时触发写入。

module Register_Files (
    input sys_clk,
    input sys_rst_n,
    input RegWrite,
    input [4:0] ReadReg1,
    input [4:0] ReadReg2,
    input [4:0] WriteReg,
    input [31:0] WriteData,

    output [31:0] ReadData1,
    output [31:0] ReadData2
);
    reg [31:0] regfile [31:0];
    integer i;

    assign ReadData1 = regfile[ReadReg1];
    assign ReadData2 = regfile[ReadReg2];

    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n)begin
            for(i = 0;i < 32;i = i + 1)
            regfile[i] <= 0;
        end
        else if((RegWrite == 1) && (WriteReg != 0)) begin
            regfile[WriteReg] <= WriteData;
        end 
    end

endmodule

控制单元ControlUnit:

根据指令的操作码[31:26]位确定控制信号,其中

RegDst:写寄存器目标寄存器号是RT还是RD

ALUSrc:ALU第二个操作数来自读数readdata2还是16位扩充数据

PCsrc:由Branch与zero信号进行与操作下次PC地址是PC+4还是分支指令

ALUop:ALU计算单元的控制号

MemToreg:写入寄存器的数据来源是ALU结果还是存储器数据

对应参数如下:

操作码指令RegDstBranchMemReadMemToRegALUopMemWriteALUScRegWrite
0add100010001
sub100010000
and100010000
or100010000
slt100010000
35lw00110011
43sw000x0110
4beq01001000
8addi000000011
13ori000011011
module ControlUnit (
    input [5:0] opcode,

    output reg RegDst,
    output reg Branch,
    output reg MemRead,
    output reg MemToreg,
    output reg [1:0] ALUop,
    output reg MemWrite,
    output reg ALUSrc,
    output reg RegWrite
);
//根据操作码确定控制信号;
//RegDst:写寄存器目标寄存器号是RT还是RD
//ALUSrc:ALU第二个操作数来自读数readdata2还是16位扩充数据
//PCsrc:由Branch与zero信号进行与操作下次PC地址是PC+4还是分支指令
//ALUop:ALU计算单元的控制号
//MemToreg:写入寄存器的数据来源是ALU结果还是存储器数据
    always @(opcode) begin
       RegDst   = 0;    
       Branch   = 0;    
       MemRead  = 0;    
       MemToreg = 0;    
       ALUop    = 0;  
       MemWrite = 0;    
       ALUSrc   = 0;    
       RegWrite = 0;   
       case(opcode)
       6'd0:  begin
        RegDst     =  1;        
        Branch     =  0;   
        MemRead    =  0 ;  
        MemToreg   =  0 ; 
        ALUop      =  2'b10 ;  
        MemWrite   =  0 ; 
        ALUSrc     =  0 ;  
        RegWrite   =  1 ;
       end //R型运算
        6'd8:  begin
        RegDst     =  0;        
        Branch     =  0;   
        MemRead    =  0 ;  
        MemToreg   =  0 ; 
        ALUop      =  2'b00 ;  
        MemWrite   =  0 ; 
        ALUSrc     =  1 ;  
        RegWrite   =  1 ;
       end   //addi运算
        6'd13:  begin
        RegDst     =  0;        
        Branch     =  0;   
        MemRead    =  0 ;  
        MemToreg   =  0 ; 
        ALUop      =  2'b11 ;  
        MemWrite   =  0 ; 
        ALUSrc     =  1 ;  
        RegWrite   =  1 ;
       end //ori运算
       6'd35:  begin
        RegDst     =  0;        
        Branch     =  0;   
        MemRead    =  1 ;  
        MemToreg   =  1 ; 
        ALUop      =  2'b00 ;  
        MemWrite   =  0 ; 
        ALUSrc     =  1 ;  
        RegWrite   =  1 ;
       end //lw指令
       6'd43:  begin
        RegDst     =  0;        
        Branch     =  0;   
        MemRead    =  0 ;  
        MemToreg   =  0 ; 
        ALUop      =  2'b00 ;  
        MemWrite   =  1 ; 
        ALUSrc     =  1 ;  
        RegWrite   =  0 ;
       end   //sw指令 
        6'd4:  begin
        RegDst     =  0;        
        Branch     =  1;   
        MemRead    =  0 ;  
        MemToreg   =  0 ; 
        ALUop      =  2'b01 ;  
        MemWrite   =  0 ; 
        ALUSrc     =  0 ;  
        RegWrite   =  0 ;
       end //分支指令
       endcase  
    end
endmodule

ALU控制单元:

该模块功能是根据ALUop以及指令的[5:0]功能位确定ALU进行的运算操作:

module ALUCtrl (
    input [5:0] Inst5_0,
    input [1:0] ALUop,

    output reg [3:0] ALUopcode
);
//根据操作码及功能码确定ALU操作码
    always @(Inst5_0 or ALUop) begin
        ALUopcode = 4'b0000;
        case(ALUop)
        2'b00: ALUopcode = 4'b0010;//存取指令
        2'b01: ALUopcode = 4'b0110;//分支指令
        2'b10:begin                //R型指令
            case(Inst5_0)
            6'b100_000:ALUopcode = 4'b0010;//加法
            6'b100_010:ALUopcode = 4'b0110;//减法
            6'b100_100:ALUopcode = 4'b0000;//与操作
            6'b100_101:ALUopcode = 4'b0001;//或操作
            6'b101_010:ALUopcode = 4'b0111;//小于则置位
            endcase
        end
        2'b11:ALUopcode = 4'b0001;
        endcase
    end
endmodule

ALU运算单元:

根据上述ALU控制单元输出的控制信号确定当前进行的运算;

module ALU(
    input [3:0] ALUopcode,
    input [31:0] rega,
    input [31:0] regb,

    output reg [31:0] result,
    output zero
);//根据ALU操作码,确定ALU运算

assign zero = (result == 0) ? 1 : 0;

always @(ALUopcode or rega or regb) begin
    result = 32'b0;
    case(ALUopcode)
    4'b0000: result = rega & regb;//与操作
    4'b0001: result = rega | regb;//或操作
    4'b0010: result = rega + regb;//加法运算
    4'b0110: result = rega - regb;//减法运算
    4'b0111: result = (rega < regb) ? 1 : 0;//小于置位操作
    4'b1100: result = ~(rega | regb) ;//或非操作
    endcase
end

endmodule

数据存储器DataMem:

DataMemory中包含32x32的ram,设计逻辑包括两部分:一方面,读存储器为组合逻辑,当输入一个32位长的数据地址时,输出对应地址中的32位数据。另一方面,写存储器是时序逻辑,在时钟上升沿到来时触发写入。

module DataMeM (
    input [31:0] MEM_Address,
    input [31:0] Din,
    input sys_clk,
    input MemRead,
    input MemWrite,

    output [31:0] Dout
);
    reg [31:0] Ram [31:0];
    assign Dout = Ram[MEM_Address[6:2]];

    always @(posedge sys_clk) begin
        if(MemWrite)
        Ram[MEM_Address[6:2]] <= Din;
    end
    integer i;
    initial begin
        for(i = 0;i < 32; i = i + 1)
        Ram[i] = 0;
    end
endmodule

除了上述几个模块之外,还有四个多选器,

muxAluSrc:确定ALU的其中一位输入(另一位输入是寄存器堆读数据1)是来自寄存器读数据2(R型指令)还是指令[15:0]经过扩展成32位的数据(I型指令);

muxPCSrc:下一指令地址是PC+4还是PC+4+偏移量(分支指令);

muxRegDst:写入寄存器号rs;1:写入寄存器号rd;

muxMemtoReg:将存储器输出或者ALU计算结果写入寄存器堆中;

上述多选器实现简单,使用三元运算符  ”?:"  即可实现;

顶层模块:

module single_cpu_top(
    input sys_clk,
    input sys_rst_n,
    input [31:0] Address_in,
    output [31:0] NowIns,//当前指令
    output [31:0] next_PC,//下一条指令地址
    output [31:0] DataMeM_Dout,//数据存储器输出数据
    output [31:0] ALU_Dout//ALU运算结果输出
);

wire [31:0] Address;
PC PC_inst(
    .sys_clk(sys_clk),
    .sys_rst_n(sys_rst_n),
    .Address_in(Address_in),

    .Address(Address)
);

wire [31:0] Inst;
InstMeM InstMeM_inst(
    .Address(Address),

    .Inst(Inst)
);

assign NowIns = Inst;

wire RegDst;
wire Branch;
wire MemRead;
wire MemToreg;
wire [1:0] ALUop;
wire MemWrite;
wire ALUSrc;
wire RegWrite;
ControlUnit ControlUnit_inst(
    .opcode(Inst[31:26]),

    .RegDst(RegDst),
    .Branch(Branch),
    .MemRead(MemRead),
    .MemToreg(MemToreg),
    .ALUop(ALUop),
    .MemWrite(MemWrite),
    .ALUSrc(ALUSrc),
    .RegWrite(RegWrite)
);

wire [31:0] ReadData1;
wire [31:0] ReadData2;
wire [4:0] muxRegDst_dout;
wire [31:0] muxMemtoReg_dout;
muxRegDst muxRegDst_inst(
    .Inst20_16(Inst[20:16]),
    .Inst15_11(Inst[15:11]),
    .RegDst(RegDst),

    .muxRegDst_dout(muxRegDst_dout)
);
Register_Files Register_Files_inst(
    .sys_clk(sys_clk),
    .sys_rst_n(sys_rst_n),
    .RegWrite(RegWrite),
    .ReadReg1(Inst[25:21]),
    .ReadReg2(Inst[20:16]),
    .WriteReg(muxRegDst_dout),
    .WriteData(muxMemtoReg_dout),

    .ReadData1(ReadData1),
    .ReadData2(ReadData2)
);

wire [31:0] data_ex;
sign_ex sign_ex_inst(
    .data_in(Inst[15:0]),
    .data_ex(data_ex)
);

wire [31:0] muxAluSrc_dout;
muxAluSrc muxAluSrc_inst(
    .ReadData2(ReadData2),
    .data_ex(data_ex),
    .ALUSrc(ALUSrc),

    .muxAluSrc_dout(muxAluSrc_dout)
);

wire [3:0] ALUopcode;
ALUCtrl ALUCtrl_inst(
    .Inst5_0(Inst[5:0]),
    .ALUop(ALUop),

    .ALUopcode(ALUopcode)
);

wire [31:0] result;
wire zero;
ALU ALU_inst(
    .ALUopcode(ALUopcode),
    .rega(ReadData1),
    .regb(muxAluSrc_dout),

    .result(result),
    .zero(zero)
);

wire [31:0] Dout;
DataMeM DataMeM_inst(
    .MEM_Address(result),
    .Din(ReadData2),
    .sys_clk(sys_clk),
    .MemRead(MemRead),
    .MemWrite(MemWrite),

    .Dout(Dout)
);


muxMemtoReg muxMemtoReg_inst(
    .Dout(Dout),
    .result(result),
    .MemToreg(MemToreg),

    .muxMemtoReg_dout(muxMemtoReg_dout)
);

wire [31:0] Address_Add;
PC_Add4 PC_Add4_inst(
    .Address(Address),

    .Address_Add(Address_Add)
);
wire [31:0] data_ex_left2;
assign data_ex_left2 = data_ex << 2;

wire [31:0] PC_ALU;
PC_ADD_ADD PC_ADD_ADD_inst(
    .Address_Add(Address_Add),
    .data_ex(data_ex_left2),

    .PC_ALU(PC_ALU)
);

assign PCSrc = Branch & zero;

wire [31:0] muxPCSrc_dout;
muxPCSrc muxPCSrc_inst(
    .Address_Add(Address_Add),
    .PC_ALU(PC_ALU),
    .PCSrc(PCSrc),

    .muxPCSrc_dout(muxPCSrc_dout)
);

assign next_PC = muxPCSrc_dout;
assign DataMeM_Dout = muxMemtoReg_dout;
assign ALU_Dout = result;

endmodule

仿真结果:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值