本文主要是介绍西工大计组实验单周期CPU,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
单周期CPU
最近看到不少学弟学妹浏览了我的两篇博客
流水线
单周期
此处加个声明,只提供参考,严禁报告直接抄袭!
老师会查重的,不要有侥幸心理,这个实验难度可能有点大,但是很锻炼个人能力,还是自己好好做下吧!
如果有帮到大家求个赞,谢谢!
话说csdn为什么不能直接从md文档导入图片呢,害的我得手动放了半天图片
1.实验要求
- 使用verilog硬件描述语言设计一个单周期cpu
- 完成基本模块的设计
- 完成
addu
指令的验证 - 完成
R
型指令的验证 - 完成
I
型指令的验证 - 完成
MEM
型指令的验证 - 完成
J
型指令的验证
2.实验过程
2.1基本模块
2.1.1 PC模块
基本功能
用于更新当前指令地址,当reset
信号为低电平,则初始化pc
,否则接受npc
模块信号
信号名 | 方向 | 描述 |
---|---|---|
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 )beginif(!reset)//reset低电平则初始化PC,否则接受新地址beginpc<=32'h0000_3000;endelsebeginpc<=npc;endendendmodule
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
模块信号
信号名 | 方向 | 描述 |
---|---|---|
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个寄存器
模块信号
信号名 | 方向 | 描述 |
---|---|---|
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; //读寄存器1input [4:0] rt; //读寄存器2input [4:0] num_write; //写寄存器input [31:0] data_write; //写数据reg [31:0] gp_registers[31:0]; //32个寄存器always @(posedge clock)
beginif(reg_write)gp_registers[num_write]<=data_write;elsegp_registers[num_write]<=gp_registers[num_write];
end//令0位永远为0
always @(*)
begingp_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];
endendmodule
2.1.4 alu模块
模块功能
实现各种运算,如加法,减法,或和运算等,并且输出结果和0标志位
模块信号
信号名 | 方向 | 描述 |
---|---|---|
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)begincase(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/SLTIdefault: c=32'd0;endcaseendendmodule
2.1.5 dm模块
模块功能
用于读取和写入数据,当写使能有效时,按照地址写入数据,当读有效时,按照地址读取出数据。
模块信号
信号名 | 方向 | 描述 |
---|---|---|
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 )beginif(mem_write)data_memory[address[11:2]] <= data_in;end
endmodule
2.1.6 npc模块
模块功能
计算下一条指令的地址
模块信号
信号名 | 方向 | 描述 |
---|---|---|
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@(*)begintemp1={{16{Imm26[15]}},Imm26[15:0]}<<2;endalways@(*)begincase(s_npc)`PC_J:beginnpc = {pc[31:28],Imm26,2'b00};end`PC_JR:beginnpc = pc_gpr;end`PC_BEQ:beginnpc = zero?pc+4+temp1:pc+4;end`PC_4:npc = pc+4;endcaseendendmodule
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_32output reg [1:0] s_num_write;//选择存入写寄存器的数捄1�7 1为rt 0为rd 2丄1�731output reg reg_write;//写使能信号,1为写有效$1�70为写无效output reg s_ext;//选择扩展方式 1为符号扩屄1�7 0为零位扩屄1�7output reg [1:0] s_data_write;//选择写入的寄存器 1为dm$1�70为aluoutput reg mem_write;//数据存储区写使能信号$1�71为可以写入,0为不可写兄1�7output reg [1:0] s_npc;input wire [5:0] op;input [5:0] funct;input [31:0] pc;always@(op,funct)beginif (op == 6'b000000)if(funct==`JR)begins_npc=`PC_JR;endelsebegins_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;endelsecase(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:beginaluop=`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:beginaluop=`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:beginaluop=`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:begins_npc=`PC_J;end`JAL:begins_npc=`PC_J;s_data_write=`MEM_PC;end`BEQ:beginaluop=`ALUOp_SUBU;s_b=`ALU_GPR;s_npc=`PC_BEQ;endendcaseendendmodule
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};
endendmodule //ext
2.1.9 mux_2_32模块
模块功能
选择输入alu的数据是寄存器还是立即数
模块信号
信号名 | 方向 | 描述 |
---|---|---|
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;
endcaseendendmodule //mux_2_32
2.1.10 mux_3_5模块
模块功能
选择alu的写寄存器
模块信号
信号名 | 方向 | 描述 |
---|---|---|
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ʱѡÔñrdinput [4:0] rt,input [4:0] rd,input [4:0] imm,output reg [4:0] num_write);always@(*)
begincase(s_num_write)`NUM_WRITE_RT:num_write=rt;`NUM_WRITE_RD:num_write=rd;`NUM_WRITE_IMM:num_write=imm;endcaseendendmodule //mux_
2.1.11 mux_3_32模块
模块功能
根据选择信号,选择最终写入寄存器的数据
模块信号
模块名 | 方向 | 描述 |
---|---|---|
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
endendmodule //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
endendmodule //mux_3
2.1.12 s_cycle_cpu 模块
模块功能
综合各模块,实现数据通路,并且根据要求实现的不同需求,合理设计信号名。
这里以最终的设计为示例
数据通路为
模块信号
信号名 | 方向 | 描述 |
---|---|---|
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
汇编代码
将指令文本文件装载入测试文件,得到结果如下
说明结果正确
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信号)。
结果验证
编写汇编验证程序,将程序加载入仿真文件中,在结果输出区得到以下结果
# 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型指令
指令说明
助记符 | 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:beginaluop=`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型指令
指令说明
助记符 | op | rs | rt | imm |
---|---|---|---|---|
sw | 101011 | rs | rt | imm |
lw | 100011 | rs | rt | imm |
需要进行的修改
- 增加dm模块,并且将其接入设计中
- 增加数据选择器
- 寄存器堆增加一个写入源
结果验证
指令为
sw $t1,0($t2)
lw $t3,0($t3)
仿真波形为
2.5 添加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
.textaddi $t5,$t5,40 # $t5 = 20li $t2, 1 # $t2 = 1 sw $t2, 0($t0) # store F[0] with 1sw $t2, 4($t0) # store F[1] with 1sw $t2, 8($t0) # store F[2] with 1ori $t6, $zero, 3 # $t6 = 3subu $t1, $t5, $t6 # the number of loop is (size-3)ori $t7, $zero, 1 # the lastest loop $t7 = 1addi $t0, $t0, 12 # point to F[3]Loop:slt $t4, $t1, $t7 # $t4 = ($t1 < 1) ? 1 : 0beq $t4, $t7, Loop_End # repeat if not finished yetlw $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 elementaddi $t1, $t1, -1 # loop counter decreased by 1j LoopLoop_End: lui $t6, 0xABCD # $t6 = 0xABCD0000sw $t6, 0($t0) # *$t0 = $t6
Loop_Forever:j Loop_Forever # loop foreverfibonacci :addu $v0, $a0, $a1 # $v0 = x + yaddu $v0, $v0, $a2 # $v0 = x + yjr $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_memoryvirtual 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_stylerun 200us; #运行一定的时间
这样就不用每次都add wave
啊,修改格式啊,还可以设置枚举值字符串标记信号,还可以把信号设置的花花绿绿的,岂不美哉
实验中遇到的问题及解决
问题1
前面的都比较顺利,就是最后一个j型指令,比较波折
提交上去,总是提示运行时间过长,后来经过询问老师同学,找到了问题所在
原来问题在这条指令
这条指令执行完毕后,pc会跳转到
但是我的跳转到了00000000
,分析指令
将gpr[31]的值放入了pc,发现我的gpr[31]的值居然是0!
所以在gpr.v
中修改一下就好了
问题2
这个是一个同学的问题
他这一直在跑,没法终止
这一看就是终止条件beq
的问题
我给了个方法
反正我的经验就是,先找到哪条指令出问题了,再回去看看控制信号有没有加全,下一步看看运算单元得到的结果对不对,基本问题就这么多了
两次实验总共时间两天左右
参考资料
一个例子
压缩
字串
verilog脚本仿真
另一个仿真脚本
这篇关于西工大计组实验单周期CPU的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!