单周期CPU——verilog语言实现

时间:2024-03-07 22:07:54

一. 实验内容

设计一个单周期CPU,要求:

1. 实现MIPS的20条指令

2. 在该CPU上实现斐波那契函数

 

计算机每执行一条指令都可分为三个阶段进行。即取指令(IF)——>分析指令(ID)——>执行指令(EXE)

取指令:根据程序计数器PC中的指令地址,从存储器中取出一条指令,同时,根据控制信号,决定选择某个来源的指令地址作为下一条指令的地址。

分析指令:对取指令操作中得到的指令进行分析并译码,确定这条指令需要完成的操作,从而产生相应的操作控制信号,用于驱动执行状态中的各种操作。

执行指令:根据指令译码得到的操作控制信号,具体地执行指令动作。

 

 

二. 20条指令的情况

R指令

 

 

I指令

 

J指令

 

 

三. 取指令(IF)的相关模块代码

//PC
module PC(
    input [31:0] next_addr,
    input rst,
    input clk,
    output reg [31:0] addr
    );
    
    always @(posedge clk)
    begin
        if(rst==1\'b1)
        begin
            addr<=next_addr;
        end
        else
        begin
            addr<=32\'b0;
        end
    end
    
    initial
        $monitor($time,,"PC:addr=%h",addr);
    
endmodule




//ROM
module rom(
    input [31:0] addr,
    output [31:0] data
    );
    
    reg[31:0] romdata;
    
    always @(*)
    case(addr[31:2])
        4\'h0:romdata=32\'b10001100000000110000000000100000;      //lw $0,$3,32  *
        4\'h1:romdata=32\'b00110100000100000000000000000010;      //ori $0,$16,2   *
        4\'h2:romdata=32\'b00000000000000111000100000100101;      //or $0,$3,$17   *
        4\'h3:romdata=32\'b00110100000100110000000000000001;      //ori $0,$19,1   *
        4\'h4:romdata=32\'b00110100000001000000000000000001;      //ori $0,$4,1     *
        4\'h5:romdata=32\'b00010010001100110000000000001011;      //beq $17,$19,11  *
        4\'h6:romdata=32\'b00000000000001000100000000100101;      //or $0,$4,$8   *
        4\'h7:romdata=32\'b00100010011100110000000000000001;      //addi $19,$19,1   *
        4\'h8:romdata=32\'b00110100000001000000000000000001;      //ori $0,$4,1     *
        4\'h9:romdata=32\'b00010010001100110000000000000111;      //beq $17,$19,7   *
        4\'ha:romdata=32\'b00000000000001000100100000100101;      //or $0,$4,$9     *
        
        4\'hb:romdata=32\'b00000001000010010010000000100000;      //add $8,$9,$4   *  
        4\'hc:romdata=32\'b00000000000010010100000000100101;      //or $0,$9,$8    *
        4\'hd:romdata=32\'b00000000000001000100100000100101;      //or $0,$4,$9    *
        4\'he:romdata=32\'b00100010000100000000000000000001;      //addi $16,$16,1   *
        4\'hf:romdata=32\'b00010110000100011111111111111011;      //bne $16,$17,-5    *
        default:romdata=32\'b10101100000001000000000000010000;      //sw $0,$4,16
    endcase
     
    assign data=romdata;
    
    initial
        $monitor($time,,"rom:romdata=%h",romdata);
    
endmodule



//Selector
module selector(
    input [31:0] addr0,
    input [31:0] addr1,
    input [31:0] addr2,
    input [31:0] addr3,
    input [1:0] pcsource,
    output reg [31:0] next_addr
    );
    
    always @(*)
    begin
        case(pcsource)
            2\'b00:
            begin
                next_addr=addr0;
            end
            
            2\'b01:
            begin
                next_addr=addr1;        //bne,beq
            end
            
            2\'b10:
            begin
                next_addr=addr2;        //j
            end
            
            2\'b11:
            begin
                next_addr=addr3;        //jal,jr
            end
                           
        endcase
    end
    
    initial
        $monitor($time,,"selector: pcsource=%h, next_addr=%h",pcsource,next_addr);
    
endmodule

 

四. 所使用的控制信号

pcindex: pc值的来源

ram2reg: 是否将数据从RAM写入到寄存器中,=1为是,否则为否

ramWE: 是否写内存,=1为是,否则为否

aluOP: ALU的运算类型

regWE: 是否写寄存器,=1为是,否则为否

imm: 是否产生立即数,=1为是,否则为否

shift: 是否移位,=1为是,否则为否

isrt: 目的寄存器地址,=1选择rt,否则选择rd

sign_ext: 立即数拓展,=1为符号数拓展,否则为零拓展

jal: 是否调用子程序跳转,=1为是,否则为否

 

五. 各指令对应产生的控制信号

  pcindex ram2reg ramWE aluOP regWE imm shift isrt sign_ext jal
 add  0  0  0  0001  1   0  0  0
 sub  0  0  0  0010  1   0   0    0   0 
 and  0  0  0  0011 1  0  0 0  0  0 
 or  0  0  0100  1    0   0  0   0  0
 xor  0  0  0101 1   0   0  0   0   0 
 sll  0  0  0110 1   0   1  1    0    0 
 srl  0  0   0111 1  0  1  1   0   0
 sra  0  0   1000  1   0  1   1   0   0 
 jr 10  0  0   0   0   0  0  0  0  0
 addi 0  0  0  0001 1  1  0  1   1   0
 andi 0  0   0    0011  1  1  0  1  0  0
 ori  0  0   0100 1  1   0    1    0    0 
 xori  0  0   0101 1   1  0   1  0  0
 lw  1  0   0001  1    1    0    1    1   0
 sw  0  0  1  0001 0 1  0   1    1   0
 beq 00/01  0  0  0010 0  0   0    0     1   0
 bne 00/01  0  0  0010  0   0  0   0     1  0
 lui  0  0  0  1001 1 1   0   1    0   0
 j  11  0   0   0 0  0  0  0    0   0
 jal  11   0   0  0 1  0  0  0    0  1

 

六. 分析指令(ID)相关模块代码

//ID
module ID(
    input [31:0] instrument,
    output reg [5:0] opcode,
    output reg [5:0] func,
    output reg [4:0] rs,
    output reg [4:0] rt,
    output reg [4:0] rd,
    output reg [4:0] sa,
    output reg [15:0] immediate,
    output reg [25:0] addr
    );
    
    always @(*)
    begin
        opcode=instrument[31:26];
        rs=5\'b0;
        rt=5\'b0;
        rd=5\'b0;
        sa=5\'b0;
        immediate=16\'b0;
        addr=25\'b0;
        case(opcode)
            6\'b000000: //R类型
            begin
                func=instrument[5:0];
                sa=instrument[10:6];
                rd=instrument[15:11];
                rt=instrument[20:16];
                rs=instrument[25:21];
            end
            
            6\'b001000,6\'b001100,6\'b001101,6\'b001110,6\'b100011,6\'b101011,6\'b000100,6\'b000101,6\'b001111:
            begin
               immediate=instrument[15:0];
               rt=instrument[20:16];
               rs=instrument[25:21];
            end
            
            6\'b000010,6\'b000011:
            begin
                addr=instrument[25:0];
            end
            
            default: rs=5\'b00000;
            
        endcase
    end
    
    
endmodule


//CU
module CU(
    input [5:0] opcode,
    input [5:0] func,
    input z,
    output reg [1:0] pcindex,
    output reg ram2reg,
    output reg ramWE,
    output reg [3:0] aluOP,
    output reg regWE,
    output reg imm,
    output reg shift,
    output reg isrt,
    output reg sign_ext,
    output reg jal
    );
    
    always @(*)
    begin 
        //设置默认值
        shift=1\'b0;
        ram2reg=1\'b0;
        ramWE=1\'b0;
        regWE=1\'b0;
        imm=1\'b0;
        isrt=1\'b0;
        sign_ext=1\'b0;
        pcindex=2\'b00;
        aluOP=4\'b0000;
        jal=1\'b0;
        
        case(opcode)
            //R指令
            6\'b000000:
            begin
                case(func)
                    6\'b100000:  //add指令
                    begin
                        aluOP=4\'b0001;
                        regWE=1\'b1;
                    end
                    
                    6\'b100010:  //sub指令
                    begin
                        aluOP=4\'b0010;
                        regWE=1\'b1;
                    end
                    
                    6\'b100100:  //and指令
                    begin
                        aluOP=4\'b0011;
                        regWE=1\'b1;
                    end
                    
                    6\'b100101:  //or指令
                    begin
                        aluOP=4\'b0100;
                        regWE=1\'b1;
                    end
                    
                    6\'b100110:  //xor指令
                    begin
                        aluOP=4\'b0101;
                        regWE=1\'b1;
                    end
                    
                    6\'b000000:  //sll指令
                    begin
                        aluOP=4\'b0110;
                        regWE=1\'b1;
                        shift=1\'b1;
                        isrt=1\'b1;
                    end
                    
                    6\'b000010:  //srl指令
                    begin
                        aluOP=4\'b0111;
                        regWE=1\'b1;
                        shift=1\'b1;
                        isrt=1\'b1;
                    end
                    
                    6\'b000011:  //sra指令
                    begin
                        aluOP=4\'b1000;
                        regWE=1\'b1;
                        shift=1\'b1;
                        isrt=1\'b1;
                    end
                    
                    6\'b001000:  //jr指令
                    begin
                        pcindex=2\'b10;
                    end
                    
                endcase   
            end
            
            //I指令
            6\'b001000:  //addi指令
            begin
                aluOP=4\'b0001;
                imm=1\'b1;
                regWE=1\'b1;
                sign_ext=1\'b1;
                isrt=1\'b1;
            end
            
            6\'b001100:  //andi指令
            begin
                aluOP=4\'b0011;
                imm=1\'b1;
                regWE=1\'b1;
                isrt=1\'b1;
            end
            
            6\'b001101:  //ori指令
            begin
                aluOP=4\'b0100;
                imm=1\'b1;
                regWE=1\'b1;
                isrt=1\'b1;
            end
            
            6\'b001110:  //xori指令
            begin
                aluOP=4\'b0101;
                imm=1\'b1;
                regWE=1\'b1;
                isrt=1\'b1;
            end
            
            6\'b100011:  //lw指令
            begin
                ram2reg=1\'b1;
                aluOP=4\'b0001;
                imm=1\'b1;
                regWE=1\'b1;
                sign_ext=1\'b1;
                isrt=1\'b1;
            end
            
            6\'b101011:  //sw指令
            begin
                ramWE=1\'b1;
                aluOP=4\'b0001;
                imm=1\'b1;
                sign_ext=1\'b1;
                isrt=1\'b1;
            end
            
            6\'b000100:  //beq指令
            begin
                aluOP=4\'b0010;
                sign_ext=1\'b1;
                if(z==1\'b1)
                begin
                    pcindex=2\'b01;
                end
            end
            
            6\'b000101:  //bne指令
            begin
                aluOP=4\'b0010;
                sign_ext=1\'b1;
                if(z==1\'b0)
                begin
                    pcindex=2\'b01;
                end
            end
            
            6\'b001111:  //lui指令
            begin
                regWE=1\'b1;
                imm=1\'b1;
                isrt=1\'b1;
                aluOP=4\'b1001;
            end
                 
            //J指令                  
            6\'b000010:  //j指令
            begin
                pcindex=2\'b11;
            end
                    
            6\'b000011:  //jal指令
            begin
                jal=1\'b1;
                regWE=1\'b1;
                pcindex=2\'b11;
            end
                                            
        endcase
        
    end
    
    initial
        $monitor($time,,"CU:imm=%b,op=%h, isrt=%h, pcindex=%h, regWE=%h, shift=%h, ram2reg=%h, ramWE=%h ",imm,aluOP,isrt,pcindex,regWE,shift,ram2reg,ramWE);
    
    
endmodule

 

七. 总体电路图

 

 

八. 执行指令(EXE)相关模块的代码

 

//selector_5
module selector_5(
    input [4:0] a,
    input [4:0] b,
    input choice,
    output [4:0] f
    );
    
    assign f=(choice==1\'b0)?a:b;
    
    initial 
        $monitor($time,,"selector_5: a=%h, b=%h, f=%h",a,b,f);
    
endmodule



//selector_32
module selector_32(
    input [31:0] a,
    input [31:0] b,
    input choice,
    output [31:0] f
    );
    assign f=(choice==1\'b0)?a:b;
    initial
        $monitor($time,,"selector_32: f=%h",f);
endmodule



//ext_imm
module ext_imm(
    input [15:0] immediate,
    input sign_ext,
    output [31:0] imm
    );
    
    //sign_ext为1时,有符号拓展;为0时,0拓展
    assign imm=(sign_ext==0)?{{16{1\'b0}},immediate}:{{16{immediate[15]}},immediate};
    
    initial
        $monitor($time,,"ext_imm: imm=%h",imm);
    
endmodule


//alu_add_4
module alu_add_4(
    input [31:0] a,
    output [31:0] f
    );
    
    assign f=a+32\'b0100;
    
    initial
        $monitor($time,,"alu_add_4:f=%h",f);
    
    
endmodule




//registers
module registers(
    input clk,
    input oc,
    input [4:0] raddr1,
    input [4:0] raddr2,
    input [4:0] waddr,
    input [31:0] wdata,
    input we,
    output reg [31:0] rdata1,
    output reg [31:0] rdata2
    );
    reg[31:0] regts[1:31];
    
    always @(*)
    begin 
        if(oc==1\'b1)
        begin
            rdata1=32\'bz;
        end
        else if(raddr1==5\'b00000)
        begin
            rdata1=32\'b0;
        end
        else
        begin
            rdata1=regts[raddr1];
        end
        $monitor($time,,"Read R %d data1=%d",raddr1,rdata1);
    end
    
    always @(*)
    begin 
        if(oc==1\'b1)
        begin
            rdata2=32\'bz;
        end
        else if(raddr2==5\'b00000)
        begin
            rdata2=32\'b0;
        end
        else
        begin
            rdata2=regts[raddr2];
        end
        $monitor($time,,"Read R %d data2=%d",raddr2,rdata2);
    end
    
    always @(posedge clk)
    begin
        #1 if((we==1\'b1)&&(waddr!=5\'b00000))
        begin
            regts[waddr]<=wdata;
            $monitor($time,,"write %h to data=%d",waddr,wdata);
        end
    end
    
endmodule


//isimm
module isimm(
    input [31:0] rt,
    input [31:0] immediate,
    input  imm,
    output [31:0] b
    );
    
    assign b=imm?immediate:rt;
    
    initial
        $monitor($time,,"isimm:b=%h",b);
    
endmodule


//ALU
module alu(
    input [31:0] a,
    input [31:0] b,
    input [3:0] op,
    output [31:0] f,
    output z
    );
    
    reg [31:0]result;
    
    always@(*)
    begin
        case(op)
            4\'b0000:result=32\'b0;
            4\'b0001:result=a+b;
            4\'b0010:result=a-b;
            4\'b0011:result=a&b;
            4\'b0100:result=a|b;
            4\'b0101:result=a^b;
            4\'b0110:result=b<<a;
            4\'b0111:result=b>>a;
            4\'b1000:result=$signed(b)>>>a;
            4\'b1001:result={b,{16{1\'b0}}};
            default:result=32\'b0;
         endcase
    end
    
    assign f=result;
    assign z=~(|result);
    
    initial
        $monitor($time,,"alu:a:%h,b:%h,op:%h,",a,b,op);
    initial
        $monitor($time,,"alu:f:%h,z:%b",f,z);
    
endmodule



//IOManager
module IOManager(
    input [5:0] addr,
    input [31:0] din,
    output [31:0] dout,
    input we,
    input clk,
    input [3:0] switch,
    output reg[31:0] displaydata
    );
    
    reg [31:0] indata,outdata;
    wire [31:0] ramdout;
    wire ramWE;
    wire enable;
    assign enable=(|addr[5:4]);
    ram dram(addr[3:0],din,clk,enable,ramWE,ramdout);
    assign dout=(addr[5]==1\'b1)?{{28{1\'b0}},switch}:ramdout;
    assign ramWE=we&(~addr[4]);
    
    always @(posedge clk)
    begin
        if((addr[4]==1\'b1)&&we)
            displaydata<=din;
    end
    
    initial
        $monitor($time,,"IOM: addr=%h, din=%h, dout=%h",addr,din,dout);
        
    initial
        $monitor($time,,"IOM: switch=%h, displaydata=%h",switch,displaydata);
    
endmodule



//ram
module ram(
    input [3:0] addr,
    input [31:0] wdata,
    input clk,
    input ce,
    input we1,
    output reg[31:0] rdata
    );
    
    reg [31:0] ram[0:30];
    
    always @(*)
    begin
        if(ce==1\'b0)
        begin
            rdata=ram[addr];
        end
    end
    
    always @(posedge clk)
    begin
        #1 if((ce==1\'b0)&&(we1==1\'b1))
        begin
            ram[addr]<=wdata;
        end
    end
    
    initial 
        $monitor($time,,"ram: ram=%h,rdata=%h",ram[addr],rdata);
    
    
endmodule



//left
module left(
    input [31:0] a,
    output [31:0] b
    );
    
    assign b=a<<2;
    
endmodule


//alu_add_b
module alu_add_b(
    input [31:0] addr,
    input [31:0] immediate,
    output [31:0] f
    );
    
    assign f=addr+immediate;
    
    initial
        $monitor($time,,"alu_add_b: addr=%h, immediate=%h, f=%h ",addr,immediate,f);
    
endmodule



//ext_adder
module ext_adder(
    input [25:0] address,
    output [27:0] f
    );
    
    assign f={address,{2{1\'b0}}};
    
    initial
        $monitor($time,,"ext_addr: address=%h, f=%h ",address,f);
    
endmodule



//mixaddr
module mixAddr(
    input [31:0] addr,
    input [27:0] address,
    output [31:0] f
    );
    
    assign f={{addr[31:28]},address};
    
    initial 
        $monitor($time,,"mixAddr: addr=%h, address=%h, f=%h",addr,address,f);
    
endmodule

 

九. TOP文件代码

 1 module top(
 2     input clk,
 3     input rst,
 4     input [4:0] n,
 5     output [31:0] result
 6     );
 7    
 8    wire [31:0] next_addr;
 9    wire [31:0] addr,addr0,addr1,addr2,addr3;
10    wire [31:0] immediate;
11    wire [27:0] fAddress;
12    wire [31:0] instrument;
13    wire [5:0] opcode,func;
14    wire [4:0] rs,rt,rd,sa;
15    wire [15:0] imm;
16    wire [25:0] address;
17    wire z;
18    wire [1:0] pcindex;
19    wire ram2reg,ramWE,regWE,isimm,shift,isrt,sign_ext,jal;
20    wire [3:0] aluOP;
21    wire [4:0] waddr;
22    wire [4:0] tmp_waddr;
23    wire [4:0] regs31=5\'b11111;
24    wire [31:0] wdata,rdata1,rdata2,f,tmp_f;
25    wire oc=1\'b0;
26    wire [31:0] a,b;
27    wire [31:0] dout;
28    wire [31:0] lImmediate;
29    wire [31:0] fsa;
30    
31    PC myPC(next_addr,rst,clk,addr);
32    rom myRom(addr,instrument);
33    ID myID(instrument,opcode,func,rs,rt,rd,sa,imm,address);
34    CU myCU(opcode,func,z,pcindex,ram2reg,ramWE,aluOP,regWE,isimm,shift,isrt,sign_ext,jal);
35    ext_imm myExtImm(imm,sign_ext,immediate);
36    selector_5 mySelector5_1(rd,rt,isrt,tmp_waddr);  //目的寄存器是否rt
37    selector_5 mySelector5_2(tmp_waddr,regs31,jal,waddr);    //是否调用子程序调转wa
38    alu_add_4 myAdd4(addr,addr0);
39    selector_32 mySelector32_1(f,addr0,jal,wdata);   //是否调用子程序调转wd
40    registers myRegs(clk,oc,rs,rt,waddr,wdata,regWE,rdata1,rdata2);
41    isimm myIsImm(rdata2,immediate,isimm,b);
42    assign fsa={{27{1\'b0}},sa};
43    selector_32 mySelector32_2(rdata1,fsa,shift,a);         //选择sa和rs数据
44    alu myAlu(a,b,aluOP,tmp_f,z);
45    IOManager myIOM(tmp_f,rdata2,dout,ramWE,clk,n,result);   
46    selector_32 mySelector32_3(tmp_f,dout,ram2reg,f);    //是否将数据从ram中写入reg
47    left myLeft(immediate,lImmediate);
48    alu_add_b myAddb(addr0,lImmediate,addr1);
49    ext_adder myExtAddr(address,fAddress);
50    mixAddr myMixAddr(addr,fAddress,addr3);
51    selector mySelector(addr0,addr1,rdata1,addr3,pcindex,next_addr);
52    
53    
54    initial
55        $monitor($time,,"top: n=%h, result=%h",n,result); 
56     
57 endmodule