西工大计组实验单周期CPU

单周期CPU

最近看到不少学弟学妹浏览了我的两篇博客
流水线
单周期
此处加个声明,只提供参考,严禁报告直接抄袭!
老师会查重的,不要有侥幸心理,这个实验难度可能有点大,但是很锻炼个人能力,还是自己好好做下吧!
如果有帮到大家求个赞,谢谢!

话说csdn为什么不能直接从md文档导入图片呢,害的我得手动放了半天图片


1.实验要求

  • 使用verilog硬件描述语言设计一个单周期cpu
  • 完成基本模块的设计
  • 完成addu指令的验证
  • 完成R型指令的验证
  • 完成I型指令的验证
  • 完成MEM型指令的验证
  • 完成J型指令的验证

2.实验过程

2.1基本模块

2.1.1 PC模块

基本功能

用于更新当前指令地址,当reset信号为低电平,则初始化pc,否则接受npc

西工大计组实验单周期CPU_第1张图片

模块信号

信号名 方向 描述
clock I 时钟信号
reset I 复位信号
npc I 下一条指令地址
pc O 当前的指令地址

模块代码

module pc(pc,clock,reset,npc);

output reg [31:0] pc;//当前指令地址

input clock;

input reset;

input [31:0] npc;//下一条pc指令地址

always@(posedge clock,negedge reset )


begin
    if(!reset)//reset低电平则初始化PC,否则接受新地址
    begin
        pc<=32'h0000_3000;
    end
    else
    begin
        pc<=npc;
    end
    
end

endmodule

2.1.2 IM模块

基本功能

用于读取指令,指令存储器通过直接读取利用MARS汇编得到的文本文件获取所需执行的指令

$readmemh("code.txt",S_CYCLE_CPU.IM.ins_memory)

西工大计组实验单周期CPU_第2张图片

im模块的输入地址pc是32位,但指令存储器ins_memory只有4kB(即1KW),所以取pc的低12位作为ins_memory的地址。 另一方面,虽然MIPS指令都是固定长度的32位(一个字),但是MIPS是按字节进行编址的,所以字地址为pc>>2

模块信号

信号名 方向 描述
pc I 地址
instruction O 指令

模块代码

module im(instruction,pc);

output [31:0] instruction;

input [31:0] pc;

reg [31:0] ins_memory[1023:0]; //4k指令存储器;

assign instruction=ins_memory[pc[11:0]>>2];

endmodule

2.1.3 gpr模块

模块功能

寄存器堆实现了MIPS指令集中的32个寄存器
西工大计组实验单周期CPU_第3张图片

模块信号

信号名 方向 描述
clock I 时钟信号
rs I 读寄存器1的地址
rt I 读寄存器2的地址
num_write I 写寄存器的地址
data_write I 写寄存器的内容
reg_write I 写使能信号
a O 读寄存器1的内容
b O 读寄存器2的内容

模块代码

module gpr(a,b,clock,reg_write,num_write,rs,rt,data_write);

output reg [31:0] a;  

output reg [31:0] b;

input clock;//时钟信号

input reg_write;//写使能信号

input [4:0] rs; //读寄存器1

input [4:0] rt; //读寄存器2

input [4:0] num_write; //写寄存器

input [31:0] data_write; //写数据

reg [31:0] gp_registers[31:0];  //32个寄存器

always @(posedge clock)
begin
    if(reg_write)
        gp_registers[num_write]<=data_write;
    else
        gp_registers[num_write]<=gp_registers[num_write];
end

//令0位永远为0
always @(*)
begin
    gp_registers[0]<=32'b0;
end
always@(*)begin
a=(rs==5'b0)?32'b0:gp_registers[rs];
b=(rt==4'b0)?32'b0:gp_registers[rt];
end

endmodule

2.1.4 alu模块

模块功能

实现各种运算,如加法,减法,或和运算等,并且输出结果和0标志位

西工大计组实验单周期CPU_第4张图片

模块信号

信号名 方向 描述
a I 操作数1
b I 操作数2
aluop I 操作码
c O 计算结果
zero O 零信号,用于分支指令

模块代码

`include "ctrl_encode_def.v"
module alu(c,
           a,
           b,
           aluop);
    
    
    
    output reg [31:0] c;
    
    input [31:0] a;
    input [31:0] b;
    input [3:0] aluop;
    always@(a or b or aluop)
    begin
        case(aluop)
        `ALUOp_ADDU:c=a+b; //ADDU
        `ALUOp_SUBU:c=a-b; //SUBU
        `ALUOp_ADD: c=$signed(a)+$signed(b);
        `ALUOp_AND:  c = a & b;                    // AND/ANDI
        `ALUOp_OR:   c = a | b;                    // OR/ORI
        `ALUOp_SLT:  c = ($signed(a) < $signed(b)) ? 32'd1 : 32'd0;  // SLT/SLTI
        default: c=32'd0;
        endcase
    end
    
    
endmodule

2.1.5 dm模块

模块功能

用于读取和写入数据,当写使能有效时,按照地址写入数据,当读有效时,按照地址读取出数据。

西工大计组实验单周期CPU_第5张图片

模块信号

信号名 方向 描述
clock I 时钟信号
address I 地址
data_in I 数据输入
mem_write I 写使能信号
data_out O 数据输出

模块代码

module dm(data_out,
          clock,
          mem_write,
          address,
          data_in);
    
    output [31:0] data_out;
    
    input clock;
    
    input mem_write;
    
    input [31:0] address;
    
    input [31:0] data_in;
    
    reg [31:0] data_memory[1023:0]; //4K鏁版嵁瀛樺偍
    
    
    assign data_out = data_memory[address[11:2]];
    
    always@(posedge clock )
    begin
        if(mem_write)
            data_memory[address[11:2]] <= data_in;
    end
endmodule

2.1.6 npc模块

模块功能

计算下一条指令的地址

西工大计组实验单周期CPU_第6张图片

模块信号

信号名 方向 描述
pc I 当前指令地址
npc O 下一条指令地址
Imm26 I 输入的26位数
pc_gpr I 从寄存器获取的地址
s_npc I 选择信号
zero I 0标志位

模块代码

`include "ctrl_encode_def.v"
module npc(pc,
           npc,
           Imm26,
           pc_gpr,
           s_npc,
           zero);
    input [31:0] pc;
    input zero;
    output reg [31:0] npc;
    input [25:0] Imm26;
    input [1:0] s_npc;
    input [31:0] pc_gpr;
    reg [31:0] temp1;
    always@(*)
    begin
    temp1={{16{Imm26[15]}},Imm26[15:0]}<<2;
    end
    
    
    always@(*)
    begin
        case(s_npc)
            `PC_J:
            begin
                npc = {pc[31:28],Imm26,2'b00};
            end
            `PC_JR:
            begin
                npc = pc_gpr;
            end
            `PC_BEQ:
            begin
              npc = zero?pc+4+temp1:pc+4;
            end
            `PC_4:
            npc = pc+4;
        endcase
    end
    
endmodule

2.1.7 ctrl模块

模块功能

根据输入的指令字段,得到控制信号,以控制alu,dm,gpr等部件

模块信号

信号名 方向 描述
aluop O 控制alu的信号
s_b O 选择立即数或寄存器值作为alu的输入
s_num_write O 选择写入寄存器堆的数据
reg_write O 寄存器堆写使能信号
s_ext O 立即数扩展信号
s_data_write O 选择写入寄存器的数据
mem_write O 存储器写使能信号
s_npc O pc选择信号
op I 信号类型
funct I 功能码信号
pc I 当前pc

模块代码

`include "ctrl_encode_def.v"
module ctrl(aluop,
            op,
            funct,
            s_b,
            s_num_write,
            reg_write,
            s_ext,
            pc,
            mem_write,
            s_data_write,
            s_npc
            );
    output reg [3:0] aluop;//aluop代表六种基本运算标志信号
    output reg s_b;//选择数据存入alu的源操作敄1�72 1为b$1�70为Imm_32
    output reg [1:0] s_num_write;//选择存入写寄存器的数捄1�7 1为rt 0为rd 2丄1�731
    output reg reg_write;//写使能信号,1为写有效$1�70为写无效
    output reg s_ext;//选择扩展方式 1为符号扩屄1�7 0为零位扩屄1�7
    output reg [1:0] s_data_write;//选择写入的寄存器 1为dm$1�70为alu
    output reg mem_write;//数据存储区写使能信号$1�71为可以写入,0为不可写兄1�7
    output reg [1:0] s_npc;
    input wire [5:0] op;
    input [5:0] funct;
    input  [31:0] pc;
    
    always@(op,funct)
    begin
        if (op == 6'b000000)
            if(funct==`JR)
            begin
            s_npc=`PC_JR;
            end
            else
            begin
            s_num_write=`NUM_WRITE_RD;reg_write=1;mem_write=0;    
            aluop = funct[3:0];s_b=`ALU_GPR;s_npc=`PC_4;s_data_write=`MEM_ALU;
            end
        else
            case(op)
            `ALUOp_ADDI:
            begin 
            aluop=`ALUOp_ADD;s_b=0;s_ext=1;s_num_write=`NUM_WRITE_RT;
            reg_write=1;mem_write=0;s_data_write=`MEM_ALU;s_npc=`PC_4;
            end
            `ALUOp_ADDIU:
            begin 
            aluop=`ALUOp_ADDU;s_b=0;s_ext=1;s_num_write=`NUM_WRITE_RT;
            reg_write=1;mem_write=0;s_data_write=`MEM_ALU;s_npc=`PC_4;
            end
            `ALUOp_ANDI:
            begin 
            aluop=`ALUOp_AND;s_b=0;s_ext=0;s_num_write=`NUM_WRITE_RT;
            reg_write=1;mem_write=0;s_data_write=`MEM_ALU;s_npc=`PC_4;
            end
            `ALUOp_ORI:
            begin 
            aluop=`ALUOp_OR;s_b=0;s_ext=0;s_num_write=`NUM_WRITE_RT;
            reg_write=1;mem_write=0;s_data_write=`MEM_ALU;s_npc=`PC_4;
            end
            `ALUOp_LUI:
            begin
            aluop=`ALUOp_LU;s_b=0;s_ext=0;s_num_write=`NUM_WRITE_RT;
            reg_write=1;mem_write=0;s_data_write=`MEM_ALU;s_npc=`PC_4;
            end
            `ALUOp_SW:
            begin
            aluop=`ALUOp_ADDU;s_b=0;s_ext=1;s_num_write=`NUM_WRITE_RT;
            reg_write=0;mem_write=1;s_data_write=`MEM_ALU;s_npc=`PC_4;
            end
            `ALUOp_LW:
            begin
            aluop=`ALUOp_ADDU;s_b=0;s_ext=1;s_num_write=`NUM_WRITE_RT;
            reg_write=1;mem_write=0;s_data_write=`MEM_MEM;s_npc=`PC_4;
            end
            `J:
            begin
            s_npc=`PC_J;
            end
            `JAL:
            begin
            s_npc=`PC_J;s_data_write=`MEM_PC;

            end
            `BEQ:
            begin
            aluop=`ALUOp_SUBU;s_b=`ALU_GPR;s_npc=`PC_BEQ;

            end
            endcase
    end
    
    
    
endmodule

2.1.8 ext模块

模块功能

将16位数扩展为32位数,可以选择0扩展或者符号扩展

在这里插入图片描述

模块信号

信号名 方向 描述
s_ext I 扩展格式选择
Imm16 I 16位立即数
Imm32 I 32位立即数

模块代码

module ext(
    input s_ext,//1是为有符号扩展,0是为无符号扩展
    input [15:0] Imm16,
    output reg [31:0] Imm32
);

always@(Imm16)
begin
if(s_ext==1)
Imm32={{16{Imm16[15]}},Imm16};
else
Imm32={{16{1'b0}},Imm16};
end


endmodule //ext

2.1.9 mux_2_32模块

模块功能

选择输入alu的数据是寄存器还是立即数

西工大计组实验单周期CPU_第7张图片

模块信号

信号名 方向 描述
s_b I 选择输入alu的数据
b I 寄存器读取的数据
Imm32 I 32位立即数
alu_in O 输入alu的数据

模块代码

`include "ctrl_encode_def.v"
module mux_2_32 (
	input s_b,
	input [31:0] b,
	input [31:0] Imm32,
	output reg [31:0] alu_in
	
);

always@(*)
begin
case(s_b)
`ALU_GPR:
alu_in=b;
`ALU_IMM:
alu_in=Imm32;
endcase

end

endmodule //mux_2_32

2.1.10 mux_3_5模块

模块功能

选择alu的写寄存器

西工大计组实验单周期CPU_第8张图片

模块信号

信号名 方向 描述
s_num_write I 选择信号
rt I 输入rt字段
rd I 输入rd字段
imm I 输入立即数
num_write O 最终选择的数据

模块代码

`include "ctrl_encode_def.v"
module mux_3_5 (
    input [1:0] s_num_write,//1ʱѡÔñrt,0ʱѡÔñrd
    input [4:0] rt,
    input [4:0] rd,
    input [4:0] imm,
    output reg [4:0] num_write
    
);

always@(*)
begin
    case(s_num_write)
    `NUM_WRITE_RT:
    num_write=rt;
    `NUM_WRITE_RD:
    num_write=rd;
    `NUM_WRITE_IMM:
    num_write=imm;
    endcase

end

endmodule //mux_

2.1.11 mux_3_32模块

模块功能

根据选择信号,选择最终写入寄存器的数据

西工大计组实验单周期CPU_第9张图片

模块信号

模块名 方向 描述
alu_out I 来自alu的数据
mem_out I 来自数据存储器的数据
pc_in I pc+4
select I 选择信号
data O 最终选择的数据

模块代码

`include "ctrl_encode_def.v"
module mux_3_32 (
	input [31:0] alu_out,
	input [31:0] mem_out,
	input [31:0] pc_in,
	input [1:0] select,
	output reg [31:0] data
	
);
always@(*)
begin
case(select)
`MEM_ALU:
data=alu_out;
`MEM_MEM:
data=mem_out;
`MEM_PC:
data=pc_in;
endcase
end

endmodule //mux_3

模块代码

`include "ctrl_encode_def.v"
module mux_3_32 (
	input [31:0] alu_out,
	input [31:0] mem_out,
	input [31:0] pc_in,
	input [1:0] select,
	output reg [31:0] data
	
);
always@(*)
begin
case(select)
`MEM_ALU:
data=alu_out;
`MEM_MEM:
data=mem_out;
`MEM_PC:
data=pc_in;
endcase
end

endmodule //mux_3 

2.1.12 s_cycle_cpu 模块

模块功能

综合各模块,实现数据通路,并且根据要求实现的不同需求,合理设计信号名。

这里以最终的设计为示例

数据通路为

西工大计组实验单周期CPU_第10张图片

模块信号

信号名 方向 描述
clock I 时钟信号
reset I 复位信号

模块代码

module s_cycle_cpu(clock,
                   reset);
    
    //ÊäÈë
    
    input clock;
    
    input reset;
    
    wire [31:0] pc;
    wire [31:0] npc;
    pc PC(.clock(clock),.reset(reset),
    .pc(pc),
    .npc(npc));
    // assign npc=pc+4;
    wire [31:0] instruction;
    
    im IM(.pc(pc),
    .instruction(instruction));

    wire [31:0] a;
    wire [31:0] b;
    wire [31:0] c;
    wire reg_write;
    // assign reg_write=1;
    wire s_ext;
    wire s_b;
    wire [1:0] s_num_write;
    wire [31:0] Imm32;
    wire [4:0] num_write;
    wire [31:0] b_out;
    wire [4:0] rt;
    assign rt=instruction[20:16];
    wire [5:0] op;
    assign op=instruction[31:26];

    wire [31:0] data_write;
    wire mem_write;
    wire [1:0] s_data_write;
    wire [1:0] s_npc;
    wire [31:0] data_out;
    wire [3:0] aluop;
    wire zero;
    
    gpr GPR(.a(a),.b(b),.clock(clock),
    .reg_write(reg_write),.num_write(num_write),
    .rs(instruction[25:21]),.rt(rt),
    .data_write(data_write));


    alu ALU(.a(a),.b(b_out),.c(c),.aluop(aluop),.zero(zero));
    

    ctrl CTRL(.pc(pc),.s_ext(s_ext),.s_b(s_b),.s_num_write(s_num_write),
    .aluop(aluop),.op(op),.funct(instruction[5:0]),.reg_write(reg_write)
    ,.mem_write(mem_write),.s_data_write(s_data_write),.s_npc(s_npc));

    ext EXT(.s_ext(s_ext),.Imm16(instruction[15:0]),.Imm32(Imm32));

    mux_3_5 MUX_3_5(.s_num_write(s_num_write),.rt(rt),.rd(instruction[15:11])
    ,.imm(5'b11111),.num_write(num_write));
    mux_2_32 MUX_2_32(.s_b(s_b),.b(b),.Imm32(Imm32),.alu_in(b_out));

    mux_3_32 MUX_3_32(.alu_out(c),.mem_out(data_out),.pc_in(pc+4),
    .select(s_data_write),.data(data_write));

    npc NPC(.pc(pc),.npc(npc),.Imm26(instruction[25:0]),
    .pc_gpr(a),.s_npc(s_npc),.zero(zero));

    dm DM(.address(c),.clock(clock),.data_in(b),
    .mem_write(mem_write),.data_out(data_out));
    
    
endmodule

2.1.13ctrl_encode_def宏定义

该文件定义了一些用到的的信号值

使用这种方法,方便进行编写代码,分析代码

// 
`define JR 6'b001000
`define J 6'b000010
`define JAL 6'b000011
`define BEQ 6'b000100

// ALU control signal
`define ALUOp_ADDU  4'b0001
`define ALUOp_ADD   4'b0000
`define ALUOp_SUBU  4'b0011
`define ALUOp_AND   4'b0100
`define ALUOp_OR    4'b0101
`define ALUOp_SLT   4'b1010
`define ALUOp_LU 4'b1111 
`define ALUOp_ADDI 6'b001000
`define ALUOp_ADDIU 6'b001001
`define ALUOp_ANDI 6'b001100
`define ALUOp_ORI 6'b001101
`define ALUOp_LUI 6'b001111
`define ALUOp_SW 6'b101011
`define ALUOp_LW 6'b100011

`define PC_J 2'b00
`define PC_4 2'b01
`define PC_JR 2'b10
`define PC_BEQ 2'b11

`define NUM_WRITE_RT 2'b00 
`define NUM_WRITE_RD 2'b01
`define NUM_WRITE_IMM 2'b10

`define ALU_GPR 1
`define ALU_IMM 0 

`define MEM_ALU 2'b00
`define MEM_MEM 2'b01
`define MEM_PC 2'b10

2.2能够执行addu的单周期CPU

指令说明

addu的指令格式为

助记符 op rs rt rd shamt funct
addu 000000 rs rt rd 00000 100001

按照pc值从指令存储器中取出指令,按照指令定义,从寄存器堆中读取GPR[rs]和GPR[rt],用ALU模块实现GPR[rs]+GPR[rt],将结果存入寄存器GPR[rd] 中。指令执行的同时,通过pc+4计算下条指令的地址npc。

结果验证

使用mars汇编器写mips汇编代码

将指令文本文件装载入测试文件,得到结果如下

西工大计组实验单周期CPU_第11张图片

说明结果正确

2.3能够执行R型指令的单周期CPU

指令说明

助记符 op rs rt rd shamt funct
addu 000000 rs rt rd 0 100000
subu 000000 rs rt rd 0 100011
add 000000 rs rt rd 0 100000
and 000000 rs rt rd 0 100100
or 000000 rs rt rd 0 100101
slt 000000 rs rt rd 0 101010

指令格式和addu指令相同:功能码均为0;操作码决定ALU的运算类型;ALU的两个源操作数是寄存器GPR[rs]和GPR[rt];ALU结果写入寄存器GPR[rd]。

需要进行的改变

  • alu模块需增加减、与、或、比较(<)功能;
  • alu模块需增加选择信号aluop,决定执行哪种类型的运算;
  • 增加ctrl模块,产生控制信号(目前只有aluop信号和reg_write信号)。

结果验证

西工大计组实验单周期CPU_第12张图片

编写汇编验证程序,将程序加载入仿真文件中,在结果输出区得到以下结果

# PC=0x00003000 Aluop=0x0001
#  a=0x00000018 b=0x0000000f c=0x00000027
# PC=0x00003004 Aluop=0x0011
#  a=0x00000018 b=0x0000000f c=0x00000009
run
run
# PC=0x00003004 Aluop=0x0011
#  a=0x00000018 b=0x0000000f c=0x00000009
run
run
# PC=0x00003004 Aluop=0x0011
#  a=0x00000018 b=0x0000000f c=0x00000009
# PC=0x00003008 Aluop=0x0000
#  a=0x00000012 b=0x00000013 c=0x00000025
run
run
# PC=0x00003008 Aluop=0x0000
#  a=0x00000012 b=0x00000013 c=0x00000025
run
run
# PC=0x00003008 Aluop=0x0000
#  a=0x00000012 b=0x00000013 c=0x00000025
# PC=0x0000300c Aluop=0x0100
#  a=0x00000018 b=0x0000000f c=0x00000008
run
run
# PC=0x0000300c Aluop=0x0100
#  a=0x00000018 b=0x0000000f c=0x00000008
run
run
# PC=0x0000300c Aluop=0x0100
#  a=0x00000018 b=0x0000000f c=0x00000008
# PC=0x00003010 Aluop=0x0101
#  a=0x00000018 b=0x0000000f c=0x0000001f
run
run
# PC=0x00003010 Aluop=0x0101
#  a=0x00000018 b=0x0000000f c=0x0000001f
run
run
# PC=0x00003010 Aluop=0x0101
#  a=0x00000018 b=0x0000000f c=0x0000001f
# PC=0x00003014 Aluop=0x1010
#  a=0x00000018 b=0x0000000f c=0x00000000
run
run
# PC=0x00003014 Aluop=0x1010
#  a=0x00000018 b=0x0000000f c=0x00000000

得到的仿真波形如图

西工大计组实验单周期CPU_第13张图片

2.4添加I型指令

指令说明

助记符 op rs rt immediate
addi 001000 rs rt imm
addiu 001001 rs rt imm
andi 001100 rs rt imm
ori 001101 rs rt imm
lui 001111 00000 rt imm

需要进行的修改

  • 需要添加一个扩展器,实现立即数扩展

  • 需要增加一个alu输入源

  • 需要设置指令对应的控制信号

case(op)
            `ALUOp_ADDI:
            begin 
            aluop=`ALUOp_ADD;s_b=0;s_ext=1;s_num_write=`NUM_WRITE_RT;
            reg_write=1;mem_write=0;s_data_write=`MEM_ALU;s_npc=`PC_4;
            end
            `ALUOp_ADDIU:
            begin 
            aluop=`ALUOp_ADDU;s_b=0;s_ext=1;s_num_write=`NUM_WRITE_RT;
            reg_write=1;mem_write=0;s_data_write=`MEM_ALU;s_npc=`PC_4;
            end
            `ALUOp_ANDI:
            begin 
            aluop=`ALUOp_AND;s_b=0;s_ext=0;s_num_write=`NUM_WRITE_RT;
            reg_write=1;mem_write=0;s_data_write=`MEM_ALU;s_npc=`PC_4;
            end
            `ALUOp_ORI:
            begin 
            aluop=`ALUOp_OR;s_b=0;s_ext=0;s_num_write=`NUM_WRITE_RT;
            reg_write=1;mem_write=0;s_data_write=`MEM_ALU;s_npc=`PC_4;
            end
            `ALUOp_LUI:
            begin
            aluop=`ALUOp_LU;s_b=0;s_ext=0;s_num_write=`NUM_WRITE_RT;
            reg_write=1;mem_write=0;s_data_write=`MEM_ALU;s_npc=`PC_4;
            end

结果验证

汇编程序如下

addi $t1 $t2,1
addiu $t2 $t3,1
andi $t3 $t4,100
ori $t4,$t5,1
lui $t6,1

运行结果如下

西工大计组实验单周期CPU_第14张图片

2.5 添加mem型指令

指令说明

助记符 op rs rt imm
sw 101011 rs rt imm
lw 100011 rs rt imm

需要进行的修改

  • 增加dm模块,并且将其接入设计中
  • 增加数据选择器
  • 寄存器堆增加一个写入源

结果验证

指令为

sw $t1,0($t2)
lw $t3,0($t3)

仿真波形为

西工大计组实验单周期CPU_第15张图片

西工大计组实验单周期CPU_第16张图片

2.5 添加J型指令

指令说明

J型指令
助记符 31-26 25-21 20-16 15-11 10-6 5-0
beq 000100 rs rt offset
j 000010 instr_index
jal 000011 instr_index
jr 000000 rs 00000 00000 00000 001000

需要进行的改变为

  • 增加一个npc模块,共4个输入源
    • pc+4
    • gpr模块得到的
    • 16位立即数
    • 26位立即数
  • 控制器件增加相应的信号
  • alu增加zero标志,用于beq判断
  • 写入数据选择器增加pc+4

结果验证

这次实验老师给出了测试代码fibonacci.asm

.text

    addi     $t5,$t5,40     #  $t5 = 20
    
    li      $t2, 1                  # $t2 = 1 
    sw      $t2, 0($t0)             # store F[0] with 1
    sw      $t2, 4($t0)             # store F[1] with 1
    sw      $t2, 8($t0)             # store F[2] with 1
    ori     $t6, $zero, 3           # $t6 = 3
    subu    $t1, $t5, $t6           # the number of loop is (size-3)
    ori     $t7, $zero, 1           # the lastest loop $t7 = 1

    addi    $t0, $t0, 12             # point to F[3]
    
Loop:
    slt     $t4, $t1, $t7           # $t4 = ($t1 < 1) ? 1 : 0
    beq	    $t4, $t7, Loop_End      # repeat if not finished yet
    lw      $a0, -12($t0)           # $a0 = F[n-3]
    lw      $a1, -8($t0)            # $a0 = F[n-2]
    lw      $a2, -4($t0)            # $a1 = F[n-1]
    jal     fibonacci               # F[n] = fibonacci( F[n-3], F[n-2], F[n-1] )
    sw      $v0, 0($t0)             # store F[n]
    addi    $t0, $t0, 4             # $t0 point to next element
    addi    $t1, $t1, -1            # loop counter decreased by 1
	j       Loop
	
Loop_End:    
    lui     $t6, 0xABCD             # $t6 = 0xABCD0000
    sw      $t6, 0($t0)             # *$t0 = $t6
Loop_Forever:
    j       Loop_Forever            # loop forever

fibonacci :
    addu    $v0, $a0, $a1	# $v0 = x + y
    addu    $v0, $v0, $a2	# $v0 = x + y
    jr      $ra             # return

几个关键点在于几条跳转指令

结果验证

西工大计组实验单周期CPU_第17张图片

结果最终pc为00003054,并且一直进行循环loop forever

在存储器中,结果如下

西工大计组实验单周期CPU_第18张图片

3.实验总结

实验tips

一些自己学到的小技巧,如果生活欺骗了你,那么不妨去调试bug~

自己写tb

自己写tb文件,先在本地运行运行,当然如果直接通过了,就没必要写了

比如我写的这个

`timescale 1ns/1ns
module tb_s_cycle_cpu();


reg clock,reset;

s_cycle_cpu S_CYCLE_CPU(
    .clock(clock),
    .reset(reset)
);

integer i;
initial begin 
    $readmemh("code_J.txt",S_CYCLE_CPU.IM.ins_memory);//得到的汇编码
$monitor("PC=0x%8X",S_CYCLE_CPU.PC.pc);
clock=1;
reset=0;
for(i=0;i<=31;i=i+1)
S_CYCLE_CPU.GPR.gp_registers[i]=0;

//S_CYCLE_CPU.GPR.gp_registers[31]=32'h0000_303c;

#20
reset=1; 
end
always
begin
fork
#50 clock=~clock;
#200 $monitor("PC=0x%8X Aluop=0x%4b\n a=0x%8h b=0x%8h c=0x%8h",
S_CYCLE_CPU.PC.pc,S_CYCLE_CPU.ALU.aluop,S_CYCLE_CPU.ALU.a,S_CYCLE_CPU.ALU.b,S_CYCLE_CPU.ALU.c);
join
end
endmodule

可以把信号值给display或者monitor出来(当然也没必要,直接看信号波形图更好)

写仿真脚本

我写了一个sim.do

quit -sim; #退出以前的仿真
.main    clear; #清屏


vsim -gui -novopt work.tb_s_cycle_cpu; #仿真不带参数
add wave sim:/tb_s_cycle_cpu/S_CYCLE_CPU/*; #添加所有信号

add wave -position insertpoint -radix hex -color white \
sim:/tb_s_cycle_cpu/S_CYCLE_CPU/GPR/gp_registers
#设置添加信号的位置和颜色
add wave -position insertpoint  -radix hex -color yellow\
sim:/tb_s_cycle_cpu/S_CYCLE_CPU/DM/data_memory

virtual type {
	{2'b00 PC_J}
	{2'b01 PC_4}
	{2'b10 PC_JR}
	{2'b11 PC_BEQ}
} PC_STYLE; #设置枚举,添加信号字符名称

virtual function {(PC_STYLE)/tb_s_cycle_cpu/S_CYCLE_CPU/s_npc} s_npc_style; #生成新信号
add wave -binary -color pink /tb_s_cycle_cpu/S_CYCLE_CPU/s_npc_style

run 200us; #运行一定的时间

这样就不用每次都add wave啊,修改格式啊,还可以设置枚举值字符串标记信号,还可以把信号设置的花花绿绿的,岂不美哉

实验中遇到的问题及解决

问题1

前面的都比较顺利,就是最后一个j型指令,比较波折

提交上去,总是提示运行时间过长,后来经过询问老师同学,找到了问题所在

原来问题在这条指令

西工大计组实验单周期CPU_第19张图片

这条指令执行完毕后,pc会跳转到

在这里插入图片描述

但是我的跳转到了00000000,分析指令

在这里插入图片描述

将gpr[31]的值放入了pc,发现我的gpr[31]的值居然是0!

所以在gpr.v中修改一下就好了

西工大计组实验单周期CPU_第20张图片

问题2

这个是一个同学的问题

西工大计组实验单周期CPU_第21张图片

他这一直在跑,没法终止

这一看就是终止条件beq的问题

西工大计组实验单周期CPU_第22张图片

西工大计组实验单周期CPU_第23张图片
西工大计组实验单周期CPU_第24张图片

我给了个方法

西工大计组实验单周期CPU_第25张图片

反正我的经验就是,先找到哪条指令出问题了,再回去看看控制信号有没有加全,下一步看看运算单元得到的结果对不对,基本问题就这么多了

两次实验总共时间两天左右

参考资料

一个例子

压缩

字串

verilog脚本仿真

另一个仿真脚本

你可能感兴趣的:(组成原理,cpu,verilog,硬件)