【计算机组成原理】实验4:单周期CPU(Verilog)中海大

【计算机组成原理】实验4

使用Verilog语言实现一个单周期CPU,测试平台:Vivado

①部分代码:

single_cycle_cpu.v:

`timescale 1ns / 1ps

`define STARTADDR 32'd0  // 程序起始地址
module single_cycle_cpu(
    input clk,    // 时钟
    input resetn,  // 复位信号,低电平有效

    //display data
    input  [ 4:0] rf_addr,
    input  [31:0] mem_addr,
    output [31:0] rf_data,
    output [31:0] mem_data,
    output [31:0] cpu_pc,
    output [31:0] cpu_inst
    );

//---------------------------------{取指}begin------------------------------------//
    reg  [31:0] pc;
    wire [31:0] next_pc;
    wire [31:0] seq_pc;
    wire [31:0] jbr_target;
    wire jbr_taken;

    // 下一指令地址:seq_pc=pc+4
    assign seq_pc[31:2]    = pc[31:2] + 1'b1;
    assign seq_pc[1:0]     = pc[1:0];
    // 新指令:若指令跳转,为跳转地址;否则为下一指令
    assign next_pc = jbr_taken ? jbr_target : seq_pc;
    always @ (posedge clk)    // PC程序计数器
    begin
        if (!resetn) begin
            pc <= `STARTADDR; // 复位,取程序起始地址
        end
        else begin
            pc <= next_pc;    // 不复位,取新指令
        end
    end

    wire [31:0] inst_addr;
    wire [31:0] inst;
    assign inst_addr = pc;  // 指令地址:指令长度32位
    inst_rom inst_rom_module(         // 指令存储器
        .addr      (inst_addr[6:2]),  // I, 5,指令地址
        .inst      (inst          )   // O, 32,指令
    );
    assign cpu_pc = pc;       //display pc
    assign cpu_inst = inst;
//----------------------------------{取指}end-------------------------------------//

//---------------------------------{译码}begin------------------------------------//
    wire [5:0] op;      //操作码 
    wire [4:0] rs;       // 源操作数1
    wire [4:0] rt;       // 源操作数2
    wire [4:0] rd;       // 目的操作数
    wire [4:0] sa;       // 特殊域,可能存放偏移量
    wire [5:0] funct;    // 功能码
    wire [15:0] imm;     // 立即数
    wire [15:0] offset;  // 地址偏移
    wire [25:0] target;  // 目标地址
    
    //补充以上变量的赋值,即译码 inst
    assign op     = inst[31:26];  // 操作码
    assign rs     = inst[25:21];  // 源操作数1
    assign rt     = inst[20:16];  // 源操作数2
    assign rd     = inst[15:11];  // 目标操作数
    assign sa     = inst[10:6];   // 特殊域,可能存放偏移量
    assign funct  = inst[5:0];    // 功能码
    assign imm    = inst[15:0];   // 立即数
    assign offset = inst[15:0];   // 地址偏移量
    assign target = inst[25:0];   // 目标地址

    wire op_zero;  // 操作码全0
    wire sa_zero;  // sa域全0
    assign op_zero = ~(|op);
    assign sa_zero = ~(|sa);
    
    // 实现指令列表
    wire inst_ADDU, inst_SUBU , inst_SLT, inst_AND;
    wire inst_NOR , inst_OR   , inst_XOR, inst_SLL;
    wire inst_SRL , inst_ADDIU, inst_BEQ, inst_BNE;
    wire inst_LW  , inst_SW   , inst_LUI, inst_J;
    
    //补充:
    assign inst_ADDU  = op_zero & sa_zero    & (funct == 6'b100001);// 无符号加法
    assign inst_SUBU  = op_zero & sa_zero    & (funct == 6'b100011);// 无符号减法
    assign inst_SLT   = op_zero & sa_zero    & (funct == 6'b101010);// 小于则置位
    assign inst_AND   = op_zero & sa_zero    & (funct == 6'b100100);// 逻辑与运算
    assign inst_NOR   = op_zero & sa_zero    & (funct == 6'b100111);// 逻辑或非运算
    assign inst_OR    = op_zero & sa_zero    & (funct == 6'b100101);// 逻辑或运算
    assign inst_XOR   = op_zero & sa_zero    & (funct == 6'b100110);// 逻辑异或运算
    assign inst_SLL   = op_zero & (rs==5'd0) & (funct == 6'b000000);// 逻辑左移
    assign inst_SRL   = op_zero & (rs==5'd0) & (funct == 6'b000010);// 逻辑右移
    assign inst_ADDIU = (op == 6'b001001);                  // 立即数无符号加法
    assign inst_BEQ   = (op == 6'b000100);                  // 判断相等跳转
    assign inst_BNE   = (op == 6'b000101);                  // 判断不等跳转
    assign inst_LW    = (op == 6'b100011);                  // 从内存装载
    assign inst_SW    = (op == 6'b101011);                  // 向内存存储
    assign inst_LUI   = (op == 6'b001111);                  // 立即数装载高半字节
    assign inst_J     = (op == 6'b000010);                  // 直接跳转

    // 无条件跳转判断
    wire        j_taken;
    wire [31:0] j_target;
    assign j_taken  = inst_J;
    // 补充:无条件跳转目标地址:PC={PC[31:28],target<<2}
    assign j_target = {pc[31:28], target, 2'b00};

    //分支跳转
    wire        beq_taken;
    wire        bne_taken;
    wire [31:0] br_target;
    // 补充
    assign beq_taken = (rs_value == rt_value);       // BEQ跳转条件:GPR[rs]=GPR[rt]
    assign bne_taken = ~beq_taken;                   // BNE跳转条件:GPR[rs]≠GPR[rt]
    assign br_target[31:2] = pc[31:2] + {{14{offset[15]}}, offset};
    assign br_target[1:0]  = pc[1:0];    // 分支跳转目标地址:PC=PC+offset<<2

    //补充:跳转指令的跳转信号和跳转目标地址
    assign jbr_taken = j_taken              // 指令跳转:无条件跳转 或 满足分支跳转条件
                    | inst_BEQ & beq_taken
                    | inst_BNE & bne_taken;
    assign jbr_target = j_taken ? j_target : br_target;

    // 寄存器堆
    wire rf_wen;
    wire [4:0] rf_waddr;
    wire [31:0] rf_wdata;
    wire [31:0] rs_value, rt_value;

    regfile rf_module(
        .clk    (clk      ),  // I, 1
        .wen    (rf_wen   ),  // I, 1
        .raddr1 (rs       ),  // I, 5
        .raddr2 (rt       ),  // I, 5
        .waddr  (rf_waddr ),  // I, 5
        .wdata  (rf_wdata ),  // I, 32
        .rdata1 (rs_value ),  // O, 32
        .rdata2 (rt_value ),   // O, 32

        //display rf
        .test_addr(rf_addr),
        .test_data(rf_data)
    );
    
    
    // 传递到执行模块的ALU源操作数和操作码
    wire inst_add, inst_sub, inst_slt,inst_sltu;
    wire inst_and, inst_nor, inst_or, inst_xor;
    wire inst_sll, inst_srl, inst_sra,inst_lui;
    assign inst_add = inst_ADDU | inst_ADDIU | inst_LW | inst_SW; // 做加法运算指令
    assign inst_sub = inst_SUBU; // 减法
    assign inst_slt = inst_SLT;  // 小于置位
    assign inst_sltu= 1'b0;      // 暂未实现
    assign inst_and = inst_AND;  // 逻辑与
    assign inst_nor = inst_NOR;  // 逻辑或非
    assign inst_or  = inst_OR;   // 逻辑或
    assign inst_xor = inst_XOR;  // 逻辑异或
    assign inst_sll = inst_SLL;  // 逻辑左移
    assign inst_srl = inst_SRL;  // 逻辑右移
    assign inst_sra = 1'b0;      // 暂未实现
    assign inst_lui = inst_LUI;  // 立即数装载高位

    wire [31:0] sext_imm;
    wire   inst_shf_sa;    //使用sa域作为偏移量的指令
    wire   inst_imm_sign;  //对立即数作符号扩展的指令
    assign sext_imm      = {{16{imm[15]}}, imm};// 立即数符号扩展
    assign inst_shf_sa   = inst_SLL | inst_SRL;
    assign inst_imm_sign = inst_ADDIU | inst_LUI | inst_LW | inst_SW;
    

    wire [31:0] alu_operand1;
    wire [31:0] alu_operand2;
    wire [11:0] alu_control;
    assign alu_operand1 = inst_shf_sa ? {27'd0,sa} : rs_value;
    assign alu_operand2 = inst_imm_sign ? sext_imm : rt_value;
    assign alu_control = {inst_add,        // ALU操作码,独热编码
                          inst_sub,
                          inst_slt,
                          inst_sltu,
                          inst_and,
                          inst_nor,
                          inst_or, 
                          inst_xor,
                          inst_sll,
                          inst_srl,
                          inst_sra,
                          inst_lui};
//----------------------------------{译码}end-------------------------------------//

//---------------------------------{执行}begin------------------------------------//
    wire [31:0] alu_result;

    alu alu_module(
        .alu_control  (alu_control ),  // I, 12, ALU控制信号
        .alu_src1     (alu_operand1),  // I, 32, ALU操作数1
        .alu_src2     (alu_operand2),  // I, 32, ALU操作数2
        .alu_result   (alu_result  )   // O, 32, ALU结果
    );
//----------------------------------{执行}end-------------------------------------//
    
//---------------------------------{访存}begin------------------------------------//
    wire [3 :0] dm_wen;
    wire [31:0] dm_addr;
    wire [31:0] dm_wdata;
    wire [31:0] dm_rdata;
    assign dm_wen   = {4{inst_SW}} & resetn;    // 内存写使能,非resetn状态下有效
    assign dm_addr  = alu_result;               // 内存写地址,为ALU结果
    assign dm_wdata = rt_value;                 // 内存写数据,为rt寄存器值
    data_ram data_ram_module(
        .clk   (clk         ),  // I, 1,  时钟
        .wen   (dm_wen      ),  // I, 1,  写使能
        .addr  (dm_addr[6:2]),  // I, 32, 读地址
        .wdata (dm_wdata    ),  // I, 32, 写数据
        .rdata (dm_rdata    ),  // O, 32, 读数据

        //display mem
        .test_addr(mem_addr[6:2]),
        .test_data(mem_data     )
    );
//----------------------------------{访存}end-------------------------------------//

//---------------------------------{写回}begin------------------------------------//
    wire inst_wdest_rt;   // 寄存器堆写入地址为rt的指令
    wire inst_wdest_rd;   // 寄存器堆写入地址为rd的指令
    //补充
    assign inst_wdest_rt = inst_ADDIU | inst_LW | inst_LUI;
    assign inst_wdest_rd = inst_ADDU | inst_SUBU | inst_SLT | inst_AND | inst_NOR
                          | inst_OR   | inst_XOR  | inst_SLL | inst_SRL; 
    
    //补充
    assign rf_wen   = (inst_wdest_rt | inst_wdest_rd) & resetn; // 寄存器堆写使能信号,非复位状态下有效
    assign rf_waddr = inst_wdest_rd ? rd : rt;        // 寄存器堆写地址rd或rt
    assign rf_wdata = inst_LW ? dm_rdata : alu_result;// 写回结果,为load结果或ALU结果
//----------------------------------{写回}end-------------------------------------//
endmodule

TestBench.v:

`timescale 1ns / 1ps

module tb;

    // Inputs
    reg clk;
    reg resetn;
    reg [4:0] rf_addr;
    reg [31:0] mem_addr;

    // Outputs
    wire [31:0] rf_data;
    wire [31:0] mem_data;
    wire [31:0] cpu_pc;
    wire [31:0] cpu_inst;

    // Instantiate the Unit Under Test (UUT)
    single_cycle_cpu uut (
        .clk(clk), 
        .resetn(resetn), 
        .rf_addr(rf_addr), 
        .mem_addr(mem_addr), 
        .rf_data(rf_data), 
        .mem_data(mem_data), 
        .cpu_pc(cpu_pc), 
        .cpu_inst(cpu_inst)
    );

    initial begin
        // Initialize Inputs
        clk = 1;
        resetn = 0;
        rf_addr = 0;
        mem_addr = 0;
        
        #50;
        resetn = 1;
    
        rf_addr = 1;
        #10;
        rf_addr = 2;
        #10;
        rf_addr = 3;
        #10;       
        rf_addr = 4;
        #10;  
        rf_addr = 5;
        #10;
        mem_addr = 32'H00000014;
        #10;          
        rf_addr = 6;
        #10;      
        rf_addr = 7;
        #10;      
        rf_addr = 8;
        #10;       
        rf_addr = 9;
        #10;
        mem_addr = 32'H0000001c;   
        #10;      
        rf_addr = 10;
        #10;       
        rf_addr = 11;
        mem_addr = 32'H00000000;
        #10;
      
    end
    always #5 clk=~clk;
endmodule

inst_rom.v:

`timescale 1ns / 1ps

module inst_rom(
    input      [4 :0] addr, // 指令地址
    output reg [31:0] inst       // 指令
    );

    wire [31:0] inst_rom[19:0];  // 指令存储器,字节地址7'b000_0000~7'b111_1111
    //------------- 指令编码 ---------|指令地址|--- 汇编指令 -----|- 指令结果 -----//
    assign inst_rom[ 0] = 32'h24010001; // 00H: addiu $1 ,$0,#1   | $1 = 0000_0001H
    assign inst_rom[ 1] = 32'h00011100; // 04H: sll   $2 ,$1,#4   | $2 = 0000_0010H
    assign inst_rom[ 2] = 32'h00411821; // 08H: addu  $3 ,$2,$1   | $3 = 0000_0011H
    assign inst_rom[ 3] = 32'h00022082; // 0CH: srl   $4 ,$2,#2   | $4 = 0000_0004H
    assign inst_rom[ 4] = 32'h00642823; // 10H: subu  $5 ,$3,$4   | $5 = 0000_000DH
    assign inst_rom[ 5] = 32'hAC250013; // 14H: sw    $5 ,#19($1) | Mem[0000_0014H] = 0000_000DH
    assign inst_rom[ 6] = 32'h00A23027; // 18H: nor   $6 ,$5,$2   | $6 = FFFF_FFE2H
    assign inst_rom[ 7] = 32'h00C33825; // 1CH: or    $7 ,$6,$3   | $7 = FFFF_FFF3H
    assign inst_rom[ 8] = 32'h00E64026; // 20H: xor   $8 ,$7,$6   | $8 = 0000_0011H
    assign inst_rom[ 9] = 32'hAC08001C; // 24H: sw    $8 ,#28($0) | Mem[0000_001CH] = 0000_0011H
    assign inst_rom[10] = 32'h00C7482A; // 28H: slt   $9 ,$6,$7   | $9 = 0000_0001H
    assign inst_rom[11] = 32'h08000000; // 4CH: j     00H         | 跳转指令00H

    //读指令,取4字节
    always @(*)
    begin
        case (addr)
            5'd0 : inst <= inst_rom[0 ];
            5'd1 : inst <= inst_rom[1 ];
            5'd2 : inst <= inst_rom[2 ];
            5'd3 : inst <= inst_rom[3 ];
            5'd4 : inst <= inst_rom[4 ];
            5'd5 : inst <= inst_rom[5 ];
            5'd6 : inst <= inst_rom[6 ];
            5'd7 : inst <= inst_rom[7 ];
            5'd8 : inst <= inst_rom[8 ];
            5'd9 : inst <= inst_rom[9 ];
            5'd10: inst <= inst_rom[10];
            5'd11: inst <= inst_rom[11];
            5'd12: inst <= inst_rom[12];
            5'd13: inst <= inst_rom[13];
            5'd14: inst <= inst_rom[14];
            5'd15: inst <= inst_rom[15];
            5'd16: inst <= inst_rom[16];
            5'd17: inst <= inst_rom[17];
            5'd18: inst <= inst_rom[18];
            5'd19: inst <= inst_rom[19];
            default: inst <= 32'd0;
        endcase
    end
endmodule

其他源文件包括:(regfile.v)(alu.v)(adder.v)(data_ram.v)

实验要求中已给出。

(附:全文文件下载地址:(88条消息) 【计算机组成原理实验】单周期cpu的实现_源码文件-单片机文档类资源-CSDN文库

②仿真图像及其分析:

使能==1时开始,

先向$1写入数据0000_0001H,

然后将$1数据逻辑左移并存入$2,

然后将$1与$2相加存于$3,

然后将$2逻辑右移存入$4,

随后$3减$4,存入$5,将结果存入地址为0000_0014H的mem中,

随后将$5和$2做或非运算,存入$6,

随后将$6和$3做或运算,存入$7,

随后将$7和$6做异或运算,存入$8,

随后将$8中数据写入地址为0000_001CH的mem中。

最后进行跳转指令跳转到开头的00H指令。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值