目录
一、实验目的
1.掌握Xilinx ISE集成开发环境和ModelSim仿真工具的使用方法
2.掌握VERILOG语言
3.掌握FPGA编程方法及硬件调试手段
4.深刻理解处理器结构和计算机系统的整体工作原理
二、实验环境(实验设备、开发环境)
- Windows10
- ISE14.7
- NEXYS3开发板
三、设计思想
1.指令格式设计
2.处理器结构设计框图及功能描述
上图是一个简单的基本上能够在单周期CPU上完成所要求设计的指令功能的数据通路和必要的控制线路图。其中指令和数据各存储在不同存储器中,即有指令存储器和数据存储器。访问存储器时,先给出内存地址,然后指令存储器以组合逻辑的方式来实现,而数据存储器以时序逻辑的方式来实现,在写时需要将对应的使能信号置为有效状态。对于寄存器组,先给出寄存器地址,读操作时不需要时钟信号,输出端就直接输出相应数据;而在写操作时,在 写使能信号为1时,在时钟边沿触发将数据写入寄存器。CU是整个CPU的控制器,随着节拍的改变而调整各个使能信号、MUX的选择输入端。
3.各功能模块结构的功能描述
PC,用于存放下一条要执行的指令的地址。
IMEM,指令存储器,用于存放所有要执行的指令。
IR,用于存放正要执行的指令。
NPC,用于暂存pc+4的值。
ADD,用于执行加法运算。
PC_select,用于在pc+4和跳转指令中地址进行选择。
Registers,寄存器组文件,这里实现了32个32位的通用寄存器。
Data_holder,用来暂存数据,A、B、IMM都是这个模块的实例化。
Extender,进行位扩展,如果是无条件跳转J指令,则将后26位符号扩展为32位,否则将后16位符号扩展为32位。
MUX,数据选择器。
ALU,算数运算单元,执行和指令相关的一些计算。
ALU_output,暂存ALU的结果。
DMEM,数据存储器。
LMD,用于暂存DMEM的数据。
CU,CPU的控制器,随着时钟节拍的改变而相应的变化控制信号。
4.各模块输入输出接口信号定义
模块名称及功能:PC,用于存放下一条要执行的指令的地址。
模块名称及功能:IMEM,指令存储器,用于存放所有要执行的指令。
模块名称及功能:IR,用于存放正要执行的指令。
模块名称及功能:NPC,用于暂存pc+4的值。
模块名称及功能:ADD,用于执行加法运算。
模块名称及功能:PC_select,用于在pc+4和跳转指令中地址进行选择。
模块名称及功能:Registers,寄存器组文件,这里实现了32个32位的通用寄存器。
模块名称及功能:Data_holder,用来暂存数据,A、B、IMM都是这个模块的实例化。
模块名称及功能:Extender,进行位扩展,如果是无条件跳转J指令,则将后26位符号扩展为32位,否则将后16位符号扩展为32位。
模块名称及功能:MUX,数据选择器。
模块名称及功能:ALU,算数运算单元,执行和指令相关的一些计算。
模块名称及功能:ALU_output,暂存ALU的结果。
模块名称及功能:DMEM,数据存储器。
模块名称及功能:LMD,用于暂存DMEM的数据。
模块名称及功能:CU,CPU的控制器,随着时钟节拍的改变而相应的变化控制信号。
四、实验设计及测试
用Verilog语言实现处理器设计与实现
① 各模块的详细设计
(1)PC:
module PC(
input [31:0] new_address, // 下一个地址
input clk,
input reset,
input pc_enable, // 是否可以更改PC
output reg [31:0] output_address // 当前PC内容
);
always@(posedge clk) begin
if(reset == 1'b1)
output_address <= 0 - 4;
else
output_address <= (pc_enable == 1)? new_address : output_address;
end
endmodule
(2)ADD:
module ADD(
input [31:0] data1,
input [31:0] data2,
output [31:0] result
);
assign result = data1 + data2;
endmodule
(3)PC_select:
module PC_select(
input [31:0] JMP_address, // 执行跳转类指令时PC值
input [31:0] PC_address, // 向下执行时的PC值
input pc_select, // 是否跳转
output [31:0] next_address
);
assign next_address = (pc_select == 1)? JMP_address : PC_address;
endmodule
(4)NPC:
module NPC(
input [31:0] input_address, // 下一个PC值
input clk,
input reset,
input npc_enable, // 是否可以更改NPC
output reg [31:0] output_address // NPC当前的值
);
always@(negedge clk) begin
if(reset == 1'b1) begin
output_address <= 0;
end
else begin
output_address <= (npc_enable==1)? input_address : output_address;
end
end
endmodule
(5)IMEM:
`include "./cpu.vh"
module IMEM(
input [31:0] address, // 指令的地址
output [31:0] output_instruction // 指令
);
reg [31:0] data [255:0]; // 32*256大小的指令存储缓冲器
initial begin
$readmemh(`INST_FILE_PATH, data); // 读取文件内容并进行初始化
end
assign output_instruction = data[address / 4];
endmodule
(6)IR:
module IR(
input [31:0] input_instruction, // IR要修改为的值,即取出的指令
input ir_enable, // IR是否可以被修改
input clk,
input reset,
output reg [31:0] output_instruction // 指令
);
always@(negedge clk) begin
if (reset == 1) begin
output_instruction <= 32'h00000000;
end
else begin
output_instruction <= (ir_enable == 1)? input_instruction : output_instruction;
end
end
endmodule
(7)Registers:
`include "./cpu.vh"
module Registers(
input [4:0] RS_1, // 指令中的寄存器部分
input [4:0] RS_2, // 指令中的寄存器部分
input [4:0] writeback_address, // 要写的寄存器的地址
input [31:0] writeback_data, // 要写入的内容
input clk,
input write_enable, // 是否可以写
input reset,
output [31:0] output_data_1, // 读到的值
output [31:0] output_data_2 // 读到的值
);
reg [31:0] memory [31:0]; // 32个32位寄存器
assign output_data_1 = memory[RS_1];
assign output_data_2 = memory[RS_2];
// 使用本地文件初始化寄存器组
initial begin
$readmemb(`GPRS_FILE_PATH , memory);
end
always@(posedge clk) begin
if(reset == 1) begin
//output_data_1 = 32'h00000000;
//output_data_2 = 32'h00000000;
end
else begin
if(write_enable == 1) begin
memory[writeback_address] <= writeback_data;
end
end
end
endmodule
(8)Data_holder:
module Data_holder(
input [31:0] in,
input clk,
output reg [31:0] out
);
always@(negedge clk) begin
out <= in;
end
endmodule
(9)Extender:
`define JMP 6'b000010
module Extender(
input [ 5:0] opcode, // 指令的操作码
input [25:0] input_26bimm, // 待扩展的数据
output [31:0] output_32bimm // 扩展到32位后的数据
);
assign output_32bimm[31:0] = (opcode == `JMP)? {
{
6{
input_26bimm[25]}}, input_26bimm[25:0]} // 无条件跳转将后25位符号扩展为32位
: {
{
16{
input_26bimm[15]}}, input_26bimm[15:0]}; // 将后16位符号扩展为32位
endmodule
(10)Writeback_select:
module Writeback_Select(
input select,
input [4:0] rt_address,
input [4:0] rd_address,
output [4:0] writeback_address
);
assign writeback_address = (select == 1)? rt_address : rd_address;
endmodule
(11)Equal:
module Equal(
input [31:0] data_A,
input [31:0] data_B,
input clk,
input reset,
output reg equal // data_A和data_B是否相等
);
always@(posedge clk) begin
if(reset == 1) begin
equal = 1'b0;
end
else begin
equal = (data_A == data_B)? 1'b1 : 1'b0;
end
end
endmodule
(12)MUX32:
module MUX32(
input [31:0] input_data0,
input [31:0] input_data1,
input select, // 选择信号,为1选择input_data1,反之input_data0
output [31:0] output_data
);
assign output_data = (select == 1)? input_data1 : input_data0;
endmodule
(13)ALU:
module ALU(
input [5:0] alu_op, // 执行哪种运算
input [31:0] input_data1,
input [31:0] input_data2,
input clk,
input reset,
output reg [31:0] output_result
);
reg [31:0] temp;
always@(posedge clk) begin
if(reset == 1) begin
output_result = 32'b0;
temp = 32'b0;
end
else begin
case(alu_op)
6'b101000 : output_result = (input_data2 << 2) + input_data1; // BEQ
6'b100000 : output_result = input_data1 + input_data2;
6'b100010 : output_result = input_data1 - input_data2;
6'b100100 : output_result = input_data1 & input_data2;
6'b100101 : output_result = input_data1 | input_data2;
6'b100110 : output_result = input_data1 ^ input_data2;
6'b101010 : output_result = (input_data1 < input_data2)? 32'h000000001 : 32'h00000000;
6'b001010 : output_result = (input_data2 == 0)? input_data1 : 32'h00000000; // MOVZ:if([rt] == 0)then[rd] ← [rs]
6'b110000 : begin
//JMP
temp = input_data2 << 2;
output_result = {
input_data1[31:26], temp[25:0]};
end
default:
begin
output_result = input_data1 + input_data2;
end
endcase
end
end
endmodule
(14)ALU_output:
module ALU_output(
input [31:0] input_data,
input clk,
output reg [31:0] output_data
);
always@(negedge clk) begin
output_data = input_data;
end
endmodule
(15)DMEM:
`include "./cpu.vh"
module DMEM(
input [31:0] alu_address, // 要访问或写入的地址
input [31:0] data, // 要写入的数据
input write_enable, // 是否可以写
input clk,
input reset,
output [31:0] out
);
reg [31:0] memory [255:0]; // 256*32的数据存储缓冲器
// 用本地文件初始化存储器
initial begin
$readmemh(`DATA_FILE_PATH, memory);
end
assign out = memory[alu_address / 4];
always@(posedge clk) begin
if(reset == 1) begin
//out = 0;
end
else begin
if(write_enable == 1) begin
memory[alu_address / 4] <= data;
end
end
end
endmodule
(16)LMD:
module LMD(
input [31:0] in,
input clk,
output reg [31:0] out
);
always@(negedge clk) begin
out <= in;
end
endmodule
(17)CU:
module CU(
input [ 5:0] opcode, // 指令的操作码
input [10:0] ALUfunc, // 指令的func,用于标志指令具体执行哪种操作
input clk, // 时钟信号
input reset, // 复位信号
input equal, // R[rs] == R[rt]
input [31:0] rt, // R[rt]
output reg pc_enable, // PC是否可以修改
output reg pc_select_enable, // PC是否需要发生跳转
output reg npc_enable, // npc是否可以被修改
output reg ir_enable, // ir是否可以被修改
output reg reg_write_enable, // 寄存器组是否可以写回
output reg writeback_select_enable, // 选择要写回到的目的地址
output reg mux1_select_enable, // mux的选择信号
output reg mux2_select_enable, // mux的选择信号
output reg [5:0] alu_op, // 具体执行哪种运算
output reg mem_write_enable, // DMEM是否可以写回
output reg mem_data_select_enable // 决定要写回的是什么数据
);
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] JMP = 6'b000010;
parameter [5:0] MOVZ= 6'b001010;
parameter [4:0] STATE_IF = 5'b00001;
parameter [4:0] STATE_ID = 5'b00010;
parameter [4:0] STATE_EX = 5'b00100;
parameter [4:0] STATE_MEM = 5'b01000;
parameter [4:0] STATE_WB = 5'b10000;
reg [4:0] STATE;
reg [4:0] NEXT_STATE;
always@(negedge clk) begin
if(reset == 1) begin
pc_enable <= 0; // ***
pc_select_enable <= 0;
npc_enable <= 1;
ir_enable <= 0;
reg_write_enable <= 0;
writeback_select_enable <= 0;
mux1_select_enable <= 0;
mux2_select_enable <= 0;
mem_write_enable <= 0;
mem_data_select_enable <= 0;
STATE <= STATE_WB;
NEXT_STATE <= STATE_IF;
end
else begin
STATE <= NEXT_STATE;
// 根据指令判断执行哪种ALU操作
case(opcode)
ALU : alu_op <= ALUfunc[5:0];
BEQ : alu_op <= 6'b101000;
JMP : alu_op <= 6'b110000;
default: alu_op <= 6'b111000;
endcase
// 更改当前所处的状态
case (NEXT_STATE)
STATE_IF : NEXT_STATE <= STATE_ID;
STATE_ID : NEXT_STATE <= STATE_EX;
STATE_EX : NEXT_STATE <= STATE_MEM;
STATE_MEM: NEXT_STATE <= STATE_WB;
STATE_WB : NEXT_STATE <= STATE_IF;
endcase
pc_enable <= (STATE == STATE_WB)? 1'b1 : 1'b0;
ir_enable <= (STATE == STATE_WB)? 1'b1 : 1'b0;
mem_write_enable <= (STATE == STATE_EX && opcode == SW)? 1'b1 : 1'b0;
pc_select_enable <= (STATE == STATE_WB && (opcode == JMP || (opcode == BEQ && equal == 1)))? 1'b1 : 1'b0;
writeback_select_enable <= (opcode == LW)? 1'b1 : 1'b0;
mux1_select_enable <= (opcode == JMP || opcode == BEQ)? 1'b0 : 1'b1;
mux2_select_enable <= (opcode == JMP || opcode == BEQ || opcode == LW || opcode == SW)? 1'b1 : 1'b0;
mem_data_select_enable <= (opcode == LW)? 1'b0 : 1'b1;
if(STATE == STATE_MEM && (opcode == LW || (opcode == ALU && !(ALUfunc[5:0] == MOVZ && rt != 0)))) begin
reg_write_enable <= 1'b1;
end
else begin
reg_write_enable <= 1'b0;
end
end
end
endmodule
② 各模块的功能测试(每个模块作为一个部分,包括测试方案、测试过程和测试波形等)
(1)PC:
(2)PC_select:
(3)IR:
(4)ALU:
③ 系统的详细设计
`timescale 1ns / 1ps
module cpu (
input clk , // clock, 100MHz
input rst , // active high, board switch 0 (SW0)
// debug signals
output [31:0] debug_wb_pc , // 当前正在执行指令的PC
output debug_wb_rf_wen , // 当前通用寄存器组的写使能信号
output [4 :0] debug_wb_rf_addr, // 当前通用寄存器组写回的寄存器编号
output [31:0] debug_wb_rf_wdata // 当前指令需要写回的数据
);
//data wire
wire [31:0] new_address;
wire [31:0] pc_output_address;
wire [31:0] pc_adder_output;
wire [31:0] npc_output;
wire [31:0] imem_ir;
wire [31:0] current_instruction;
wire [31:0] writeback_data;
wire [ 4:0] writeback_address;
wire [31:0] reg_output1;
wire [31:0] reg_output2;
wire [31:0] holderA;
wire [31:0] holderB;
wire [31:0] imm32b;
wire [31:0] holderImm;
wire [31:0] alu_dataA;
wire [31:0] alu_dataB;
wire zero;
wire [31:0] alu_temp;
wire [31:0] alu_result;
wire [31:0] mem_data_temp;
wire [31:0] mem_data;
//command wire
wire [ 5:0] opcode;
wire [ 4:0] rs1;
wire [ 4:0] rs2;
wire [ 4:0] rrd;
wire [10:0] alu_func;
wire [25:0] imm26b;
//command bind
assign opcode = current_instruction[31:26];
assign rs1 = current_instruction[25:21];
assign rs2 = current_instruction[20:16];
assign rrd = current_instruction[15:11];
assign alu_func = current_instruction[10: 0];
assign imm26b = current_instruction[25: 0];
//CU enable
wire pc_enable;
wire pc_select_enable;
wire npc_enable;
wire ir_enable;
wire reg_write_enable;
wire writeback_select_enable;
wire mux1_select_enable;
wire mux2_select_enable;
wire [5:0] alu_op;
wire mem_write_enable;
wire mem_data_select_enable;
//CU input
wire equal;
wire [31:0] rt;
//CPU output
assign debug_wb_pc = pc_output_address;
assign debug_wb_rf_wen = reg_write_enable;
assign debug_wb_rf_addr = writeback_address;
assign debug_wb_rf_wdata = writeback_data;
//IF state
PC pc(
.clk (clk ),
.reset (rst ),
.new_address (new_address ),
.pc_enable (pc_enable ),
.output_address(pc_output_address)
);
ADD pc_adder(
.data1 (pc_output_address),
.data2 (32'h00000004),
.result (pc_adder_output )
);
PC_select pc_select(
.JMP_address (alu_temp ),
.PC_address (pc_adder_output ),
.pc_select (pc_select_enable),
.next_address(new_address )
);
NPC npc(
.clk (clk ),
.reset (rst ),
.input_address (new_address),
.npc_enable (npc_enable ),
.output_address(npc_output )
);
IMEM instruction_memory(
.address (pc_output_address),
.output_instruction (imem_ir )
);
IR ir(
.clk (clk ),
.reset (rst ),
.input_instruction (imem_ir ),
.ir_enable (ir_enable ),
.output_instruction(current_instruction)
);
//ID state
Registers register_set(
.clk (clk ),
.reset (rst ),
.RS_1 (rs1 ),
.RS_2 (rs2 ),
.writeback_address(writeback_address),
.writeback_data (writeback_data ),
.write_enable (reg_write_enable ),
.output_data_1 (reg_output1 ),
.output_data_2 (reg_output2 )
);
Data_holder reg_holder_A(
.clk(clk ),
.in (reg_output1),
.out(holderA )
);
Data_holder reg_holder_B(
.clk(clk ),
.in (reg_output2),
.out(holderB )
);
Extender extender(
.opcode (opcode),
.input_26bimm (imm26b),
.output_32bimm(imm32b)
);
Data_holder imm_holder(
.clk(clk ),
.in (imm32b ),
.out(holderImm)
);
Writeback_Select writeback_select(
.select (writeback_select_enable),
.rt_address (rs2 ), // ***
.rd_address (rrd ),
.writeback_address(writeback_address )
);
//EX state
Equal eqcontrol(
.clk (clk ),
.reset (rst ),
.data_A(holderA),
.data_B(holderB),
.equal (equal )
);
MUX32 mux1(
.input_data0(npc_output ),
.input_data1(holderA ),
.select (mux1_select_enable),
.output_data(alu_dataA )
);
MUX32 mux2(
.input_data0(holderB ),
.input_data1(holderImm ),
.select (mux2_select_enable),
.output_data(alu_dataB )
);
ALU alu(
.clk (clk ),
.reset (rst ),
.alu_op (alu_op ),
.input_data1 (alu_dataA),
.input_data2 (alu_dataB),
.output_result(alu_temp )
);
ALU_output alu_output(
.clk (clk ),
.input_data (alu_temp ),
.output_data(alu_result)
);
//MEM state
DMEM data_memory(
.clk (clk ),
.reset (rst ),
.alu_address (alu_result ),
.data (holderB ), // ***
.write_enable (mem_write_enable),
.out (mem_data_temp )
);
LMD mem_holder(
.clk(clk ),
.in (mem_data_temp),
.out(mem_data )
);
//WB state
MUX32 mux3(
.input_data0(mem_data ),
.input_data1(alu_result ),
.select (mem_data_select_enable),
.output_data(writeback_data )
);
//CU
CU cu(
.clk (clk ),
.reset (rst ),
.opcode (opcode ),
.ALUfunc (alu_func ),
.equal (equal ),
.rt (reg_output2 ), // ***
.pc_enable (pc_enable ),
.pc_select_enable (pc_select_enable ),
.npc_enable (npc_enable ),
.ir_enable (ir_enable ),
.reg_write_enable (reg_write_enable ),
.writeback_select_enable(writeback_select_enable),
.mux1_select_enable (mux1_select_enable ),
.mux2_select_enable (mux2_select_enable ),
.alu_op (alu_op ),
.mem_write_enable (mem_write_enable ),
.mem_data_select_enable (mem_data_select_enable )
);
endmodule
④ 系统的功能测试
附录
本实验使用的测试环境
1.头文件
2.寄存器文件
3.指令存储器的值
注意:指令存储器的大小为32*256,这里只列出了前23个,后面全为0!
4.数据存储器
注意:数据存储器的大小为32*256,这里只列出了前22个,后面全为0!