系列文章目录
【计算机组织与体系结构】实验一:算术逻辑单元的实现
【计算机组织与体系结构】实验二:给定指令系统的处理器设计
【计算机组织与体系结构】实验三:流水线处理器
【计算机组织与体系结构】实验四:指令 CACHE 的设计与实现
文章目录
一、实验目的
- 掌握 Vivado 集成开发环境
- 掌握 Verilog 语言
- 掌握 FPGA 编程方法及硬件调试手段
- 深刻理解处理器结构和计算机系统的整体工作原理
二、实验环境
Vivado 集成开发环境和龙芯 Artix-7 实验平台
三、实验内容
根据课程第五章 MIPS 指令集的基本实现,设计并实现一个符合实验指令的非流水处理器,包括 Verilog语言的实现和 FPGA 芯片的编程实现,要求该处理器可以通过所提供的自动测试环境。
1、处理器功能
本实验的任务是设计一个简单的 RISC 处理器,该处理器是在给定的指令集(与 MIPS32 类似)下构建的,支持 11 条指令。假定存储器分为数据缓冲存储器
和指令缓冲存储器
,且都可以在一个时钟周期内完成一次同步存取操作,时钟信号和 CPU 相同。处理器的指令字长为 32 位,包含 32 个 32 位通用寄存器 R0~R31
, 1 个32 位的指令寄存器 IR
和 1 个 32 位的程序计数器 PC
, 1 个 256×32 位指令缓冲存储器
, 1 个 256×32 位的数据缓冲存储器
。
2、指令系统定义
处理器所支持的指令包括 LW, SW, ADD, SUB, AND, OR, XOR, SLT, MOVZ, BEQ, J
。
其中仅有 LW
和 SW
是字访存指令,所有的存储器访问都通过这两条指令完成; ADD、 SUB、 AND、 OR、 XOR、SLT、 MOVZ
是运算指令,他们都在处理器内部完成;BEQ
是分支跳转指令,根据寄存器的内容进行相对跳转;J
是无条件转移指令
1)指令说明
关于汇编指令的说明:
• rs 意味着 source register, rd 意味着 destination register;
• rt 有时(如运算指令)意味着第二个 source register,有时(如访存指令)意味着 target (source/destination)
register;
• 我们使用 [rs] 表示寄存器 rs 的内容;
另外,所有算数运算指令都执行有符号运算。
2)运算指令
(1)加法指令 ADD rd, rs, rt
该指令将两个源寄存器内容相加,结果送回目的寄存器的操作。
具体为: [rd] <- [rs] + [rt]
(2)减法指令 SUB rd, rs, rt
该指令将两个源寄存器内容相减,结果送回目的寄存器的操作。
具体为: [rd] <- [rs] – [rt]
(3)与运算指令 AND rd, rs, rt
该指令将两个源寄存器内容相与,结果送回目的寄存器的操作。
具体为: [rd] <- [rs] & [rt]
(4)或运算指令 OR rd, rs, rt
该指令将两个源寄存器内容相或,结果送回目的寄存器的操作。
具体为: [rd] <- [rs] | [rt]
(5)异或指令 XOR rd, rs, rt
该指令将两个源寄存器内容相异或,结果送回目的寄存器的操作。
具体为: [rd] <- [rs] ⊕ [rt]
(6)小于指令 SLT rd, rs, rt
该指令将两个源寄存器内容相比较,结果决定目的寄存器的值。
具体为: [rd] <- [rs] < [rt] ? 1 : 0
(7)条件移动指令 MOVZ rd, rs, rt
该指令根据其中一个源寄存器内容,决定另一个源寄存器的值是否写回目的寄存器。
具体为: if ([rt] == 0) then [rd] <- [rs]
Note
: 注意:当 [rt] != 0 时,不对寄存器 rd 进行任何写回操作。
3)访存指令
Warning
: 使用访存指令时,计算后的目标地址必须是访问数据单元大小的整数倍。对于 LW 和 SW 指令, ([base] + offset) % 4 == 0
(1)存数指令 SW rt, offset(base)
该指令将寄存器 rt 的内容存于主存单元中,对应的地址由 16 位偏移地址 offset 经符号拓展加上 base的内容生成。
具体操作为: Mem[[base] + offset] <- [rt]
(2)取数指令 LW rt, offset(base)
该指令将主存单元中的内容存于寄存器 rt,对应的地址由 16 位偏移地址 offset 经符号拓展加上 base
内容生成。
具体操作为: [rt] <- Mem[[base] + offset]
3)转移类指令
Warning
:
• 和 MIPS32 指令集不同,我们给出的跳转指令没有延迟槽。
• 跳转指令使用 NPC(也就是 PC+4)参与跳转地址运算。
(1)条件转移(相等跳转)指令 BEQ rs, rt, offset
该指令根据寄存器 rs 和 rt 的内容决定下一条指令的地址,若两个寄存器内容相等,则 16 位偏移 offset扩充为 32 位,左移 2 位后与 NPC 相加,作为下一条指令的地址,否则程序按原顺序执行。
具体操作为: PC <- ([rs] == [rt]) ? [sign_extend(offset) << 2 + NPC] : NPC
(2)无条件转移指令 J target
该指令改变下一条指令的地址,地址由指令中的 26 位形式地址 instr_index 左移 2 位作为低 28 位,和NPC 的高 4 位拼接而成。
具体操作为: PC <- (NPC[31:28]) ## (instr_index << 2)
四、实验要求
要求根据以上给定的指令系统设计处理器,包括指令格式设计、操作的定义、 Verilog 语言的实现及 FPGA 编程实现。处理器设计实验要求按指定阶段进行。
五、设计思想
(一)CPU接口信号定义
(二)处理器的设计方案
1、指令格式设计
处理器所支持的指令包括 LW, SW, ADD, SUB, AND, OR, XOR, SLT, MOVZ, BEQ, J。其中仅有 LW 和 SW 是字访存指令,所有的存储器访问都通过这两条指令完成; ADD、 SUB、 AND、 OR、 XOR、SLT、 MOVZ 是运算指令,他们都在处理器内部完成; BEQ 是分支跳转指令,根据寄存器的内容进行相对跳转;J 是无条件转移指令。
2、处理器结构设计框图及功能描述
该处理器可以实现11种指令,具体见上面表格
3、各功能模块结构设计框图及功能描述
(1)模块名称及功能:pc;指令寄存器,用来给出指令在指令存储器中的地址。
(2)模块名称及功能:npc;暂存PC输出的结果。
(3)模块名称及功能:符号位扩展signextension;将26位数据符号扩展为32位或者取出16位扩展为32位
(4)模块名称及功能:指令缓冲存储器imem;根据输入的地址来找到相应的指令
(5)模块名称及功能:算术逻辑单元alu;根据不同指令进行不同运算操作。
(6)模块名称及功能:数据存储器dmem;大小为256*32位,用来存储数据,在时钟上升沿,判断是否复位,若复位,则将数据缓冲存储器初始化,如果不复位,则根据写使能信号进行写操作。读操作不需要时钟信号。
(7)模块名称及功能:控制单元cu;根据输入的opcode、func、equal、radata、resetn, 来输出各种控制信号。
(8)模块名称及功能:32个32位寄存器堆regfile;可以进行两个寄存器的数据读出和一个寄存器的写回。
(9)模块名称及功能:二选一mux;对输入的两个32位数据进行选择,如果选择控制信号为1,则选择data1;如果为0,则选择data0。
(10)模块名称及功能:datatempstore;暂存数据imm、a、b、ir、aluoutput、lmd
(11)模块名称及功能:equal;比较数据是否相等,0表示不相等;1表示相等。
(12)模块名称及功能:writebackaddrmux;进行写回寄存器的选择
4、各模块输入输出接口信号定义
(1)模块名称及功能:pc;指令寄存器,用来给出指令在指令存储器中的地址。
(2)模块名称及功能:npc;暂存PC输出的结果。
(3)模块名称及功能:符号位扩展signextension;将26位数据符号扩展为32位或者取出16位扩展为32位
(4)模块名称及功能:指令缓冲存储器imem;根据输入的地址来找到相应的指令
(5)模块名称及功能:算术逻辑单元alu;根据不同指令进行不同运算操作。
(6)模块名称及功能:数据存储器dmem;大小为256*32位,用来存储数据,在时钟上升沿,判断是否复位,若复位,则将数据缓冲存储器初始化,如果不复位,则根据写使能信号进行写操作。读操作不需要时钟信号。
(7)模块名称及功能:控制单元cu;根据输入的opcode、func、equal、radata、resetn,
来输出各种控制信号。
(8)模块名称及功能:32个32位寄存器堆regfile;可以进行两个寄存器的数据读出和一个寄存器的写回。
(9)模块名称及功能:二选一mux;对输入的两个32位数据进行选择,如果选择控制信号为1,则选择data1;如果为0,则选择data0。
(10)模块名称及功能:datatempstore;暂存数据,包括imm、a、b、ir、aluoutput、lmd
(11)模块名称及功能:equal;比较数据是否相等,0表示不相等;1表示相等。
(12)模块名称及功能:writebackaddrmux;进行写回寄存器的选择
六、实验设计及测试
1、各模块的详细设计
(包括各模块功能详述,设计方法,Verilog语言实现等)
(1)模块名称及功能:pc;指令寄存器,用来给出指令在指令存储器中的地址。
module pc(
input clk,
input resetn,
input[31:0] nextpc,
output reg[31:0] currentpc
);
initial begin
currentpc <= 0;
end
always@(posedge clk)begin
if(!resetn) currentpc <= 0;
else currentpc <= nextpc;
end
endmodule
(2)模块名称及功能:npc;暂存PC输出的结果。
module npc(
input[31:0] pc,
output[31:0] pcadd4
);
assign pcadd4 = pc + 4;
endmodule
(3)模块名称及功能:符号位扩展signextension;将26位数据符号扩展为32位或者取出16位扩展为32位
module signextension(
input is_J,//控制信号,如果为0,表示为sw/lw/beq(16->32),如果为1,表示为J(26->32)
input [25:0]data,//待扩展的数据
output [31:0]extendresult//扩展结果
);
assign extendresult = (is_J)?{{6{data[25]}},data}:{{16{data[15]}},data[15:0]};//如果是J指令,则将26位扩展为32位
endmodule
(4)模块名称及功能:指令缓冲存储器imem;根据输入的地址来找到相应的指令
module imem( input[31:0] pc, output[31:0] instruction ); reg [31:0] instmemory[255:0]; initial begin $readmemh("D:/Users/Administrator/VivadoProjects/lab2/lab2.data/inst_data.txt",instmemory);//读取指令文件到inst_data end assign instruction = instmemory[pc>>2]; endmodule
(5)模块名称及功能:算术逻辑单元alu;根据不同指令进行不同运算操作。
module alu(
input[31:0] data0,
input[31:0] data1,
input[5:0] operation,
output[31:0] result
);
//存储不同指令的计算结果
wire [31:0] result1;//add lw sw
wire [31:0] result2;//sub
wire [31:0] result3;//and
wire [31:0] result4;//or
wire [31:0] result5;//xor
wire [31:0] result6;//slt
wire [31:0] result7;//movz
wire [31:0] result8;//beq
wire [31:0] result9;//j
wire [31:0] temp;
//计算不同指令的结果
assign result1 = data0 + data1;//add sw lw
assign result2 = data0 - data1;
assign result3 = data0 & data1;
assign result4 = data0 | data1;
assign result5 = data0 ^ data1;
assign result6 = (data0 < data1)?32'b1 : 32'b0;
assign result7 = (data1 == 0)? data0 : 32'b0;
assign result8 = data0 + (data1 << 2);//beq
assign temp = data1 << 2;
assign result9 ={data0[31:26], temp[25:0]};//j
//根据operation选择result
assign result = ({32{operation == 6'b100000}} & result1) |//add
({32{operation == 6'b100010}} & result2) |//sub
({32{operation == 6'b100100}} & result3) |//and
({32{operation == 6'b100101}} & result4) |//or
({32{operation == 6'b100110}} & result5) |//xor
({32{operation == 6'b101010}} & result6) |//slt
({32{operation == 6'b001010}} & result7) |//movz
({32{operation == 6'b111000}} & result1) |//lw sw
({32{operation == 6'b101000}} & result8) |//beq
({32{operation == 6'b110000}} & result9) ;//j
endmodule
(6)模块名称及功能:数据存储器dmem;大小为256*32位,用来存储数据,在时钟上升沿,判断是否复位,若复位,则将数据缓冲存储器初始化,如果不复位,则根据写使能信号进行写操作。读操作不需要时钟信号。
module dmem(
input[31:0] data,//待存入的数据
input[31:0] address,//要读数据的地址
input dmem_write_en,//0表示lw取数;1表示sw存数,即写使能有效
input clk,
input resetn,
output[31:0] outputdata
);
reg [31:0]datamemory[255:0];
reg [31:0]static_memory[255:0];
initial begin
$readmemh("D:/Users/Administrator/VivadoProjects/lab2/lab2.data/data_data.txt",datamemory);
$readmemh("D:/Users/Administrator/VivadoProjects/lab2/lab2.data/data_data.txt",static_memory);
end
reg[7:0] addr;
initial addr = 0;
always@(posedge clk)begin
if(!resetn)
datamemory[addr] <= static_memory[addr];
else if(dmem_write_en)
datamemory[address>>2]<=data;//sw
end
always@(posedge clk) begin
if(!resetn) addr <=addr +1;
end
assign outputdata = datamemory[address>>2];//lw
endmodule
(7)模块名称及功能:控制单元cu;根据输入的opcode、func、equal、radata、resetn,
来输出各种控制信号。
module cu(
input[5:0] opcode,
input[10:0] func,
input equal,
input[31:0] rtdata,
input resetn,
output pc_select,
output lw_select,
output reg_wen,
output a_mux_select,
output b_mux_select,
output dmem_write_en,
output is_J,
output[5:0] operation
);
//opcode
parameter [5:0] ALU = 6'b000000;
parameter [5:0] SW = 6'b101011;
parameter [5:0] LW = 6'b100011;
parameter [5:0] BEQ = 6'b000100;
parameter [5:0] J = 6'b000010;
//func
parameter [5:0] MOVZFUNC = 6'b001010;
//用来暂存不同指令应该输出的operation
wire [5:0] operation1;
wire [5:0] operation2;
wire [5:0] operation3;
wire [5:0] operation4;
//列举所有operation情况
assign operation1 = func[5:0];//alu指令后6位作为operation标志
assign operation2 = 6'b101000;//beq
assign operation3 = 6'b110000;//j
assign operation4 = 6'b111000;//lw sw
assign operation = ({6{opcode == ALU}}& operation1) |
({6{opcode == BEQ}}& operation2) |
({6{opcode == J}}& operation3) |
({6{opcode == LW}}& operation4) |
({6{opcode == SW}}& operation4);
//输出各种信号
assign pc_select = (opcode == J || (opcode == BEQ && equal ==1 ))? 1'b1:1'b0;
assign lw_select = (opcode == LW)? 1'b1:1'b0;
assign reg_wen = (opcode == LW || (opcode == ALU &&!(func[5:0]==MOVZFUNC && rtdata !=0))) ? 1'b1 :1'b0;
assign a_mux_select = (opcode ==J || opcode == BEQ) ? 1'b1:1'b0;//1表示需要跳转
assign b_mux_select = (opcode == J || opcode == BEQ || opcode == LW || opcode == SW) ? 1'b1:1'b0;//表示需要用到偏移
assign dmem_write_en = (opcode == SW)? 1'b1:1'b0;//只有sw往dmem写
assign is_J = (opcode == J)? 1'b1:1'b0;
endmodule
(8)模块名称及功能:32个32位寄存器堆regfile;可以进行两个寄存器的数据读出和一个寄存器的写回。
module regfile(
input clk ,
input resetn ,
// READ PORT 1
input [4 :0] raddr1,
output [31:0] rdata1,
// READ PORT 2
input [4 :0] raddr2,
output [31:0] rdata2,
// WRITE PORT
input wen , //write enable, active high
input [4 :0] waddr ,
input [31:0] wdata
);
reg [31:0] rf [31:1];//R0恒0 不可操作
// initial with $readmemh is synthesizable here
initial begin
$readmemh("D:/Users/Administrator/VivadoProjects/lab2/lab2.data/reg_data.txt", rf);
end
//WRITE
always @(posedge clk) begin
if (wen && |waddr) begin // don't write to $0
rf[waddr] <= wdata;
end
end
//READ OUT 1
assign rdata1 = (raddr1 == 5'b0) ? 32'b0 : rf[raddr1];
//READ OUT 2
assign rdata2 = (raddr2 == 5'b0) ? 32'b0 : rf[raddr2];
endmodule
(9)模块名称及功能:二选一mux;对输入的两个32位数据进行选择,如果选择控制信号为1,则选择data1;如果为0,则选择data0。
module mux(
input[31:0] data0,
input[31:0] data1,
input selectsignal,
output[31:0] result
);
assign result = (selectsignal)?data1:data0;
endmodule
(10)模块名称及功能:datatempstore;暂存数据,包括imm、a、b、ir、aluoutput、lmd
module datatempstore(
input[31:0] inputdata,
output[31:0] outputdata
);
assign outputdata = inputdata;
endmodule
(11)模块名称及功能:equal;比较数据是否相等,0表示不相等;1表示相等。
module equal(
input[31:0] data0,
input[31:0] data1,
output result
);
assign result = (data0 == data1)? 1 : 0;
endmodule
(12)模块名称及功能:writebackaddrmux;进行写回寄存器的选择
module writebackaddrmux(
input [4:0] addr0,
input [4:0] addr1,
input lw_select,
output [4:0] result
);
assign result = (lw_select)? addr1:addr0;
endmodule
2、各模块的功能测试
(每个模块作为一个部分,包括测试方案、测试过程和测试波形等)
(1)alu_tb 对模块alu进行仿真
测试方案:
module alu_tb;
//输入
reg [31:0] data0;
reg [31:0] data1;
reg [5:0] operation;
//输出
wire [31:0] result;
initial begin
data0 = 31'hfffffff0;
data1 = 32'h00000002;
#10 operation = 6'b100000;//add
#10 operation = 6'b111000;//sw lw
#10 operation = 6'b100010;//sub
#10 operation = 6'b100100;//and
#10 operation = 6'b100101;//or
#10 operation = 6'b100110;//xor
#10 operation = 6'b101010;//slt
#10 operation = 6'b001010;//movz
#10 operation = 6'b101000;//beq
#10 operation = 6'b110000;//jmp
end
alu alu_0(
.operation(operation),
.data0(data0),
.data1(data1),
.result(result)
);
endmodule
仿真波形:
(2) cu_tb对模块cu进行仿真
测试方案:
module cu_tb;
//输入
reg [5:0] opcode;
reg [10:0] func;
reg equal;
reg [31:0] rtdata;
//输出
wire pc_select;
wire lw_select;
wire reg_wen;
wire a_mux_select;
wire b_mux_select;
wire dmem_write_en;
wire is_J;
wire [5:0] operation;
initial begin
//初始化
opcode = 6'b000000;
func = 11'b00000100000;
equal = 1'b0;
rtdata = 32'b0;
#10 //add
opcode = 6'b000000;
func = 11'b00000100000;
equal = 1'b0;
#10 //sub
opcode = 6'b000000;
func = 11'b00000100010;
equal = 1'b0;
#10 //and
opcode = 6'b000000;
func = 11'b00000100100;
equal = 1'b0;
#10 //or
opcode = 6'b000000;
func = 11'b00000100101;
equal = 1'b0;
#10 //xor
opcode = 6'b000000;
func = 11'b00000100110;
equal = 1'b0;
#10 //slt
opcode = 6'b000000;
func = 11'b00000101010;
equal = 1'b0;
#10 //movz rtdata==0
opcode = 6'b000000;
func = 11'b00000001010;
equal = 1'b0;
rtdata = 32'b0;
#10 //movz rtdata!=0
opcode = 6'b000000;
func = 11'b00000001010;
equal = 1'b0;
rtdata = 32'b1;
#10 //sw
opcode = 6'b101011;
#10 //lw
opcode = 6'b100011;
#10 //beq equal == 1
opcode = 6'b000100;
equal = 1'b1;
#10 //beq equal == 0
opcode = 6'b000100;
equal = 1'b0;
#10 //j
opcode = 6'b000010;
end
cu cu_0(
.opcode(opcode),
.func(func),
.equal(equal),
.rtdata(rtdata),
.resetn(resetn),
.pc_select(pc_select),
.lw_select(lw_select),
.reg_wen(reg_wen),
.a_mux_select(a_mux_select),
.b_mux_select(b_mux_select),
.dmem_write_en(dmem_write_en),
.is_J(is_J),
.operation(operation)
);
endmodule
仿真波形:
3、系统的详细设计
(包括系统功能详述,设计方法,Verilog语言实现等)
module cpu(
input clk , // clock, 100MHz
(*mark_debug = "true"*)input resetn , // active high
// debug signals
(*mark_debug = "true"*)output [31:0] debug_wb_pc , // 当前正在执行指令的 PC
(*mark_debug = "true"*)output debug_wb_rf_wen , // 当前通用寄存器组的写使能信号
(*mark_debug = "true"*)output [4 :0] debug_wb_rf_addr, // 当前通用寄存器组写回的寄存器编号
(*mark_debug = "true"*)output [31:0] debug_wb_rf_wdata // 当前指令需要写回的数据
);
//存储指令的各个部分
wire [5:0] opcode;//cu
wire [4:0] raddr1;
wire [4:0] raddr2;
wire [4:0] raddr3;
wire [10:0] func;//cu
wire [25:0] instr_index;
wire [31:0] current_instruction;//当前指令,从ir取出
assign opcode = current_instruction[31:26];
assign raddr1 = current_instruction[25:21];
assign raddr2 = current_instruction[20:16];
assign raddr3 = current_instruction[15:11];
assign func = current_instruction[10:0];
assign instr_index = current_instruction[25:0];
//定义各种线
//cu的输出
wire pc_select;
wire lw_select;
wire reg_wen;
wire a_mux_select;
wire b_mux_select;
wire dmem_write_en;
wire [5:0] operation;
wire is_J;//控制signextension
wire equal;
wire [31:0] nextpc;//pc输入,pc_mux输出
wire [31:0] currentpc;//pc输出,imem\npc输入
wire [31:0] pcadd4;//pc+4
wire [31:0] output_instruction;//imem读出的指令
wire [31:0] immin;//imm输入
wire [31:0] immout;//imm输出
wire [31:0] aout;//A输出
wire [31:0] bout;//B输出
wire [31:0] rdata1;//register读出的第一个数,A输入
wire [31:0] rdata2;//register读出的第二个数,B输入
wire [4:0] waddr;//register要写回的寄存器编号,writebackaddr_mux输出
wire [31:0] wdata;//register要写回的寄存器内容,data_mux输出
wire [31:0] alua;//alu第一个操作数,a_mux输出
wire [31:0] alub;//alu第二个操作数,b_mux输出
wire [31:0] result;//alu的计算结果
wire [31:0] aluresultdata;//aluoutput输出
wire [31:0] lmdin;//dmem读出的数据,lmd输入
wire [31:0] lmdout;//lmd输出,data_mux输入
//cpu输出
assign debug_wb_pc = currentpc;
assign debug_wb_rf_wen = reg_wen;
assign debug_wb_rf_addr = waddr;
assign debug_wb_rf_wdata = wdata;
//各个模块的连线
pc U_pc(
.clk(clk),
.resetn(resetn),
.nextpc(nextpc),
.currentpc(currentpc)
);
npc U_npc(
.pc (currentpc),
.pcadd4 (pcadd4)
);
imem U_imem(
.pc(currentpc),
.instruction(output_instruction)
);
datatempstore U_ir(
.inputdata (output_instruction),
.outputdata (current_instruction)
);
regfile U_regfile(
.clk(clk),
.resetn(resetn),
.raddr1(raddr1),
.rdata1(rdata1),
.raddr2(raddr2),
.rdata2(rdata2),
.wen(reg_wen),
.waddr(waddr) ,
.wdata(wdata)
);
datatempstore U_a(
.inputdata (rdata1),
.outputdata (aout)
);
datatempstore U_b(
.inputdata (rdata2),
.outputdata (bout)
);
signextension U_signextension(
.is_J(is_J),
.data(instr_index),
.extendresult(immin)
);
datatempstore U_imm(
.inputdata (immin),
.outputdata (immout)
);
equal U_equal(
.data0(aout),
.data1(bout),
.result(equal)
);
mux U_a_mux(
.data0(aout),
.data1(pcadd4),
.selectsignal(a_mux_select),
.result(alua)
);
mux U_b_mux(
.data0(bout),
.data1(immout),
.selectsignal(b_mux_select),
.result(alub)
);
alu U_alu(
.data0(alua),
.data1(alub),
.operation(operation),
.result(result)
);
datatempstore U_aluoutput(
.inputdata (result),
.outputdata (aluresultdata)
);
dmem U_dmem(
.data(bout),
.address(aluresultdata),
.dmem_write_en(dmem_write_en),
.clk(clk),
.resetn(resetn),
.outputdata(lmdin)
);
datatempstore U_lmd(
.inputdata (lmdin),
.outputdata (lmdout)
);
mux U_pc_mux(
.data0(pcadd4),
.data1(aluresultdata),
.selectsignal(pc_select),
.result(nextpc)
);
mux U_data_mux(
.data0(aluresultdata),
.data1(lmdout),
.selectsignal(lw_select),
.result(wdata)
);
writebackaddrmux U_writebackaddrmux(
.addr0(raddr3),
.addr1(raddr2),
.lw_select(lw_select),
.result(waddr)
);
cu U_cu(
.opcode(opcode),
.resetn(resetn),
.func(func),
.equal(equal),
.rtdata(rdata2),
.pc_select(pc_select),
.lw_select(lw_select),
.reg_wen(reg_wen),
.a_mux_select(a_mux_select),
.b_mux_select(b_mux_select),
.dmem_write_en(dmem_write_en),
.operation(operation),
.is_J(is_J)
);
endmodule
4、系统的功能测试
(包括系统整体功能的测试方案、测试过程和测试波形等)
测试方案:本实验已经提供了测试代码,观察代码发现,首先resetn信号置为0,表示重置,即需要初始化,等到2000ns时,重置信号resetn变成1,这时我们的指令才开始执行,即调用cpu_top模块来进行测试。
`timescale 1ns / 1ps
module cpu_tb(
);
//-----Clock and reset signal simulation-----
//signals
reg clk;
reg resetn;
//simulation
initial begin
clk = 1'b0;
resetn = 1'b0;
#2000;
resetn = 1'b1;
end
always #5 clk = ~clk;
cpu_top U_cpu_top(
.clk (clk ),
.reset (resetn )
);
//-----monitor test-----
initial
begin
$timeformat(-9,0," ns",10);
while(!resetn) #5;
$display("==============================================================");
$display("Test begin!");
#10000;
end
endmodule
测试波形如下:
七、测试结果及实验分析
1、处理器仿真测试波形(整体)
2、FPGA编程下载
(1) 编写处理器功能测试程序,包括助记符和二进制代码。
对于实验二,提供了一个自动测试环境,包含了所设计的11中指令。cpu_tb文件具体设计如下,调用了cpu_top函数。观察发现,时钟信号clk是每5ns进行取反。前2000ns里面resetn信号一直为0,直到第2000ns resetn信号才变为1,指令才开始向下执行。
`timescale 1ns / 1ps
module cpu_tb(
);
//-----Clock and reset signal simulation-----
//signals
reg clk;
reg resetn;
//simulation
initial begin
clk = 1'b0;
resetn = 1'b0;
#2000;
resetn = 1'b1;
end
always #5 clk = ~clk;
cpu_top U_cpu_top(
.clk (clk ),
.reset (resetn )
);
//-----monitor test-----
initial
begin
$timeformat(-9,0," ns",10);
while(!resetn) #5;
$display("==============================================================");
$display("Test begin!");
#10000;
end
endmodule
(2) 上板波形记录与解释
上板情况如下,开发板上一排单色灯同时亮起:
对4个信号的抓取情况如下:首先启动逻辑分析仪,然后按下复位键,可以观察到捕捉到了后半阶段的波形,data的输出与之前的仿真结果相同,pc表示指令地址,也是每次加4,写使能信号和addr的输入也都符合之前的分析。
八、实验总结
这次实验通过编写verilog实现了一个非流水线单周期处理器,本次实验细节非常多,需要考虑的事情也很多,也使我体会到了模块化编程的简便之处。通过自己动手设计一个处理器,是我更加深刻地学习到了其中的细节,体会到了乐趣。总之,这次实验使我收获很多。
没有回复内容