單周期CPU——verilog語言實現


一. 實驗內容

設計一個單周期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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM