计算机组成原理实验——五、单周期CPU设计

一、实验目的

  1. 掌握指令执行过程的5个阶段
  2. 掌握每条指令的数据通路选择
  3. 掌握译码器和控制器的功能和实现
  4. 掌握数据输入输出处理的方法
  5. 实现risc-v中RV32I指令的单周期CPU
  6. 利用实现的risc-v CPU实现平方数

二.实验内容

  1. 实现risc-v中37条RV32I指令的单周期 cpu;
  2. 完成1后在该cpu上实现斐波那契数。

三、实验程序

rom
请添加图片描述

dataRAM

请添加图片描述
ins文件(斐波那契数列的机器代码)
请添加图片描述

top.v

module top(
    input clk,
    input rst,
    input [3:0] n,
    output [2:0] an,
    output [6:0] out
    );
    wire[31:0] temp;
    CPU mycpu(.clk(clk),.rst(rst),.data(n),.result(temp));
    show myshow(.clk(clk),.rst(rst),.result(temp[11:0]),.an(an),.out(out)); 
endmodule

CPU.v

module CPU(
    input clk,
    input rst,
    input [3:0] data,         //n
    output [11:0] result       //f(n)
    );
    //声明wire总线,用来连接各个模块
    wire[31:0]nextaddr,addr,addr4,insdata,rdata1,rdata2,rwdata,a,b,f,mdata,imm12_exp,imm20_exp,addr_branch,addr_jal,addr_jalr,out;
    wire[4:0] rs1,rs2,rd;
    wire[6:0] opcode;
    wire[2:0] func,mm;
    wire[1:0] pcsource;
    wire dis,con,rwe,mwe,isimm,isfpc,is20,isfm,ispc4;
    wire[11:0] imm12;
    wire[19:0] imm20;
    wire[3:0] aluop;
    wire ecall; // 判断是否为中断
    wire inflag, outflag, finish;   // 输入标识,输出标识,结束标识
    
    // 实例化PC
    PC mypc(
        .rst(rst),
        .clk(clk),
        .next(nextaddr),
        .finish(finish),
        .addr(addr)
    );
        
    // 实例化ROM
    InsROM ir(
        .a(addr),
        .spo(insdata)
     );
    
    // 实例化ID
    ID myid(
        .ins(insdata),
        .rs1(rs1),
        .rs2(rs2),
        .rd(rd),
        .opcode(opcode),
        .func(func),
        .dis(dis),
        .imm12(imm12),
        .imm20(imm20),
        .ecall(ecall)
    );
    
    // 实例化控制单元
    ControlUnit2 mycu(
        .opcode(opcode),
        .func(func),
        .dis(dis),
        .c(con),
        .pcs(pcsource),
        .rwe(rwe),
        .mwe(mwe),
        .mm(mm),
        .aluOP(aluop),
        .isimm(isimm),
        .is20(is20),
        .isfpc(isfpc),
        .isfm(isfm),
        .ispc4(ispc4)
    );    

    assign inflag=ecall?((rs1==10&&rdata1==5)?1:0):0;   // x[10]=5,输入   
    assign outflag=ecall?((rs1==10&&rdata1==1)?1:0):0;  // x[10]=1, 输出
    assign finish=ecall?((rs1==10&&rdata1==10)?1:0):0;  // x[10]=10,结束
    assign rwdata=inflag?data:(ispc4?addr4:(isfm?mdata:f)); // 输入中断,x[10]=data
    assign result=outflag?rdata2:result;    // 输出中断,result=x[11]
    // 实例化寄存器堆
    RegFiles myrf(
        .raddr1(rs1),
        .rdata1(rdata1),
        .raddr2(rs2),
        .rdata2(rdata2),
        .waddr(rd),
        .wdata(rwdata),
        .we(rwe),
        .clk(clk)
    );
    
    // 将12位立即数符号扩展位32位
    assign imm12_exp={{20{imm12[11]}},imm12};
    // 将20位立即数符号扩展位32位
    assign imm20_exp={{12{imm20[19]}},imm20};      
    // 获取a的数据 来源于addr、rdata1
    assign a=isfpc?addr:rdata1;
    // 获取b的数据,来源于imm12、imm20或rdata2
    assign b=isimm?(is20?imm20_exp:imm12_exp):rdata2;
    
    // 实例化ALU
    ALU myalu(
        .a(a),
        .b(b),
        .op(aluop),
        .f(f),
        .c(con)
    );
            
    // 实例化RAM
    NEWRAM nr(
       .maddr(f),
       .mwdata(rdata2),
       .clk(clk),
       .we(mwe),
       .mm(mm),
       .mdata(mdata)
    );
    
    // 计算顺序指令地址  pc+4
    assign addr4=addr+4;
    // 下一条指令的地址
    assign nextaddr=pcsource[1]?(pcsource[0]?{f[31:1],1'b0}:f):(pcsource[0]?(imm12_exp<<1)+addr:addr4);

    initial
    begin
        $monitor($time,,"当前指令地址=%h,指令=%h",addr,insdata);  
        $monitor($time,,"寄存器读端口1:编号=%d,数据=%h",rs1,rdata1);  
        $monitor($time,,"寄存器读端口2:编号=%d,数据=%h",rs2,rdata2);
        $monitor($time,,"立即数=%h",b); 
        $monitor($time,,"寄存器写地址=%d,写数据=%h,写使能端=%b", rd,rwdata,rwe); 
        $monitor($time,,"输入标识=%d, 输出标识=%d,结束标识=%d",inflag,outflag,finish); 
    end
        
endmodule

PC.v

module PC(
    input rst,
    input clk,
    input [31:0] next,
    input finish,
    output reg [31:0] addr
    );
    
    always @(posedge clk)
        if(rst)
            addr<=32'h0;
        else
            if (!finish)
                addr<=next;  

//initial
//    begin
//        $monitor($time,,"PC Input: next=%h", next);
//        $monitor($time,,"PC Output: addr=%h", addr);
//    end
        
endmodule

module ID(
    input [31:0] ins,       // 指令
    output [4:0] rs1,       // 参数信息(REGFILES)
    output [4:0] rs2,       // 参数信息(REGFILES)
    output [4:0] rd,        // 参数信息(REGFILES)
    output [6:0] opcode,    // 身份信息
    output [2:0] func,      // 身份信息(func3和func7)
    output [11:0] imm12,    // 12位立即数
    output [19:0] imm20,    // 20位立即数
    output dis,             // 区分码,指令的第30位
    output reg ecall       // 是否为中断
    );

// 判断是否为ecall指令    
    always @(*)
    begin
        if (ins==32'h00000073) 
            ecall=1'b1;
        else
            ecall=1'b0;
    end
    
    assign opcode=ins[6:0];
    assign rd=ecall?10:ins[11:7];   // ecall指令,rd=10
    assign func=ins[14:12];         
    assign rs1=ecall?10:ins[19:15]; // ecall指令, rs1=10
    assign rs2=ecall?11:ins[24:20]; // ecall指令, rs2=11
    assign dis=ins[30];

    wire I_type,S_type,B_type,J_type,U_type;
    // 00x0011  I指令  1100111   jalr
    assign I_type=~ins[6]&~ins[5]&~ins[3]&~ins[2]&ins[1]&ins[0]|
                  (&ins[6:5])&~(|ins[4:3])&(&ins[2:0]);  
     // 0100011  S指令        
    assign S_type=~ins[6]&ins[5]&~(|ins[4:3])&(&ins[1:0]); 
    // 1100011  B指令
    assign B_type=(&ins[6:5])&~(|ins[4:3])&(&ins[1:0]);   
    // 0x10111 U指令
    assign U_type=~ins[6]&ins[4]&~ins[3]&(|ins[2:0]);
    // 1101111 jal
    assign J_type=(&ins[6:5])&~ins[4]&(&ins[3:0]);
       
    assign imm12={12{I_type}}&ins[31:20]|
                 {12{B_type}}&{ins[31],ins[7],ins[30:25],ins[11:8]}|
                 {12{S_type}}&{ins[31:25],ins[11:7]};
    assign imm20={20{U_type}}&ins[31:12]|
                 {20{J_type}}&{ins[31],ins[19:12],ins[20],ins[30:21]};

//initial
//    begin
//        $monitor($time,,"ID Output: ecall=%h", ecall);
//        $monitor($time,,"ID Input: ins=%h", ins);
//        $monitor($time,,"ID Output: rs1=%h, rs2=%h, rd=%h, opcode=%h, func=%h, imm12=%h, imm20=%h",rs1,rs2,rd,opcode,func,imm12,imm20);
//    end
endmodule

ControlUnit2.v

module ControlUnit2(
    input [6:0] opcode,
    input [2:0] func,
    input dis,
    input c,                //是否满足B类指令的跳转条件 =1  满足
    output reg [1:0]pcs,    //下一条指令地址来源方式   =00 顺序执行   =01 有条件跳转   =10  jal    =11  jalr
    output reg rwe,         //寄存器写使能端   =1
    output reg mwe,         //RAM写使能端      =1
    output reg [2:0] mm,    //RAM读写控制方式
    output reg [3:0] aluOP,//ALU运算类型
    output reg isimm,      //b是否是立即数
    output reg is20,       //b是否是20位的立即数
    output reg isfpc,      //a是否来源于pc
    output reg isfm,       //rwdata是否来源于mem
    output reg ispc4       //rwdata是否来源于pc+4
    );
    always @(*)
    begin
        case(opcode)
            7'b0110011: // R类指令
            begin
                pcs=2'b00;
                rwe=1'b1;
                mwe=1'b0;
                mm=3'b0;
                isimm=1'b0;
                is20=1'b0;
                isfpc=1'b0;
                isfm=1'b0;
                ispc4=1'b0;
                case(func)
                    3'b000: 
                    begin
                        if (dis==0)
                            aluOP=4'b0000;  // add 加法
                        else
                            aluOP=4'b0001;  // sub 减法
                    end
                    3'b001: aluOP=4'b0111;  // sll 左移指令
                    3'b010: aluOP=4'b0101;  // slt 有符号数比较
                    3'b011: aluOP=4'b0110;  // sltu 无符号数比较
                    3'b100: aluOP=4'b0100;  // xor 异或运算
                    3'b101:
                    begin
                        if (dis==0)
                            aluOP=4'b1000;  // srl 右移指令
                        else
                            aluOP=4'b1001;  // sra 算术右移指令
                    end
                    3'b110: aluOP=4'b0011;  // or 或运算
                    3'b111: aluOP=4'b0010;  // and 与运算
            endcase                 
            end
            7'b0010011: // I类指令
            begin
                pcs=2'b00;
                rwe=1'b1;
                mwe=1'b0;
                mm=3'b0;
                isimm=1'b1;
                is20=1'b0;
                isfpc=1'b0;
                isfm=1'b0;
                ispc4=1'b0;
                case(func)
                    3'b000: aluOP=4'b0000;      // addi 加法
                    3'b010: aluOP=4'b0101;      // slti 有符号数比较
                    3'b011: aluOP=4'b0110;      // sltiu 无符号数比较
                    3'b100: aluOP=4'b0100;      // xori 异或运算
                    3'b110: aluOP=4'b0011;      // ori 或运算
                    3'b111: aluOP=4'b0010;      // andi 与运算
                    3'b001: aluOP=4'b0111;      // slli 左移指令
                    3'b101: 
                        begin
                            if (dis==0)
                                aluOP=4'b1000;  // srli 右移指令
                            else 
                                aluOP=4'b1001;  // srai 循环右移指令
                        end
                    default:;
                endcase     
            end  
            7'b0000011:  // I类:load指令
            begin
                pcs=2'b00;
                rwe=1'b1;
                mwe=1'b0;
                isimm=1'b1;
                is20=1'b0;
                isfpc=1'b0;
                isfm=1'b1;
                ispc4=1'b0;
                aluOP=4'b0;
                case(func)
                    3'b000: mm=3'b000; // lb 取内存低8位,符号拓展成32位
                    3'b001: mm=3'b001; // lh 取内存低16位,符号拓展成32位
                    3'b010: mm=3'b100; // lw 取内存32位
                    3'b100: mm=3'b010; // lbu 取内存低8位,零拓展成32位
                    3'b101: mm=3'b011; // lhu 取内存低16位,零拓展成32位
                    default:;
                endcase     
            end
            7'b0100011: // S类指令
            begin
                pcs=2'b00;
                rwe=1'b0;
                mwe=1'b1;
                isimm=1'b1;
                is20=1'b0;
                isfpc=1'b0;
                isfm=1'b0;
                ispc4=1'b0;
                aluOP=4'b0;
                case(func)
                    3'b000: mm=3'b101; // sb 取寄存器rs2中低8位,存放到对应内存
                    3'b001: mm=3'b110; // sh 取寄存器rs2中低16位,存放到对应内存
                    3'b010: mm=3'b111; // sw 取寄存器rs2中的数,存放到对应内存
                    default:;
                endcase     
            end     
            7'b0110111: // lui指令 将20位立即数保存到寄存器rd的高20位
            begin 
                pcs=2'b00;
                rwe=1'b1;
                mwe=1'b0;
                mm=3'b0;
                isimm=1'b1;
                is20=1'b1;
                isfpc=1'b0;
                isfm=1'b0;
                ispc4=1'b0;
                aluOP=4'b1010;
            end          
            7'b0010111: // auipc指令 将20位立即数左移12位加上PC值保存到寄存器rd中
            begin 
                pcs=2'b00;
                rwe=1'b1;
                mwe=1'b0;
                mm=3'b0;
                isimm=1'b1;
                is20=1'b1;
                isfpc=1'b1;
                isfm=1'b0;
                ispc4=1'b0;
                aluOP=4'b1011;
            end  
            7'b1100111: // jalr指令
            begin 
                pcs=2'b11;
                rwe=1'b1;
                mwe=1'b0;
                mm=3'b0;
                isimm=1'b1;
                is20=1'b0;
                isfpc=1'b0;
                isfm=1'b0;
                ispc4=1'b1;
                aluOP=4'b0;
            end   
            7'b1101111: // jal指令
            begin 
                pcs=2'b10;
                rwe=1'b1;
                mwe=1'b0;
                mm=3'b0;
                isimm=1'b1;
                is20=1'b1;
                isfpc=1'b0;
                isfm=1'b0;
                ispc4=1'b1;
                aluOP=4'b1100;
            end  
            7'b1100011: // B类指令
            begin
                pcs=c?2'b01:2'b00;
                rwe=1'b0;
                mwe=1'b0;
                mm=3'b0;
                isimm=1'b0;
                is20=1'b0;
                isfpc=1'b0;
                isfm=1'b0;
                ispc4=1'b0;
                case(func)
                    3'b000: aluOP=4'b0001;  // beq == 跳转
                    3'b001: aluOP=4'b1101;  // bne != 跳转
                    3'b100: aluOP=4'b0101;  // blt < 跳转
                    3'b101: aluOP=4'b1110;  // bge >= 跳转
                    3'b110: aluOP=4'b0110;  // bltu < 跳转(无符号数)
                    3'b111: aluOP=4'b1111;  // bgeu >= 跳转(无符号数)
                endcase                     
            end                                                        
        endcase
     end

//initial
//    begin
//        $monitor($time,,"CU Input: opcode=%h, func=%h, dis=%h, c=%h", opcode, func, dis, c);
//        $monitor($time,,"CU Output: pcs=%h, rwe=%h, mwe=%h, mm=%h, aluOP=%h, isimm=%h, is20=%h, isfpc=%h, isfm=%h, ispc4=%h",pcs,rwe,mwe,mm,aluOP,isimm,is20,isfpc,isfm,ispc4);
//    end
endmodule

RegFiles.v

module RegFiles(
    input clk,
    input [4:0] raddr1,
    output [31:0] rdata1,
    input [4:0] raddr2,
    output [31:0] rdata2,
    input [4:0] waddr,
    input we,
    input [31:0] wdata
    );
    reg[31:0] regs[1:31];
    
    assign rdata1=(raddr1==5'b00000)?32'b0:regs[raddr1];
    assign rdata2=(raddr2==5'b00000)?32'b0:regs[raddr2];
                      
     always @(posedge clk)
        if(we)
            if(waddr!=4'b00000)
                regs[waddr]<=wdata;
    
//initial
//    begin
//        $monitor($time,,"REG Input: raddr1=%h, raddr2=%h, waddr=%h, wdata=%h, we=%h", raddr1, raddr2, waddr, wdata, we);
//        $monitor($time,,"REG Output: rdata1=%h, rdata2=%h",rdata1,rdata2);
//    end

endmodule

ALU.v

module ALU(
    input [31:0] a,
    input [31:0] b,
    input [3:0] op,
    output [31:0] f,
    output reg c           //判断条件是否满足
    );
    reg[31:0] res;
    
    always @(*)
    begin
        case(op)
            4'b0000:    
            begin
                res=a+b; 
                c=1'b0;
            end
            4'b0001:
            begin    
                res=a-b;
                if (res==0)
                    c=1'b1;
                else
                    c=1'b0;
            end
            4'b0010:    
                begin
                    res=a&b;
                    c=1'b0;
                end
            4'b0011:    
                begin   
                    res=a|b;
                    c=1'b0;
                end
            4'b0100:    
            begin
                res=a^b;
                c=1'b0;
            end
            4'b0101:
                if (($signed(a))<($signed(b)))
                begin
                    res=32'b1;
                    c=1'b1;
                end
                else
                begin
                    res=32'b0;
                    c=1'b0;
                end
            4'b0110: 
                if (a<b)
                begin
                    res=32'b1;
                    c=1'b1;
                end
                else
                begin
                    res=32'b0;
                    c=1'b0;
                end
            4'b0111:
                begin    
                    res=a<<b[4:0];
                    c=1'b0;
                end
            4'b1000:    
                begin
                    res=a>>b[4:0];
                    c=1'b0;
                end
            4'b1001:    
                begin
                    res=($signed(a))>>>b[4:0];
                    c=1'b0;
                end
            4'b1010:    
                begin
                    res=b<<12;
                    c=1'b0;
                end
            4'b1011:    
                begin
                    res=(b<<12)+a;
                    c=1'b0;
                end
            4'b1100: 
                begin  
                    res=(b<<1)+a;
                    c=1'b0;
                end
            4'b1101:
                if (a!=b)
                    c=1'b1;
                else
                    c=1'b0;
            4'b1110:
                if (($signed(a))>=($signed(b)))
                    c=1'b1;
                else
                    c=1'b0;
            4'b1111:
                if(a>=b)
                    c=1'b1;
                else
                    c=1'b0;
        endcase
    end
    assign  f=res;
    
//initial
//    begin
//        $monitor($time,,"ALU Input: a=%h, b=%h, op=%h",a, b, op);
//        $monitor($time,,"ALU Output: f=%h, c=%h",f, c);
//    end
endmodule

NEWRAM.v

module NEWRAM(
    input [5:0] maddr,
    input [31:0] mwdata,
    input clk,
    input we,
    input [2:0] mm,
    output [31:0] mdata
    );
    wire [31:0] tmp,tmp_b,tmp_h,tmp_bu,tmp_hu;
    wire [31:0] tmp_mwdata;
    dataRAM dr(.a(maddr),.d(tmp_mwdata),.clk(clk),.we(we),.spo(tmp));
    wire lb,lh,lw,lbu,lhu,sb,sh,sw;
    // 识别出各类读写方法
    assign lb=~(|mm);               // 000
    assign lh=~(|mm[2:1])&mm[0];    // 001
    assign lw=mm[2]&~(|mm[1:0]);    // 100
    assign lbu=~mm[2]&mm[1]&~mm[0]; // 010
    assign lhu=~mm[2]&(&mm[1:0]);   // 011
    assign sb=mm[2]&~mm[1]&mm[0];   // 101
    assign sh=(&mm[2:1])&~mm[0];    // 110
    assign sw=&mm;                  // 111
    // 读数据
    assign tmp_b={{24{tmp[7]}},tmp[7:0]};
    assign tmp_h={{16{tmp[15]}},tmp[15:0]};
    assign tmp_bu={24'b0,tmp[7:0]};
    assign tmp_hu={16'b0,tmp[15:0]};
    assign mdata={32{lb}}&tmp_b|
                 {32{lh}}&tmp_h|
                 {32{lw}}&tmp|
                 {32{lbu}}&tmp_bu|
                 {32{lhu}}&tmp_hu;
     // 写数据
     assign tmp_mwdata={32{sb}}&{24'b0, mwdata[7:0]}|
                       {32{sh}}&{16'b0, mwdata[15:0]}|
                       {32{sw}}&mwdata;
// initial
//     begin
//         $monitor($time,,"RAM Input: maddr=%h, mwdata=%h, we=%h, mm=%h",maddr, mwdata, we, mm);
//         $monitor($time,,"RAM Temp:tmp_mwdata=%h, tmp=%h", tmp_mwdata, tmp);
//         $monitor($time,,"RAM Output: mdata=%h",mdata);
//     end
endmodule

show.v

module show(
    input clk,
    input rst,
    input [11:0] result,
    output reg[2:0] an,
    output [6:0] out
    );
    wire clk_new;
    div mydiv(.clk(clk),.clk_new(clk_new));
    reg[3:0] data;
    reg[1:0] cur_state,nex_state;
    // 状态转移
    always @(posedge clk)
//    always @(posedge clk_new)
    begin
    if (rst)
        cur_state<=2'b00;
    else
        cur_state<=nex_state;
    end
    // 状态转移条件
    always @(*)
    begin
        case(cur_state)
            2'b00:
                nex_state<=2'b01;
            2'b01:
                nex_state<=2'b10;
            2'b10:
                nex_state<=2'b00;
        endcase
    end
    // 状态输出
    always @(posedge clk)
//    always @(posedge clk_new)
    begin
        case(cur_state)
            2'b00:
            begin
                an<=3'b01;
                data<=result[3:0];
            end
            2'b01:
            begin
                an<=3'b010;
                data<=result[7:4];
            end
            2'b10:
            begin
                an<=3'b100;
                data<=result[11:8];
            end
        endcase
    end
    seven myseven(.data(data),.out(out));        
endmodule

div.v

module div(
    input clk,
    output clk_new
    );
    reg[17:0] q = 18'b0;
    always @(posedge clk)
    begin
        q=q+1'b1;
    end
    assign clk_new=q[17];
endmodule

seven.v

module seven(
    input [3:0] data,
    output reg[6:0] out
    );
  always @(*)
    case (data)
        4'b0000:out = 7'b1111110; // 7e
        4'b0001:out = 7'b0110000; // 30
        4'b0010:out = 7'b1101101; // 6d
        4'b0011:out = 7'b1111001; // 79
        4'b0100:out = 7'b0110011; // 33
        4'b0101:out = 7'b1011011; // 5b
        4'b0110:out = 7'b1011111; // 5f
        4'b0111:out = 7'b1110000; // 70
        4'b1000:out = 7'b1111111; // 7f
        4'b1001:out = 7'b1111011; // 7b
        4'b1010:out = 7'b1110111; // 77
        4'b1011:out = 7'b0011111; // 1f
        4'b1100:out = 7'b1001110; // 4e
        4'b1101:out = 7'b0111101; // 3d
        4'b1110:out = 7'b1001111; // 4f
        4'b1111:out = 7'b1000111; // 47
        default:out = 7'b1111110; //7e
    endcase     
endmodule

四、仿真程序

mysim.v

module mysim(

    );
    /*********定义数据类型,输入数据用reg类型,
    输出数据用wire类型***********/
    reg clk=1'b0;
    reg[3:0] n=4'b1111;
    reg rst=1'b0;
    wire[2:0] an;
    wire[6:0] out;
    /*********为每个输入变量产生测试数据*******/
    always #5
        clk=~clk;            
    initial
    begin
        #4 rst=1'b1;
        #2 rst=1'b0;
    end       
    /*********用上面定义的输入输出数据
    来对测试模块实例化***********/
    top mytop(.clk(clk),.rst(rst),.n(n),.an(an),.out(out));
endmodule

五、仿真结果

请添加图片描述

六、实验结果

请添加图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值