设计并实现一个单周期非流水的CPU(哈工大计组实验二:给定指令系统的处理器设计)

一、实验目的

1.掌握Xilinx ISE集成开发环境和ModelSim仿真工具的使用方法
2.掌握VERILOG语言
3.掌握FPGA编程方法及硬件调试手段
4.深刻理解处理器结构和计算机系统的整体工作原理

二、实验环境(实验设备、开发环境)

  1. Windows10
  2. ISE14.7
  3. NEXYS3开发板

三、设计思想

1.指令格式设计

1
2
3

2.处理器结构设计框图及功能描述

1

上图是一个简单的基本上能够在单周期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,用于存放下一条要执行的指令的地址。
1

模块名称及功能:IMEM,指令存储器,用于存放所有要执行的指令。

2

模块名称及功能:IR,用于存放正要执行的指令。

3

模块名称及功能:NPC,用于暂存pc+4的值。

4

模块名称及功能:ADD,用于执行加法运算。
5

模块名称及功能:PC_select,用于在pc+4和跳转指令中地址进行选择。

6

模块名称及功能:Registers,寄存器组文件,这里实现了32个32位的通用寄存器。
7

模块名称及功能:Data_holder,用来暂存数据,A、B、IMM都是这个模块的实例化。

8

模块名称及功能:Extender,进行位扩展,如果是无条件跳转J指令,则将后26位符号扩展为32位,否则将后16位符号扩展为32位。

9

模块名称及功能:MUX,数据选择器。

10

模块名称及功能:ALU,算数运算单元,执行和指令相关的一些计算。

11

模块名称及功能:ALU_output,暂存ALU的结果。

11

模块名称及功能:DMEM,数据存储器。

12

模块名称及功能:LMD,用于暂存DMEM的数据。

13

模块名称及功能:CU,CPU的控制器,随着时钟节拍的改变而相应的变化控制信号。

14

四、实验设计及测试

用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:

1

(2)PC_select:

2

(3)IR:

3

(4)ALU:

3

4
5

③ 系统的详细设计

`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

附录

本实验使用的测试环境

1.头文件

1

2.寄存器文件

2

3.指令存储器的值

3
注意:指令存储器的大小为32*256,这里只列出了前23个,后面全为0!

4.数据存储器

4
注意:数据存储器的大小为32*256,这里只列出了前22个,后面全为0!

猜你喜欢

转载自blog.csdn.net/zhang_qing_yun/article/details/121049946
今日推荐