最近看到不少学弟学妹浏览了我的两篇博客
流水线
单周期
此处加个声明,只提供参考,严禁报告直接抄袭!
老师会查重的,不要有侥幸心理,这个实验难度可能有点大,但是很锻炼个人能力,还是自己好好做下吧!
如果有帮到大家求个赞,谢谢!
话说csdn为什么不能直接从md文档导入图片呢,害的我得手动放了半天图片
addu
指令的验证R
型指令的验证I
型指令的验证MEM
型指令的验证J
型指令的验证用于更新当前指令地址,当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 )
begin
if(!reset)//reset低电平则初始化PC,否则接受新地址
begin
pc<=32'h0000_3000;
end
else
begin
pc<=npc;
end
end
endmodule
用于读取指令,指令存储器通过直接读取利用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
信号名 | 方向 | 描述 |
---|---|---|
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
实现各种运算,如加法,减法,或和运算等,并且输出结果和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)
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
用于读取和写入数据,当写使能有效时,按照地址写入数据,当读有效时,按照地址读取出数据。
信号名 | 方向 | 描述 |
---|---|---|
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
计算下一条指令的地址
信号名 | 方向 | 描述 |
---|---|---|
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
根据输入的指令字段,得到控制信号,以控制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
将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
选择输入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;
endcase
end
endmodule //mux_2_32
选择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ʱѡÔñ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_
根据选择信号,选择最终写入寄存器的数据
模块名 | 方向 | 描述 |
---|---|---|
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
综合各模块,实现数据通路,并且根据要求实现的不同需求,合理设计信号名。
这里以最终的设计为示例
数据通路为
信号名 | 方向 | 描述 |
---|---|---|
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
ctrl_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
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
汇编代码
将指令文本文件装载入测试文件,得到结果如下
说明结果正确
助记符 | 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]。
编写汇编验证程序,将程序加载入仿真文件中,在结果输出区得到以下结果
# 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
得到的仿真波形如图
助记符 | 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
运行结果如下
助记符 | op | rs | rt | imm |
---|---|---|---|---|
sw | 101011 | rs | rt | imm |
lw | 100011 | rs | rt | imm |
指令为
sw $t1,0($t2)
lw $t3,0($t3)
仿真波形为
助记符 | 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 |
这次实验老师给出了测试代码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
在存储器中,结果如下
一些自己学到的小技巧,如果生活欺骗了你,那么不妨去调试bug~
自己写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
啊,修改格式啊,还可以设置枚举值字符串标记信号,还可以把信号设置的花花绿绿的,岂不美哉
前面的都比较顺利,就是最后一个j型指令,比较波折
提交上去,总是提示运行时间过长,后来经过询问老师同学,找到了问题所在
原来问题在这条指令
这条指令执行完毕后,pc会跳转到
但是我的跳转到了00000000
,分析指令
将gpr[31]的值放入了pc,发现我的gpr[31]的值居然是0!
所以在gpr.v
中修改一下就好了
这个是一个同学的问题
他这一直在跑,没法终止
这一看就是终止条件beq
的问题
我给了个方法
反正我的经验就是,先找到哪条指令出问题了,再回去看看控制信号有没有加全,下一步看看运算单元得到的结果对不对,基本问题就这么多了
两次实验总共时间两天左右
一个例子
压缩
字串
verilog脚本仿真
另一个仿真脚本