西工大计组实验单周期CPU

2023-10-10 22:10

本文主要是介绍西工大计组实验单周期CPU,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

单周期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 )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

模块信号
信号名方向描述
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; //读寄存器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标志位

在这里插入图片描述

模块信号
信号名方向描述
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)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模块

模块功能

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

在这里插入图片描述

模块信号
信号名方向描述
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 )beginif(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@(*)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等部件

模块信号
信号名方向描述
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_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_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};
endendmodule //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;
endcaseendendmodule //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ʱѡÔñ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_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
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 模块

模块功能

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

这里以最终的设计为示例

数据通路为

在这里插入图片描述

模块信号
信号名方向描述
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: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型指令

指令说明
助记符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

.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的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/183434

相关文章

使用Python检查CPU型号并弹出警告信息

《使用Python检查CPU型号并弹出警告信息》本教程将指导你如何编写一个Python程序,该程序能够在启动时检查计算机的CPU型号,如果检测到CPU型号包含“I3”,则会弹出一个警告窗口,感兴趣的小... 目录教程目标方法一所需库步骤一:安装所需库步骤二:编写python程序步骤三:运行程序注意事项方法二

计组基础知识

操作系统的特征 并发共享虚拟异步 操作系统的功能 1、资源分配,资源回收硬件资源 CPU、内存、硬盘、I/O设备。2、为应⽤程序提供服务操作系统将硬件资源的操作封装起来,提供相对统⼀的接⼝(系统调⽤)供开发者调⽤。3、管理应⽤程序即控制进程的⽣命周期:进程开始时的环境配置和资源分配、进程结束后的资源回收、进程调度等。4、操作系统内核的功能(1)进程调度能⼒: 管理进程、线

STM32(十一):ADC数模转换器实验

AD单通道: 1.RCC开启GPIO和ADC时钟。配置ADCCLK分频器。 2.配置GPIO,把GPIO配置成模拟输入的模式。 3.配置多路开关,把左面通道接入到右面规则组列表里。 4.配置ADC转换器, 包括AD转换器和AD数据寄存器。单次转换,连续转换;扫描、非扫描;有几个通道,触发源是什么,数据对齐是左对齐还是右对齐。 5.ADC_CMD 开启ADC。 void RCC_AD

HNU-2023电路与电子学-实验3

写在前面: 一、实验目的 1.了解简易模型机的内部结构和工作原理。 2.分析模型机的功能,设计 8 重 3-1 多路复用器。 3.分析模型机的功能,设计 8 重 2-1 多路复用器。 4.分析模型机的工作原理,设计模型机控制信号产生逻辑。 二、实验内容 1.用 VERILOG 语言设计模型机的 8 重 3-1 多路复用器; 2.用 VERILOG 语言设计模型机的 8 重 2-1 多

Java程序到CPU上执行 的步骤

相信很多的小伙伴在最初学习编程的时候会容易产生一个疑惑❓,那就是编写的Java代码究竟是怎么一步一步到CPU上去执行的呢?CPU又是如何执行的呢?今天跟随小编的脚步去化解开这个疑惑❓。 在学习这个过程之前,我们需要先讲解一些与本内容相关的知识点 指令 指令是指导CPU运行的命令,主要由操作码+被操作数组成。 其中操作码用来表示要做什么动作,被操作数是本条指令要操作的数据,可能是内存地址,也

win10不用anaconda安装tensorflow-cpu并导入pycharm

记录一下防止忘了 一、前提:已经安装了python3.6.4,想用tensorflow的包 二、在pycharm中File-Settings-Project Interpreter点“+”号导入很慢,所以直接在cmd中使用 pip install -i https://mirrors.aliyun.com/pypi/simple tensorflow-cpu下载好,默认下载的tensorflow

定位cpu占用过高的线程和对应的方法

如何定位cpu占用过高的线程和对应的方法? 主要是通过线程id找到对应的方法。 1 查询某个用户cpu占用最高的进程号 top -u 用户名 2 查询这个进程中占用cpu最高的线程号 top –p 进程号-H    3 查询到进程id后把进程相关的代码打印到jstack文件 jstack -l pid > jstack.txt 4 在jstack文件中通过16进制的线程id搜索到

61.以太网数据回环实验(4)以太网数据收发器发送模块

(1)状态转移图: (2)IP数据包格式: (3)UDP数据包格式: (4)以太网发送模块代码: module udp_tx(input wire gmii_txc ,input wire reset_n ,input wire tx_start_en , //以太网开始发送信

LTspice模拟CCM和DCM模式的BUCK电路实验及参数计算

关于BUCK电路的原理可以参考硬件工程师炼成之路写的《 手撕Buck!Buck公式推导过程》.实验内容是将12V~5V的Buck电路仿真,要求纹波电压小于15mv. CCM和DCM的区别: CCM:在一个开关周期内,电感电流从不会到0. DCM:在开关周期内,电感电流总会到0. CCM模式Buck电路仿真: 在用LTspice模拟CCM电路时,MOS管驱动信号频率为100Khz,负载为10R(可自

CPU亲和性设置 代码示例 sched_setaffinity sched_getaffinity

视频教程在这: cpu亲和性设置,NCCL,sched_setaffinity sched_getaffinity,CPU_ZERO、SET、ISSET、linux_哔哩哔哩_bilibili 一、CPU亲和性简介 CPU亲和性(CPU Affinity)设置是操作系统中一个重要的性能优化手段,它允许程序或进程被绑定到特定的CPU核心上运行。这样做的好处包括减少缓存未命中、降低线程迁移(co