【计算机组织与体系结构及其FPGA实现】实验二:给定指令系统的处理器设计-FPGA常见问题论坛-FPGA CPLD-ChipDebug

【计算机组织与体系结构及其FPGA实现】实验二:给定指令系统的处理器设计

 

 

系列文章目录

【计算机组织与体系结构】实验一:算术逻辑单元的实现
【计算机组织与体系结构】实验二:给定指令系统的处理器设计
【计算机组织与体系结构】实验三:流水线处理器
【计算机组织与体系结构】实验四:指令 CACHE 的设计与实现



一、实验目的

  • 掌握 Vivado 集成开发环境
  • 掌握 Verilog 语言
  • 掌握 FPGA 编程方法及硬件调试手段
  • 深刻理解处理器结构和计算机系统的整体工作原理

二、实验环境

Vivado 集成开发环境和龙芯 Artix-7 实验平台

三、实验内容

根据课程第五章 MIPS 指令集的基本实现,设计并实现一个符合实验指令的非流水处理器,包括 Verilog语言的实现和 FPGA 芯片的编程实现,要求该处理器可以通过所提供的自动测试环境。

1、处理器功能

本实验的任务是设计一个简单的 RISC 处理器,该处理器是在给定的指令集(与 MIPS32 类似)下构建的,支持 11 条指令。假定存储器分为数据缓冲存储器指令缓冲存储器,且都可以在一个时钟周期内完成一次同步存取操作,时钟信号和 CPU 相同。处理器的指令字长为 32 位,包含 32 个 32 位通用寄存器 R0~R31, 1 个32 位的指令寄存器 IR 和 1 个 32 位的程序计数器 PC, 1 个 256×32 位指令缓冲存储器, 1 个 256×32 位的数据缓冲存储器

2、指令系统定义

处理器所支持的指令包括 LW, SW, ADD, SUB, AND, OR, XOR, SLT, MOVZ, BEQ, J
其中仅有 LWSW 是字访存指令,所有的存储器访问都通过这两条指令完成; ADD、 SUB、 AND、 OR、 XOR、SLT、 MOVZ 是运算指令,他们都在处理器内部完成;BEQ 是分支跳转指令,根据寄存器的内容进行相对跳转;J 是无条件转移指令

1)指令说明

关于汇编指令的说明:
• rs 意味着 source register, rd 意味着 destination register;
• rt 有时(如运算指令)意味着第二个 source register,有时(如访存指令)意味着 target (source/destination)
register;
• 我们使用 [rs] 表示寄存器 rs 的内容;
另外,所有算数运算指令都执行有符号运算。

2)运算指令

(1)加法指令 ADD rd, rs, rt
在这里插入图片描述
该指令将两个源寄存器内容相加,结果送回目的寄存器的操作。
具体为: [rd] <- [rs] + [rt]

(2)减法指令 SUB rd, rs, rt
在这里插入图片描述
该指令将两个源寄存器内容相减,结果送回目的寄存器的操作。
具体为: [rd] <- [rs] – [rt]

(3)与运算指令 AND rd, rs, rt
在这里插入图片描述
该指令将两个源寄存器内容相与,结果送回目的寄存器的操作。
具体为: [rd] <- [rs] & [rt]

(4)或运算指令 OR rd, rs, rt
在这里插入图片描述
该指令将两个源寄存器内容相或,结果送回目的寄存器的操作。
具体为: [rd] <- [rs] | [rt]

(5)异或指令 XOR rd, rs, rt
在这里插入图片描述
该指令将两个源寄存器内容相异或,结果送回目的寄存器的操作。
具体为: [rd] <- [rs] ⊕ [rt]

(6)小于指令 SLT rd, rs, rt
在这里插入图片描述
该指令将两个源寄存器内容相比较,结果决定目的寄存器的值。
具体为: [rd] <- [rs] < [rt] ? 1 : 0

(7)条件移动指令 MOVZ rd, rs, rt
在这里插入图片描述
该指令根据其中一个源寄存器内容,决定另一个源寄存器的值是否写回目的寄存器。
具体为: if ([rt] == 0) then [rd] <- [rs]

Note: 注意:当 [rt] != 0 时,不对寄存器 rd 进行任何写回操作。

3)访存指令

Warning: 使用访存指令时,计算后的目标地址必须是访问数据单元大小的整数倍。对于 LW 和 SW 指令, ([base] + offset) % 4 == 0

(1)存数指令 SW rt, offset(base)
在这里插入图片描述
该指令将寄存器 rt 的内容存于主存单元中,对应的地址由 16 位偏移地址 offset 经符号拓展加上 base的内容生成。
具体操作为: Mem[[base] + offset] <- [rt]

(2)取数指令 LW rt, offset(base)
在这里插入图片描述
该指令将主存单元中的内容存于寄存器 rt,对应的地址由 16 位偏移地址 offset 经符号拓展加上 base
内容生成。
具体操作为: [rt] <- Mem[[base] + offset]

3)转移类指令

Warning:
• 和 MIPS32 指令集不同,我们给出的跳转指令没有延迟槽。
• 跳转指令使用 NPC(也就是 PC+4)参与跳转地址运算。

(1)条件转移(相等跳转)指令 BEQ rs, rt, offset
在这里插入图片描述
该指令根据寄存器 rs 和 rt 的内容决定下一条指令的地址,若两个寄存器内容相等,则 16 位偏移 offset扩充为 32 位,左移 2 位后与 NPC 相加,作为下一条指令的地址,否则程序按原顺序执行。
具体操作为: PC <- ([rs] == [rt]) ? [sign_extend(offset) << 2 + NPC] : NPC

(2)无条件转移指令 J target
在这里插入图片描述
该指令改变下一条指令的地址,地址由指令中的 26 位形式地址 instr_index 左移 2 位作为低 28 位,和NPC 的高 4 位拼接而成。
具体操作为: PC <- (NPC[31:28]) ## (instr_index << 2)

四、实验要求

要求根据以上给定的指令系统设计处理器,包括指令格式设计、操作的定义、 Verilog 语言的实现及 FPGA 编程实现。处理器设计实验要求按指定阶段进行。

五、设计思想

(一)CPU接口信号定义

填写下表

(二)处理器的设计方案

1、指令格式设计

处理器所支持的指令包括 LW, SW, ADD, SUB, AND, OR, XOR, SLT, MOVZ, BEQ, J。其中仅有 LW 和 SW 是字访存指令,所有的存储器访问都通过这两条指令完成; ADD、 SUB、 AND、 OR、 XOR、SLT、 MOVZ 是运算指令,他们都在处理器内部完成; BEQ 是分支跳转指令,根据寄存器的内容进行相对跳转;J 是无条件转移指令。
在这里插入图片描述
在这里插入图片描述

2、处理器结构设计框图及功能描述

在这里插入图片描述
该处理器可以实现11种指令,具体见上面表格

3、各功能模块结构设计框图及功能描述

(1)模块名称及功能:pc;指令寄存器,用来给出指令在指令存储器中的地址。
在这里插入图片描述

(2)模块名称及功能:npc;暂存PC输出的结果。
在这里插入图片描述

(3)模块名称及功能:符号位扩展signextension;将26位数据符号扩展为32位或者取出16位扩展为32位
在这里插入图片描述

(4)模块名称及功能:指令缓冲存储器imem;根据输入的地址来找到相应的指令
在这里插入图片描述

(5)模块名称及功能:算术逻辑单元alu;根据不同指令进行不同运算操作。
在这里插入图片描述

(6)模块名称及功能:数据存储器dmem;大小为256*32位,用来存储数据,在时钟上升沿,判断是否复位,若复位,则将数据缓冲存储器初始化,如果不复位,则根据写使能信号进行写操作。读操作不需要时钟信号。
在这里插入图片描述

(7)模块名称及功能:控制单元cu;根据输入的opcode、func、equal、radata、resetn, 来输出各种控制信号。
在这里插入图片描述

(8)模块名称及功能:32个32位寄存器堆regfile;可以进行两个寄存器的数据读出和一个寄存器的写回。
在这里插入图片描述

(9)模块名称及功能:二选一mux;对输入的两个32位数据进行选择,如果选择控制信号为1,则选择data1;如果为0,则选择data0。
在这里插入图片描述
(10)模块名称及功能:datatempstore;暂存数据imm、a、b、ir、aluoutput、lmd
在这里插入图片描述
(11)模块名称及功能:equal;比较数据是否相等,0表示不相等;1表示相等。
在这里插入图片描述

(12)模块名称及功能:writebackaddrmux;进行写回寄存器的选择
在这里插入图片描述

4、各模块输入输出接口信号定义

(1)模块名称及功能:pc;指令寄存器,用来给出指令在指令存储器中的地址。
在这里插入图片描述
(2)模块名称及功能:npc;暂存PC输出的结果。
在这里插入图片描述

(3)模块名称及功能:符号位扩展signextension;将26位数据符号扩展为32位或者取出16位扩展为32位
在这里插入图片描述

(4)模块名称及功能:指令缓冲存储器imem;根据输入的地址来找到相应的指令
在这里插入图片描述

(5)模块名称及功能:算术逻辑单元alu;根据不同指令进行不同运算操作。
在这里插入图片描述
(6)模块名称及功能:数据存储器dmem;大小为256*32位,用来存储数据,在时钟上升沿,判断是否复位,若复位,则将数据缓冲存储器初始化,如果不复位,则根据写使能信号进行写操作。读操作不需要时钟信号。
在这里插入图片描述
(7)模块名称及功能:控制单元cu;根据输入的opcode、func、equal、radata、resetn,
来输出各种控制信号。
在这里插入图片描述

(8)模块名称及功能:32个32位寄存器堆regfile;可以进行两个寄存器的数据读出和一个寄存器的写回。在这里插入图片描述

(9)模块名称及功能:二选一mux;对输入的两个32位数据进行选择,如果选择控制信号为1,则选择data1;如果为0,则选择data0。
在这里插入图片描述
(10)模块名称及功能:datatempstore;暂存数据,包括imm、a、b、ir、aluoutput、lmd
在这里插入图片描述
(11)模块名称及功能:equal;比较数据是否相等,0表示不相等;1表示相等。在这里插入图片描述

(12)模块名称及功能:writebackaddrmux;进行写回寄存器的选择
在这里插入图片描述

六、实验设计及测试

1、各模块的详细设计

(包括各模块功能详述,设计方法,Verilog语言实现等)
(1)模块名称及功能:pc;指令寄存器,用来给出指令在指令存储器中的地址。

module pc(
    input clk,
    input resetn,
    input[31:0] nextpc,
    output reg[31:0] currentpc
    );
    initial begin
        currentpc <= 0;
    end        
    always@(posedge clk)begin
        if(!resetn) currentpc <= 0; 
        else currentpc <= nextpc;
    end
endmodule

 

(2)模块名称及功能:npc;暂存PC输出的结果。

module npc(
    input[31:0] pc,
    output[31:0] pcadd4
    );
    assign pcadd4 = pc + 4;
endmodule

 

(3)模块名称及功能:符号位扩展signextension;将26位数据符号扩展为32位或者取出16位扩展为32位

module signextension(
    input is_J,//控制信号,如果为0,表示为sw/lw/beq(16->32),如果为1,表示为J(26->32)
    input [25:0]data,//待扩展的数据
    output [31:0]extendresult//扩展结果
);
    assign extendresult = (is_J)?{{6{data[25]}},data}:{{16{data[15]}},data[15:0]};//如果是J指令,则将26位扩展为32位
endmodule

 

(4)模块名称及功能:指令缓冲存储器imem;根据输入的地址来找到相应的指令

module imem(     input[31:0] pc,     output[31:0] instruction     );     reg [31:0] instmemory[255:0];     initial begin           $readmemh("D:/Users/Administrator/VivadoProjects/lab2/lab2.data/inst_data.txt",instmemory);//读取指令文件到inst_data     end     assign instruction = instmemory[pc>>2]; endmodule 

(5)模块名称及功能:算术逻辑单元alu;根据不同指令进行不同运算操作。

module alu(
    input[31:0] data0,
    input[31:0] data1,
    input[5:0] operation,
    output[31:0] result
);
    
    //存储不同指令的计算结果
    wire [31:0] result1;//add lw sw
    wire [31:0] result2;//sub
    wire [31:0] result3;//and
    wire [31:0] result4;//or
    wire [31:0] result5;//xor
    wire [31:0] result6;//slt
    wire [31:0] result7;//movz
    wire [31:0] result8;//beq
    wire [31:0] result9;//j
    wire [31:0] temp;
    
    //计算不同指令的结果
    assign result1 = data0 + data1;//add sw lw
    assign result2 = data0 - data1;
    assign result3 = data0 & data1;
    assign result4 = data0 | data1;
    assign result5 = data0 ^ data1;
    assign result6 = (data0 < data1)?32'b1 : 32'b0;
    assign result7 = (data1 == 0)? data0 : 32'b0;
    assign result8 = data0 + (data1 << 2);//beq
    assign temp = data1 << 2;
    assign result9 ={data0[31:26], temp[25:0]};//j
    
    //根据operation选择result
    assign result = ({32{operation == 6'b100000}} & result1) |//add
                    ({32{operation == 6'b100010}} & result2) |//sub
                    ({32{operation == 6'b100100}} & result3) |//and
                    ({32{operation == 6'b100101}} & result4) |//or
                    ({32{operation == 6'b100110}} & result5) |//xor
                    ({32{operation == 6'b101010}} & result6) |//slt
                    ({32{operation == 6'b001010}} & result7) |//movz
                    ({32{operation == 6'b111000}} & result1) |//lw sw
                    ({32{operation == 6'b101000}} & result8) |//beq
                    ({32{operation == 6'b110000}} & result9) ;//j      
endmodule

 

(6)模块名称及功能:数据存储器dmem;大小为256*32位,用来存储数据,在时钟上升沿,判断是否复位,若复位,则将数据缓冲存储器初始化,如果不复位,则根据写使能信号进行写操作。读操作不需要时钟信号。

module dmem(
    input[31:0] data,//待存入的数据
    input[31:0] address,//要读数据的地址
    input dmem_write_en,//0表示lw取数;1表示sw存数,即写使能有效
    input clk,
    input resetn,
    output[31:0] outputdata    
    );
    reg [31:0]datamemory[255:0];
    reg [31:0]static_memory[255:0];
    initial begin
        $readmemh("D:/Users/Administrator/VivadoProjects/lab2/lab2.data/data_data.txt",datamemory);
        $readmemh("D:/Users/Administrator/VivadoProjects/lab2/lab2.data/data_data.txt",static_memory);
    end
    reg[7:0] addr;
    initial addr = 0;
    always@(posedge clk)begin
        if(!resetn)
            datamemory[addr] <= static_memory[addr];
        else if(dmem_write_en)
            datamemory[address>>2]<=data;//sw
    end
    always@(posedge clk) begin
        if(!resetn) addr <=addr +1;
    end
    assign outputdata = datamemory[address>>2];//lw
endmodule

 

(7)模块名称及功能:控制单元cu;根据输入的opcode、func、equal、radata、resetn,
来输出各种控制信号。

module cu(
    input[5:0] opcode,
    input[10:0] func,
    input equal,
    input[31:0] rtdata,
    input resetn,
    
    output pc_select,
    output lw_select,
    output reg_wen,
    output a_mux_select,
    output b_mux_select,
    output dmem_write_en,
    output is_J,
    output[5:0] operation
    );
    
    //opcode
    parameter [5:0] ALU = 6'b000000;
    parameter [5:0] SW = 6'b101011;
    parameter [5:0] LW = 6'b100011;
    parameter [5:0] BEQ = 6'b000100;
    parameter [5:0] J = 6'b000010;
    
    //func
    parameter [5:0] MOVZFUNC = 6'b001010;
    
    //用来暂存不同指令应该输出的operation
    wire [5:0] operation1;
    wire [5:0] operation2;
    wire [5:0] operation3;
    wire [5:0] operation4;
    
    //列举所有operation情况
    assign operation1 = func[5:0];//alu指令后6位作为operation标志
    assign operation2 = 6'b101000;//beq
    assign operation3 = 6'b110000;//j
    assign operation4 = 6'b111000;//lw sw
    assign operation = ({6{opcode == ALU}}& operation1) |
                       ({6{opcode == BEQ}}& operation2) |
                       ({6{opcode == J}}& operation3) |
                       ({6{opcode == LW}}& operation4) |
                       ({6{opcode == SW}}& operation4);
                       
    //输出各种信号
    assign pc_select = (opcode == J || (opcode == BEQ && equal ==1 ))? 1'b1:1'b0;
    assign lw_select = (opcode == LW)? 1'b1:1'b0;
    assign reg_wen = (opcode == LW || (opcode == ALU &&!(func[5:0]==MOVZFUNC && rtdata !=0))) ? 1'b1 :1'b0;
    assign a_mux_select = (opcode ==J || opcode == BEQ) ? 1'b1:1'b0;//1表示需要跳转
    assign b_mux_select = (opcode == J || opcode == BEQ || opcode == LW || opcode == SW) ? 1'b1:1'b0;//表示需要用到偏移
    assign dmem_write_en = (opcode == SW)? 1'b1:1'b0;//只有sw往dmem写
    assign is_J = (opcode == J)? 1'b1:1'b0;
endmodule

 

(8)模块名称及功能:32个32位寄存器堆regfile;可以进行两个寄存器的数据读出和一个寄存器的写回。

module regfile(
    input clk ,
    input resetn ,
    // READ PORT 1
    input [4 :0] raddr1,
    output [31:0] rdata1,
    // READ PORT 2
    input [4 :0] raddr2,
    output [31:0] rdata2,
    // WRITE PORT
    input wen , //write enable, active high
    input [4 :0] waddr ,
    input [31:0] wdata
);
    reg [31:0] rf [31:1];//R0恒0 不可操作
    
    // initial with $readmemh is synthesizable here
    initial begin
        $readmemh("D:/Users/Administrator/VivadoProjects/lab2/lab2.data/reg_data.txt", rf);
    end
    
    //WRITE
    always @(posedge clk) begin
            if (wen && |waddr) begin // don't write to $0
                rf[waddr] <= wdata;
        end
    end 
    
    //READ OUT 1
    assign rdata1 = (raddr1 == 5'b0) ? 32'b0 : rf[raddr1];
    
    //READ OUT 2
    assign rdata2 = (raddr2 == 5'b0) ? 32'b0 : rf[raddr2];
    
endmodule

 

(9)模块名称及功能:二选一mux;对输入的两个32位数据进行选择,如果选择控制信号为1,则选择data1;如果为0,则选择data0。

module mux(
    input[31:0] data0,
    input[31:0] data1,
    input selectsignal,
    output[31:0] result
    );
    assign result = (selectsignal)?data1:data0;
    
endmodule

 

(10)模块名称及功能:datatempstore;暂存数据,包括imm、a、b、ir、aluoutput、lmd

module datatempstore(
    input[31:0] inputdata,
    output[31:0] outputdata
    );
    assign outputdata = inputdata;
endmodule

 

(11)模块名称及功能:equal;比较数据是否相等,0表示不相等;1表示相等。

module equal(
    input[31:0] data0,
    input[31:0] data1,
    output result
    );
    assign result = (data0 == data1)? 1 : 0;
endmodule

 

(12)模块名称及功能:writebackaddrmux;进行写回寄存器的选择

module writebackaddrmux(
    input [4:0] addr0,
    input [4:0] addr1,
    input lw_select,
    output [4:0] result
    );
    assign result = (lw_select)? addr1:addr0;
endmodule

 

2、各模块的功能测试

(每个模块作为一个部分,包括测试方案、测试过程和测试波形等)
(1)alu_tb 对模块alu进行仿真
测试方案:

module alu_tb;
    //输入
    reg [31:0] data0;
    reg [31:0] data1;
    reg [5:0] operation;
    //输出
    wire [31:0] result;
    initial begin
        data0 = 31'hfffffff0;
        data1 = 32'h00000002;
    #10 operation = 6'b100000;//add
    #10 operation = 6'b111000;//sw lw
    #10 operation = 6'b100010;//sub
    #10 operation = 6'b100100;//and
    #10 operation = 6'b100101;//or
    #10 operation = 6'b100110;//xor
    #10 operation = 6'b101010;//slt
    #10 operation = 6'b001010;//movz
    #10 operation = 6'b101000;//beq
    #10 operation = 6'b110000;//jmp
    end
    alu alu_0(
        .operation(operation),
        .data0(data0),
        .data1(data1),
        .result(result)
    );
endmodule

 

仿真波形:
在这里插入图片描述
(2) cu_tb对模块cu进行仿真
测试方案:

module cu_tb;
    //输入
    reg [5:0] opcode;
    reg [10:0] func;
    reg equal;
    reg [31:0] rtdata;
    //输出
    wire pc_select;
    wire lw_select;
    wire reg_wen;
    wire a_mux_select;
    wire b_mux_select;
    wire dmem_write_en;
    wire is_J;
    wire [5:0] operation;
    
    initial begin
        //初始化
        opcode = 6'b000000;
        func = 11'b00000100000;
        equal = 1'b0;
        rtdata = 32'b0;
    #10 //add
        opcode = 6'b000000;
        func = 11'b00000100000;
        equal = 1'b0;
    #10 //sub
        opcode = 6'b000000;
        func = 11'b00000100010;
        equal = 1'b0;
    #10 //and
        opcode = 6'b000000;
        func = 11'b00000100100;
        equal = 1'b0;
    #10 //or
        opcode = 6'b000000;
        func = 11'b00000100101;
        equal = 1'b0;
    #10 //xor
        opcode = 6'b000000;
        func = 11'b00000100110;
        equal = 1'b0;
    #10 //slt
        opcode = 6'b000000;
        func = 11'b00000101010;
        equal = 1'b0;
    #10 //movz rtdata==0
        opcode = 6'b000000;
        func = 11'b00000001010;
        equal = 1'b0;
        rtdata = 32'b0;
    #10 //movz rtdata!=0
        opcode = 6'b000000;
        func = 11'b00000001010;
        equal = 1'b0;
        rtdata = 32'b1;
    #10 //sw
        opcode = 6'b101011;
    #10 //lw
        opcode = 6'b100011;
    #10 //beq equal == 1
        opcode = 6'b000100;
        equal = 1'b1;
    #10 //beq equal == 0
        opcode = 6'b000100;
        equal = 1'b0;
    #10 //j
        opcode = 6'b000010;
    end
    
    cu cu_0(
        .opcode(opcode),
        .func(func),
        .equal(equal),
        .rtdata(rtdata),
        .resetn(resetn),
        
        .pc_select(pc_select),
        .lw_select(lw_select),
        .reg_wen(reg_wen),
        .a_mux_select(a_mux_select),
        .b_mux_select(b_mux_select),
        .dmem_write_en(dmem_write_en),
        .is_J(is_J),
        .operation(operation) 
    );
endmodule

 

仿真波形:
在这里插入图片描述

3、系统的详细设计

(包括系统功能详述,设计方法,Verilog语言实现等)

module cpu(
     input clk , // clock, 100MHz
    (*mark_debug = "true"*)input resetn , // active high
    // debug signals
    (*mark_debug = "true"*)output [31:0] debug_wb_pc , // 当前正在执行指令的 PC
    (*mark_debug = "true"*)output debug_wb_rf_wen , // 当前通用寄存器组的写使能信号
    (*mark_debug = "true"*)output [4 :0] debug_wb_rf_addr, // 当前通用寄存器组写回的寄存器编号
    (*mark_debug = "true"*)output [31:0] debug_wb_rf_wdata // 当前指令需要写回的数据
    );
    
    //存储指令的各个部分
    wire [5:0] opcode;//cu
    wire [4:0] raddr1;
    wire [4:0] raddr2;
    wire [4:0] raddr3;
    wire [10:0] func;//cu
    wire [25:0] instr_index;
    
    wire [31:0] current_instruction;//当前指令,从ir取出
    assign opcode = current_instruction[31:26];
    assign raddr1  =  current_instruction[25:21];
    assign raddr2  =  current_instruction[20:16];
    assign raddr3  =  current_instruction[15:11];
    assign  func =  current_instruction[10:0];
    assign  instr_index = current_instruction[25:0];
    
    //定义各种线
    //cu的输出
    wire pc_select;
    wire lw_select;
    wire reg_wen;
    wire a_mux_select;
    wire b_mux_select;
    wire dmem_write_en;
    wire [5:0] operation;
    wire is_J;//控制signextension
    
    wire equal;
    wire [31:0] nextpc;//pc输入,pc_mux输出
    wire [31:0] currentpc;//pc输出,imem\npc输入
    wire [31:0] pcadd4;//pc+4
    wire [31:0] output_instruction;//imem读出的指令
    wire [31:0] immin;//imm输入
    wire [31:0] immout;//imm输出
    wire [31:0] aout;//A输出
    wire [31:0] bout;//B输出
    wire [31:0] rdata1;//register读出的第一个数,A输入
    wire [31:0] rdata2;//register读出的第二个数,B输入
    wire [4:0] waddr;//register要写回的寄存器编号,writebackaddr_mux输出
    wire [31:0] wdata;//register要写回的寄存器内容,data_mux输出
    wire [31:0] alua;//alu第一个操作数,a_mux输出
    wire [31:0] alub;//alu第二个操作数,b_mux输出
    wire [31:0] result;//alu的计算结果
    wire [31:0] aluresultdata;//aluoutput输出
    wire [31:0] lmdin;//dmem读出的数据,lmd输入
    wire [31:0] lmdout;//lmd输出,data_mux输入
    
    //cpu输出
    assign debug_wb_pc = currentpc;
    assign debug_wb_rf_wen = reg_wen;
    assign debug_wb_rf_addr = waddr;
    assign debug_wb_rf_wdata = wdata;
    
    //各个模块的连线    
    pc U_pc(
        .clk(clk),
        .resetn(resetn),
        .nextpc(nextpc),
        .currentpc(currentpc)
    );
    npc U_npc(
        .pc (currentpc),
        .pcadd4 (pcadd4)
    );
    
    imem U_imem(
        .pc(currentpc),
        .instruction(output_instruction)
    );
    
    datatempstore U_ir(
        .inputdata (output_instruction),
        .outputdata (current_instruction)
    );
    
    regfile U_regfile(
        .clk(clk),
        .resetn(resetn),
        .raddr1(raddr1),
        .rdata1(rdata1),
        .raddr2(raddr2),
        .rdata2(rdata2),
        .wen(reg_wen),
        .waddr(waddr) ,
        .wdata(wdata)
    );
    
    datatempstore U_a(
        .inputdata (rdata1),
        .outputdata (aout)
    );
    
    datatempstore U_b(
        .inputdata (rdata2),
        .outputdata (bout)
    );    
    
    signextension U_signextension(
        .is_J(is_J),
        .data(instr_index),
        .extendresult(immin)
    );
    
    datatempstore U_imm(
        .inputdata (immin),
        .outputdata (immout)
    );    
    
    equal U_equal(
        .data0(aout),  
        .data1(bout),
        .result(equal)
    );  
   
    mux U_a_mux(
        .data0(aout),
        .data1(pcadd4),
        .selectsignal(a_mux_select),
        .result(alua)
    );
    
    mux U_b_mux(
        .data0(bout),
        .data1(immout),
        .selectsignal(b_mux_select),
        .result(alub)
    );
    
    alu U_alu(
        .data0(alua),
        .data1(alub),
        .operation(operation),
        .result(result)
    );
    
    datatempstore U_aluoutput(
        .inputdata (result),
        .outputdata (aluresultdata)
    ); 
    
    dmem U_dmem(
        .data(bout),
        .address(aluresultdata),
        .dmem_write_en(dmem_write_en),
        .clk(clk),
        .resetn(resetn),
        .outputdata(lmdin)
    );
    
    datatempstore U_lmd(
        .inputdata (lmdin),
        .outputdata (lmdout)
    ); 
    
    mux U_pc_mux(
        .data0(pcadd4),
        .data1(aluresultdata),
        .selectsignal(pc_select),
        .result(nextpc)
    );
    
    mux U_data_mux(
        .data0(aluresultdata),
        .data1(lmdout),
        .selectsignal(lw_select),
        .result(wdata)
    );
    
    writebackaddrmux U_writebackaddrmux(
        .addr0(raddr3),
        .addr1(raddr2),
        .lw_select(lw_select),
        .result(waddr)
    );      
    
    cu U_cu(
        .opcode(opcode),
        .resetn(resetn),
        .func(func),
        .equal(equal),
        .rtdata(rdata2),
        
        .pc_select(pc_select),
        .lw_select(lw_select),
        .reg_wen(reg_wen),
        .a_mux_select(a_mux_select),
        .b_mux_select(b_mux_select),
        .dmem_write_en(dmem_write_en),
        .operation(operation),
        .is_J(is_J)
    );
   
    
endmodule

 

4、系统的功能测试

(包括系统整体功能的测试方案、测试过程和测试波形等)

测试方案:本实验已经提供了测试代码,观察代码发现,首先resetn信号置为0,表示重置,即需要初始化,等到2000ns时,重置信号resetn变成1,这时我们的指令才开始执行,即调用cpu_top模块来进行测试。

`timescale 1ns / 1ps

module cpu_tb(

    );

//-----Clock and reset signal simulation-----
//signals
reg clk;
reg resetn;
//simulation
initial begin
    clk    = 1'b0;
    resetn = 1'b0;
    #2000;
    resetn = 1'b1;
end

always #5 clk = ~clk;

cpu_top U_cpu_top(
        .clk     (clk     ),
        .reset   (resetn  )
    );

//-----monitor test-----
initial
begin
    $timeformat(-9,0," ns",10);
    while(!resetn) #5;
    $display("==============================================================");
    $display("Test begin!");
    #10000;
end

endmodule

 

测试波形如下:
在这里插入图片描述在这里插入图片描述

七、测试结果及实验分析

1、处理器仿真测试波形(整体)

在这里插入图片描述在这里插入图片描述

2、FPGA编程下载

(1) 编写处理器功能测试程序,包括助记符和二进制代码。
对于实验二,提供了一个自动测试环境,包含了所设计的11中指令。cpu_tb文件具体设计如下,调用了cpu_top函数。观察发现,时钟信号clk是每5ns进行取反。前2000ns里面resetn信号一直为0,直到第2000ns resetn信号才变为1,指令才开始向下执行。

`timescale 1ns / 1ps

module cpu_tb(

    );

//-----Clock and reset signal simulation-----
//signals
reg clk;
reg resetn;
//simulation
initial begin
    clk    = 1'b0;
    resetn = 1'b0;
    #2000;
    resetn = 1'b1;
end

always #5 clk = ~clk;

cpu_top U_cpu_top(
        .clk     (clk     ),
        .reset   (resetn  )
    );

//-----monitor test-----
initial
begin
    $timeformat(-9,0," ns",10);
    while(!resetn) #5;
    $display("==============================================================");
    $display("Test begin!");
    #10000;
end

endmodule

 

(2) 上板波形记录与解释
上板情况如下,开发板上一排单色灯同时亮起:
在这里插入图片描述
对4个信号的抓取情况如下:首先启动逻辑分析仪,然后按下复位键,可以观察到捕捉到了后半阶段的波形,data的输出与之前的仿真结果相同,pc表示指令地址,也是每次加4,写使能信号和addr的输入也都符合之前的分析。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

八、实验总结

这次实验通过编写verilog实现了一个非流水线单周期处理器,本次实验细节非常多,需要考虑的事情也很多,也使我体会到了模块化编程的简便之处。通过自己动手设计一个处理器,是我更加深刻地学习到了其中的细节,体会到了乐趣。总之,这次实验使我收获很多。

 

请登录后发表评论

    没有回复内容