计算机组成原理实验——单周期CPU的实现Verilog

逻辑框图
在这里插入图片描述
IOManager
在这里插入图片描述

在这里插入图片描述
小组组员共同完成:

//Verilog
//cpu模块:
module cpu(
    input clk,
    input oc,
    input rst,
    input [3:0] switch, //开关输入数据
    output [31:0] displaydata    //输出到led数据
    );
        //ID
        wire[5:0] opcode;   //指令类型
        wire[5:0] func;     //指令功能码
        wire [4:0] rs;
        wire [4:0] rt;
        wire[4:0] rd;
        wire[4:0] sa;
        wire[31:0] sa1={27'b0,sa};//为了统一sa和cpu、selector2里的接口位数,确保数值传输和运算正确,将其扩展为32位
        wire[15:0] immediate;
        wire[25:0] addr;
        //CU
        wire[1:0] pcindex;//4选1
        wire ram2reg;
        wire ramWE;     //ram写信号
        wire[3:0] aluOP;//alu运算类型
        wire regWE;     //reg写信号
        wire imm;       //立即数信号
        wire shift;     //移位信号
        wire isrt;      //1:rd 0:rt
        wire sign_ext;  //1:符号扩展 0:零扩展
        wire jal;       //跳转信号
        //alu
        wire [31:0] f;
        wire z;
        //reg
        wire [31:0]rs_data;
        wire [31:0]rt_data;
        //rom
        wire [31:0]instrument;  //指令
        //pc
        wire [31:0]nextAddr, insAddr;
        //数据选择器
        wire [31:0]sel2_1, sel2_2, sel2_3, sel2_4, sel2_5, sel2_6;
        //extend
        wire [31:0]immediate_32;
        //ram/IOManager
        wire [31:0]ramOut;
        
        //程序计数器
        pc my_pc(clk, rst, nextAddr, insAddr);
        //四选一数据选择器,确定pc下一个地址
        selector selector_4(insAddr+4, immediate_32, rs_data, addr, pcindex, nextAddr);
        //waddr的选择
        selector2 rt_or_rd(rt, rd, isrt, sel2_1);
        //alu参数a的选择
        selector2 rs_or_sd(rs_data, sa1, shift, sel2_2);
        //alu参数b的选择
        selector2 rt_or_imm(rt_data,immediate_32, imm, sel2_3);//imm
        //waddr选择(jal时选择31号寄存器)
        selector2 write_addr(sel2_1, 5'b11111, jal, sel2_4);
        //输出结果选择(默认为alu结果f,ram2reg置1时选用ramOut)
        selector2 f_or_ram(f, ramOut, ram2reg, sel2_5);
        //wdata选择(jal时选择pc+4)
        selector2 write_data(sel2_5, insAddr+4, jal, sel2_6);
        //指令解码器
        InstrumentDecoder ID(instrument, opcode, func, rs, rt, rd, sa1, immediate, addr);
        //寄存器堆
        register my_reg(clk, oc, rs, rt, sel2_4, sel2_6, regWE|ram2reg, rs_data, rt_data);
        //立即数扩展
        extend imm_extd(immediate, sign_ext, immediate_32);
        //运算单元
        alu my_alu(sel2_2, sel2_3, aluOP, f, z);
        //rom存储器
        rom my_rom(insAddr,instrument);
        //输入输出设备
        IOManager my_iom(f, rt_data, ramWE, clk, ramOut, switch, displaydata);
        //控制单元
        ControlUnit CU(opcode, func, z, pcindex, ram2reg, ramWE, aluOP, regWE, imm, shift, isrt, sign_ext, jal);
        //initial
        //$monitor($time,,"sel2_2=%h",sel2_2); 
       //$monitor($time,,"sel2_1=%b;sel2_5=%b;regWE=%b;ram2reg=%b;regWE|ram2reg=%b;rs_data=%b;rt_data=%b",sel2_1, sel2_5, regWE,ram2reg,regWE|ram2reg,rs_data, rt_data);
endmodule


//PC模块:
module pc(
    input clk,
    input rst,
    input [31:0] nextAddr,
    output reg [31:0] insAddr
    );
    always @(posedge clk)
    begin
        if(rst==1'b0)
            insAddr<=32'h0;    //当rst=0时进行初始化,初始指令的地址为0
        else
            insAddr<=nextAddr; 
    end
    initial
        $monitor($time,,"pc:%h", insAddr);
endmodule

//ROM模块:
module rom(
    input [31:0] addr,
    output [31:0] data
    );
    reg[31:0] romdata;
    always @(*)
        case(addr)//编写rom存储内容为实现斐波那契数列的机器码
        4'h0:romdata=32'h00000026;   //xor $0, $0, $0
        4'h4:romdata=32'h34030000;   //ori $3, $0, 0
        4'h8:romdata=32'h34040001;   //ori $4, $0, 1
        8'hc:romdata=32'h8c078000;   //lw $7, 8000h($0)
        8'h10:romdata=32'h34060001;  // ori $6, $0, 1
        8'h14:romdata=32'h10e60006;  //beq $7, $6, 24 
        8'h18:romdata=32'h00641020;  //add $2, $3, $4 
        8'h1c:romdata=32'h20830000;  //addi $3, $4, 0
        8'h20:romdata=32'h20440000;  //addi $4, $2, 0
        8'h24:romdata=32'h20e7ffff;  //addi $7, $7, -1
        8'h28:romdata=32'h14e6fffc;  //bne $7, $6, -16
        8'h2c:romdata=32'hac044000;  //sw $4, 4000h($0) 
        endcase
    assign data=romdata;
    initial 
    $monitor($time,,"rom:instrument=%h",data);
endmodule

//ID模块:
module InstrumentDecoder(
    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,//16位立即数
    output reg[25:0] addr	//地址
    );
    
    always @(*)
    begin
        opcode <= instrument[31:26];
        rs <= 5'b0;
        rt <= 5'b0;
        rd <= 5'b0;
        sa <= 5'b0;
        immediate <= 15'b0;
        addr <= 25'b0;
        
        case(opcode)
            6'b000000:          //R
            begin
                func <= instrument[5:0];    //add、sub、and、or、xor、sll、srl、sra、jr
                sa <= instrument[10:6];
                rd <= instrument[15:11];
                rt <= instrument[20:16];
                rs <= instrument[25:21];
            end
            6'b001000,  //addi:rs + imm -> rt 
            6'b001100,  //andi:rs & imm -> rt
            6'b001101,  //ori:rs | imm -> rt
            6'b001110,  //xori:rs ^ imm -> rt
            6'b100011,  //lw:ram[rs + offset] -> r]
            6'b101011,  //sw:rt -> ram[rs + offset]
            6'b000100,  //beq:if(rs == rt),jump pc+4 + offset<<2
            6'b000101,  //bne:if(rs != rt),jump pc+4 + offset<<2
            6'b001111:  //lui:将imm数保存到rt的高16位
            begin
                immediate <= instrument[15:0];
                rt <= instrument[20:16];
                rs <= instrument[25:21];
            end
            //J指令
            6'b000010,  //j:跳转到{pc[31:28],address<<2}
            6'b000011:  //jal:跳转到{pc[31:28],address<<2},并将pc+4保存到$31中
            begin
                addr <= instrument[25:0];
            end
        endcase
    end
endmodule

//CU模块:
module ControlUnit(
    input [5:0] opcode,         //指令类型
    input [5:0] func,           //指令功能码
    input z,                    //判断
    output reg [1:0] pcindex,   //pc来源
    output reg ram2reg,         //是否将数据从ram写到reg
    output reg ramWE,           //ram写信号
    output reg [3:0] aluOP,     //alu运算类型
    output reg regWE,           //reg写信号
    output reg imm,             //立即数信号
    output reg shift,           //移位信号
    output reg isrt,            //1:rd 0:rt
    output reg sign_ext,        //1:符号扩展 0:零扩展
    output reg jal              //跳转信号
    );
    
    always @(*)
    begin
        shift <= 1'b0;
        ram2reg <= 1'b0;
        ramWE = 1'b0;
        regWE <= 1'b0;
        imm <= 1'b0;
        isrt <= 1'b0;   //isrt为0选择rt,为1选择rd
        sign_ext <= 1'b0;
        pcindex <= 2'b0;
        aluOP <= 4'b0;
        jal <= 1'b0;
        
        case(opcode)
            6'b000000:
            begin
                case(func)
                    6'b100000:  //add
                    begin
                        aluOP <= 4'b0001;
                        regWE <= 1'b1;
                        isrt <= 1'b1;
                    end
                    6'b100010:  //sub
                    begin
                        aluOP <= 4'b0010;
                        regWE <= 1'b1;
                        isrt <= 1'b1;
                    end
                    6'b100100:  //and
                    begin
                        aluOP <= 4'b0011;
                        regWE <= 1'b1;
                        isrt <= 1'b1;
                    end
                    6'b100101:  //or
                    begin
                        aluOP <= 4'b0100;
                        regWE <= 1'b1;
                        isrt <= 1'b1;
                    end
                    6'b100110:  //xor
                    begin
                        aluOP <= 4'b0101;
                        regWE <= 1'b1;
                        isrt <= 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'b1010;
                        regWE <= 1'b1;
                        shift <= 1'b1;
                        isrt <= 1'b1;
                    end
                    6'b001000:  //jr
                    begin
                        pcindex <= 2'b10;
                    end
                 endcase
            end
            6'b001000:  //addi
            begin
                regWE <= 1'b1;
                aluOP <= 4'b0001;
                sign_ext <= 1'b1;
                imm<=1'b1;
            end
            6'b001100:  //andi
            begin
                regWE <= 1'b1;
                aluOP <= 4'b0011;
                imm<=1'b1;
            end
            6'b001101:  //ori
            begin
                regWE <= 1'b1;
                aluOP <= 4'b0100;
                imm<=1'b1;
            end
            6'b001110:  //xori
            begin
                regWE <= 1'b1;
                aluOP <= 4'b0101;
                imm<=1'b1;
            end
            6'b100011:  //lw, 寄存器内容加上偏移后数值不可以超出14位地址最大值,否则该行为是未定义的。
            begin
                ram2reg <= 1'b1;
                aluOP <= 4'b0001;
                sign_ext <= 1'b1;
                imm<=1'b1;
            end
            6'b101011:  //sw, 注意点同lw
            begin
                ramWE <= 1'b1;
                aluOP <= 4'b0001;
                sign_ext <= 1'b1;
                imm<=1'b1;
            end
            6'b000100:  //beq
            begin
                aluOP <= 4'b0010;
                sign_ext <= 1'b1;
                if(z != 0)
                    pcindex <= 2'b01;
            end
            6'b000101:  //bne
            begin
                aluOP <= 4'b0010;
                sign_ext <= 1'b1;
                if(z == 0)
                     pcindex <= 2'b01;
            end
            6'b001111:  //lui
            begin
                regWE <= 1'b1;
                imm <= 1'b1;
                aluOP <= 4'b1000;
            end
            6'b000010:  //j
            begin
                pcindex <= 2'b11;
            end
            6'b000011:  //jal
            begin
                jal <= 1'b1;
                pcindex <= 2'b11;
                regWE <= 1'b1;
            end
        endcase
    end
endmodule

//register模块:
module register(
    input clk,
    input oc,
    input [4:0] raddr1,     //读地址1
    input [4:0] raddr2,     //读地址2
    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]; //0号寄存器固定为32'b0
    //读端口1
    always @(*) 
    begin
        if(oc == 1'b1)
        begin
            rdata1 <= 32'b0;        
        end
        else if(raddr1 == 5'b00000)     //$0号寄存器只保存0
        begin
            rdata1 <= 32'b0;
        end
        else 
        begin
            rdata1 <= regts[raddr1];
        end
    end
    //读端口2
    always @(*)
        begin
            if(oc == 1'b1)
            begin
                rdata2 <= 32'b0;
            end
            else if(raddr2 == 5'b00000)
            begin
                rdata2 <= 32'b0;
            end
            else
            begin
                rdata2 <= regts[raddr2];
            end
        end
        
    always @(posedge clk)   //脉冲信号作用下才能写
        begin
            #1 if((we == 1'b1) &&(waddr != 5'b00000))      //判断使能端是否为1,是否为0号地址,0号寄存器不可写
            begin
                regts[waddr] <= wdata;
            end
        end
 initial
        $monitor($time,,"regts[0]=%d",regts[0]);
    initial
        $monitor($time,,"regts[2]=%d",regts[2]);    
    initial
        $monitor($time,,"regts[3]=%d",regts[3]); 
    initial
        $monitor($time,,"regts[4]=%d",regts[4]);
    initial
        $monitor($time,,"regts[5]=%d",regts[5]);
    initial
        $monitor($time,,"regts[6]=%d",regts[6]);
    initial
        $monitor($time,,"regts[7]=%d",regts[7]); 
endmodule 

//IOManager模块:
module IOManager(
    input [31:0] addr,  //地址
    input [31:0] din,   //内存输入数据
    input we,           //内存使能端
    input clk,          
    output [31:0] dout, //内存或开关获得数据
    input [3:0] switch, //开关输入数据
    output reg[31:0] displaydata    //输出到led数据
    );
    
    reg[31:0] indata, outdata;
    wire[31:0] ramout;
    wire ramWE;
    
    // assign enable = ~(|addr15:14[);这条可去可不去,我们的设计中是去除了enable
    ram my_ram(addr[13:0], din, clk, ramWE, ramout);
    assign dout = addr[15]?{{28{1'b0}}, switch}:ramout;
    assign ramWE = we&(~addr[14]); 
    
    always @(posedge clk)
    begin
    if((addr[14] == 1'b1) && we)
        displaydata <= din;
    end
endmodule

//RAM模块:
module ram(
    input [13:0] addr,
    input [31:0] wdata,
    input clk,
    //input ce,
    input we1,
    output reg [31:0] rdata
    );
    reg [13:0] ram[0:16383]; //地址空间为0~2^14-1
    always @(*)		//只要有输入就立即输出
      begin
          begin
              rdata <= ram[addr];
          end
      end
    always @(posedge clk)		//脉冲信号作用下才能写
        begin
            #1 if(we1 == 1'b1)  //判断使能端是否为1
            begin
                ram[addr] <= wdata;
            end
        end
endmodule



//alu模块:
module alu(
    input [31:0] a,
    input [31:0] b,
    input [3:0] operation,
    output [31:0] f,
    output z
    );
    reg[31:0]result;
    always@(*)
    begin
        case(operation)
            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=b<<16;
            4'b1001:result=$signed(b)<<<a;//算术左移
            4'b1010:result=$signed(b)>>>a;//算术右移
            default:result=32'b0;
        endcase
    end
    assign f=result;
    assign z=~(|result);
    
    initial
        $monitor($time,,"alu:a=%d,b=%d,operation=0b%b",a,b,operation);
    initial
        $monitor($time,,"alu:f=%d,z=0b%b",f,z);
endmodule

//extend模块:
module extend(
    input [15:0] immediate_16,
    input sign_ext,         //1:符号 0:零扩展
    output reg[31:0] immediate_32
    );   
    
    always @(*)
    begin
        case(sign_ext)
            1'b0:   //零扩展
            begin
                immediate_32 <= {16'h0000,immediate_16};
            end
            1'b1:   //符号扩展
            begin
                if(immediate_16[15] == 0)
                    immediate_32 <= {16'h0000,immediate_16};
                else
                    immediate_32 <= {16'hffff,immediate_16};
            end
        endcase
    end
    initial 
    $monitor($time,,"immediate_32=%h",immediate_32);
endmodule  

//selector模块:
module selector(
    input [31:0] pc4,       //顺序执行pc+4
    input [31:0] offset,    // bne、beq跳转
    input [31:0] rs,        //jr跳转
    input [25:0] target,    //jal、j跳转
    input [1:0] pcindex,
    output reg [31:0] nextAddr
    );
    
    always@(*)
    begin
        case(pcindex)
        2'b00:                  //顺序执行
        begin
            nextAddr <= pc4; 
        end
        2'b01:                  //beq、bne跳转
        begin
            nextAddr <= pc4 - 4 + (offset << 2);
        end
        2'b10:                  //jr跳转
        begin
            nextAddr <= rs;
        end
        2'b11:                  //ja1、j跳转
        begin
            nextAddr <= {pc4[31:28], 28'b0+target << 2};   //存疑
        end
        endcase
    end
    
    initial
        $monitor($time,,"offset=%b",offset);
endmodule

//selector2模块:
module selector2(
    input [31:0] a,
    input [31:0] b,
    input sel,
    output reg [31:0] result
    );
    always @(*)
    begin
        case(sel)
        1'b0:
        begin
            result <= a;
        end
        1'b1:
        begin
            result <= b;
        end
        endcase
    end
endmodule 

//仿真程序
 module cpu_sim(
);
    reg clk;
    reg rst=1'b1;
    reg oc;
reg[3:0] n=4'b1111; //输入所求斐波那契数在数列中的序号
    wire[31:0] result; //输出斐波那契数列结果
    //时钟信号仿真
    initial 
        begin
            clk=1'b0;
            forever
                #2 clk=~clk;
        end
    //cpu实例化,传参switch(n)
    cpu mycpu(clk,oc,rst,4'b1111, result);
initial
        begin
            #1 rst=1'b0;
            #4 rst=1'b1;
        End 
 initial
     $monitor($time, "ps: result is 0x%d", result);
endmodule

仿真结果:
在这里插入图片描述

  • 9
    点赞
  • 81
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值