西工大计组实验单周期CPU

西工大计组实验单周期CPU,第1张

单周期CPU

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


1.实验要求
  • 使用verilog硬件描述语言设计一个单周期cpu
  • 完成基本模块的设计
  • 完成addu指令的验证
  • 完成R型指令的验证
  • 完成I型指令的验证
  • 完成MEM型指令的验证
  • 完成J型指令的验证
2.实验过程 2.1基本模块 2.1.1 PC模块 基本功能

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

模块信号
信号名方向描述
clockI时钟信号
resetI复位信号
npcI下一条指令地址
pcO当前的指令地址
模块代码
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)

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

模块信号
信号名方向描述
pcI地址
instructionO指令
模块代码
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个寄存器

模块信号
信号名方向描述
clockI时钟信号
rsI读寄存器1的地址
rtI读寄存器2的地址
num_writeI写寄存器的地址
data_writeI写寄存器的内容
reg_writeI写使能信号
aO读寄存器1的内容
bO读寄存器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标志位

模块信号
信号名方向描述
aI *** 作数1
bI *** 作数2
aluopI *** 作码
cO计算结果
zeroO零信号,用于分支指令
模块代码
`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模块 模块功能

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

模块信号
信号名方向描述
clockI时钟信号
addressI地址
data_inI数据输入
mem_writeI写使能信号
data_outO数据输出
模块代码
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模块 模块功能

计算下一条指令的地址

模块信号
信号名方向描述
pcI当前指令地址
npcO下一条指令地址
Imm26I输入的26位数
pc_gprI从寄存器获取的地址
s_npcI选择信号
zeroI0标志位
模块代码
`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等部件

模块信号
信号名方向描述
aluopO控制alu的信号
s_bO选择立即数或寄存器值作为alu的输入
s_num_writeO选择写入寄存器堆的数据
reg_writeO寄存器堆写使能信号
s_extO立即数扩展信号
s_data_writeO选择写入寄存器的数据
mem_writeO存储器写使能信号
s_npcOpc选择信号
opI信号类型
functI功能码信号
pcI当前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_extI扩展格式选择
Imm16I16位立即数
Imm32I32位立即数
模块代码
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的数据是寄存器还是立即数

模块信号
信号名方向描述
s_bI选择输入alu的数据
bI寄存器读取的数据
Imm32I32位立即数
alu_inO输入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的写寄存器

模块信号
信号名方向描述
s_num_writeI选择信号
rtI输入rt字段
rdI输入rd字段
immI输入立即数
num_writeO最终选择的数据
模块代码
`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模块 模块功能

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

模块信号
模块名方向描述
alu_outI来自alu的数据
mem_outI来自数据存储器的数据
pc_inIpc+4
selectI选择信号
dataO最终选择的数据
模块代码
`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 模块 模块功能

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

这里以最终的设计为示例

数据通路为

模块信号
信号名方向描述
clockI时钟信号
resetI复位信号
模块代码
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的指令格式为

助记符oprsrtrdshamtfunct
addu000000rsrtrd00000100001

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

结果验证

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

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

说明结果正确

2.3能够执行R型指令的单周期CPU 指令说明
助记符oprsrtrdshamtfunct
addu000000rsrtrd0100000
subu000000rsrtrd0100011
add000000rsrtrd0100000
and000000rsrtrd0100100
or000000rsrtrd0100101
slt000000rsrtrd0101010

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

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

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

# 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

得到的仿真波形如图

2.4添加I型指令 指令说明
助记符oprsrtimmediate
addi001000rsrtimm
addiu001001rsrtimm
andi001100rsrtimm
ori001101rsrtimm
lui00111100000rtimm
需要进行的修改
  • 需要添加一个扩展器,实现立即数扩展

  • 需要增加一个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

运行结果如下

2.5 添加mem型指令 指令说明
助记符oprsrtimm
sw101011rsrtimm
lw100011rsrtimm
需要进行的修改
  • 增加dm模块,并且将其接入设计中
  • 增加数据选择器
  • 寄存器堆增加一个写入源
结果验证

指令为

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

仿真波形为

2.5 添加J型指令 指令说明
J型指令
助记符31-2625-2120-1615-1110-65-0
beq000100rsrtoffset
j000010instr_index
jal000011instr_index
jr000000rs000000000000000001000
需要进行的改变为
  • 增加一个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

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

结果验证

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

在存储器中,结果如下

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型指令,比较波折

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

原来问题在这条指令

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

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

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

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

问题2

这个是一个同学的问题

他这一直在跑,没法终止

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


我给了个方法

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

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

参考资料

一个例子

压缩

字串

verilog脚本仿真

另一个仿真脚本

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1296059.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-10
下一篇 2022-06-10

发表评论

登录后才能评论

评论列表(0条)

保存