RISC存储程序机的设计与综合

RISC存储程序机

精简指令集计算机(RISC)指令集小,时钟周期短,每条指令的执行周期数少。RISC机优化后可以实现高效的指令流水线操作。本节将构建一个简单地RISC机。

RISC_SPM是一个RISC架构的存储程序机(SPM,Stored-Program Machine)——其指令包含在存储器的程序里。

该电路包括三个功能单元:处理器(数据通路)、控制器、存储器。程序指令及数据存放在存储器中。程序执行时同步地进行指令读取、译码和执行:
1)对ALU中的数据进行操作;
2)修改寄存器的内容;
3)修改程序计数器(PC)、指令寄存器(IR)、和地址寄存器(ADD_R)的内容;
4)修改存储单元的内容;
5)获取存储器中的数据和指令;
6)控制系统总线上的数据传送。
其中,指令寄存器(IR)用于存放当前正在执行的指令,程序计数器(PC)用于存放下一条将要执行的指令的存储地址,而地址寄存器(ADD_R)则用于保存下一个将要读/写的存储单元地址。

1、RISC_SPM:处理器

处理器包括寄存器、数据通路、控制信号和ALU。ALU能根据指令寄存器中存放的操作码对操作数进行算数、逻辑运算。

在这里插入图片描述
多路复用器Mux_1决定送往Bus_1的数据源,多路复用器Mux_2决定送往Bus_2的数据源。
Mux_1的输入来自四个内部通用寄存器(R0、R1、R2、R3)和程序计数器(PC)。
Bus_1上的内容可以被传送至ALU、存储器或Bus_2(经由Mux_2)。
Mux_2的输入来自ALU、Mux_1和存储单元。
这样,从存储器中取出的指令可以经由Bus_2装入指令寄存器;从存储器中取出的数据在送入ALU进行操作之前可以先存入;通用寄存器或操作数寄存器(Reg_Y)算术逻辑运算的结果可以经由Bus_2装入寄存器,再写入存储器中。专用寄存器(Reg_Z)用于便是ALU的操作结果是否为0(该功能可以用于监控循环次数)。

2、RISC_SPM:ALU

本例中,ALU有两个操作数(数据通路)——data_1和data_2,且指令集包含的内容如下:

指令 操作
ADD
SUB
AND 相与
NOT 按位求反

3、RISC_SPM:控制器

RISC机所有动作时序都有控制器决定。控制器根据当前指令把数据送到合适的目的地。因此,控制器的设计严重依赖于ALU性能、数据通路资源和可用时钟方案。本例将使用单时钟,并只在时钟的某个边沿(如上升沿)开始操作。控制器监控处理器状态和执行的指令,并决定控制信号的值。控制器的输入是指令字和ALU的零标识。
控制器的输入信号定义如下:
在这里插入图片描述
控制器的作用包括:

  1. 决定何时装载寄存器
  2. 控制多路复用器选择数据通道
  3. 决定何时将数据写入存储器
  4. 控制三态总线

4、RISC_SPM:指令集

RISC机由存储器中的由指令序列组成的机器语言程序控制。因此,除了机器架构,控制器的设计还依赖于处理器指令集(即程序执行的指令)。机器语言程序由8位(字节)的存储序列构成。RISC_ SPM的指令可长可短,由可执行的操作决定。

短指令格式如图7.11(a)所示。每条短指令需要1个字节存储。该字节包括4位操作码、2位源寄存器地址和2位目的寄存器地址。而长指令需要2个字节存储:第1个字节包含4位操作码,余下4位用来指定源和目的寄存器的地址,由指令决定;第2个字节用于存放指令所需存储器操作数的地址。图7.11(b)给出了2字节格式的长指令。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
程序计数器PC用于保存将要执行的下一条指令的地址。当外部复位有效时,PC被清0,这表示第一条将要执行的指令存放在存储器底部。对单周期指令来说,在时钟有效沿PC所指存储单元中的指令将装人IR且PC加1。指令译码器决定了数据通路和ALU的最终动作。两字节长指令的执行需要一个额外的时钟周期,PC所指存储单元中的第2个(指令)字节在第2个执行周期被读人后,才能完成该指令的执行。在双周期执行过程中,ALU中的暂存数据无意义。

5、RISC_SPM:控制器设计

控制器可以设计成FSM,在给定设计的架构、指令集及时钟方案后,还必须定义状态。这可以通过确定指令执行时必须完成什么样的动作来实现。下 面将使用ASM图来描述ISC_ SPM机的动作,并清晰地说明在指令支配下状态机怎样进行操作。

状态机有三个操作阶段:取指、译码和执行。取指阶段负责从存储器中获取指令,译码阶段负责解释指令、控制数据通路和加载寄存器。执行阶段则将产生指令结果。取指阶段需要两个时钟周期,一个时钟周期用来加载地址寄存器,另一个时钟周期用来从存储器中得到给定地址的数据。译码阶段在一个时钟周期内完成。执行阶段根据所执行的指令的不同可能需要0、1或2个额外的时钟周期: NOT指令的执行可在译码周期内同时完成;单字节指令,如ADD,需要用一个时钟周期来执行并将结果写人目的寄存器,源寄存器则在译码阶段进行加载;2字节指令(如RD)的执行需要两个时钟周期一个时钟周期把指令的第2字节载入地址寄存器,另一个时钟周期用来从该地址指定的存储器中得到数据,并将其载人目的寄存器。RISC_ SPM的控制器有11个状态。各状态中产生的控制行为如下所列。
在这里插入图片描述
RISC_SPM控制部分的ASM如图7.12所示,为了清楚可见,对状态进行了编号。完成ASM图的创建后,设计者可以根据给定架构编写整个RISC机的verilog描述。

该过程按下列步骤展开:首先根据RISC机划分对各功能单元进行声明;接着定义端口和变量,并进行语法检查;然后对各个单元进行描述、调试和验证;最后整合设计并进行功能验证。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

顶层模块RISC_SPM

最先给出的是verilog的顶层模块RISC_SPM,对图7.10中各个模块进行整合。实例化三个模块 Processing_Unit 、 Control_Unit 、Memory_Unit 实例名为M0_Processor、M1_Controller、M2_SRAM,这三个结构/功能单元之间的数据通路结构(尺寸)参数也在层次里的这个阶段进行声明。

//Verilog HDL

module RISC_SPM (clk, rst);
  parameter word_size = 8;
  parameter Sel1_size = 3;
  parameter Sel2_size = 2;
  wire [Sel1_size-1: 0] Sel_Bus_1_Mux;
  wire [Sel2_size-1: 0] Sel_Bus_2_Mux;

  input clk, rst;

  // Data Nets
  wire zero;
  wire [word_size-1: 0] instruction, address, Bus_1, mem_word;
   
  // Control Nets
  wire Load_R0, Load_R1, Load_R2, Load_R3, Load_PC, Inc_PC, Load_IR;   
  wire Load_Add_R, Load_Reg_Y, Load_Reg_Z;
  wire write;
 
  Processing_Unit M0_Processor 
    (instruction, zero, address, Bus_1, mem_word, Load_R0, Load_R1,
    Load_R2, Load_R3, Load_PC, Inc_PC, Sel_Bus_1_Mux, Load_IR, 
    Load_Add_R, Load_Reg_Y,
    Load_Reg_Z,  Sel_Bus_2_Mux, clk, rst);

  Control_Unit M1_Controller (Load_R0, Load_R1, Load_R2, Load_R3, Load_PC, Inc_PC, 
    Sel_Bus_1_Mux, Sel_Bus_2_Mux , Load_IR, Load_Add_R, Load_Reg_Y, Load_Reg_Z, 
    write, instruction, zero, clk, rst);

  Memory_Unit M2_SRAM (
    .data_out(mem_word), 
    .data_in(Bus_1), 
    .address(address), 
    .clk(clk),
    .write(write) );
endmodule

处理器

处理器的verilog模型描述图7.10所示的功能单元的结构、寄存器操作和数据通路操作。该处理器例化的其他几个模块也必须加以声明。

//Verilog HDL

module Processing_Unit (instruction, Zflag, address, Bus_1, mem_word, Load_R0, Load_R1, Load_R2, 
  Load_R3, Load_PC, Inc_PC, Sel_Bus_1_Mux, Load_IR, Load_Add_R, Load_Reg_Y, Load_Reg_Z, 
  Sel_Bus_2_Mux, clk, rst);

  parameter word_size = 8;
  parameter op_size = 4;
  parameter Sel1_size = 3;
  parameter Sel2_size = 2;

  output [word_size-1: 0] 	instruction, address, Bus_1;
  output 			Zflag;

  input [word_size-1: 0]  	mem_word;
  input 			Load_R0, Load_R1, Load_R2, Load_R3, Load_PC, Inc_PC;
  input [Sel1_size-1: 0] 	Sel_Bus_1_Mux;
  input [Sel2_size-1: 0] 	Sel_Bus_2_Mux;
  input 			Load_IR, Load_Add_R, Load_Reg_Y, Load_Reg_Z;
  input 			clk, rst;

  wire			Load_R0, Load_R1, Load_R2, Load_R3;
  wire [word_size-1: 0] 	Bus_2;
  wire [word_size-1: 0] 	R0_out, R1_out, R2_out, R3_out;
  wire [word_size-1: 0] 	PC_count, Y_value, alu_out;
  wire 			alu_zero_flag;
  wire [op_size-1 : 0] 	opcode = instruction [word_size-1: word_size-op_size];

  Register_Unit 		R0 	(R0_out, Bus_2, Load_R0, clk, rst);
  Register_Unit 		R1 	(R1_out, Bus_2, Load_R1, clk, rst);
  Register_Unit 		R2 	(R2_out, Bus_2, Load_R2, clk, rst);
  Register_Unit 		R3 	(R3_out, Bus_2, Load_R3, clk, rst);
  Register_Unit 		Reg_Y 	(Y_value, Bus_2, Load_Reg_Y, clk, rst);
  D_flop 			Reg_Z 	(Zflag, alu_zero_flag, Load_Reg_Z, clk, rst);
  Address_Register 	Add_R	(address, Bus_2, Load_Add_R, clk, rst);
  Instruction_Register	IR	(instruction, Bus_2, Load_IR, clk, rst);
  Program_Counter 	PC	(PC_count, Bus_2, Load_PC, Inc_PC, clk, rst);
  Multiplexer_5ch 		Mux_1 	(Bus_1, R0_out, R1_out, R2_out, R3_out, PC_count, Sel_Bus_1_Mux);
  Multiplexer_3ch 		Mux_2	(Bus_2, alu_out, Bus_1, mem_word, Sel_Bus_2_Mux);
  Alu_RISC 		ALU	(alu_zero_flag, alu_out, Y_value, Bus_1, opcode);
endmodule 

module Register_Unit (data_out, data_in, load, clk, rst);
  parameter 		word_size = 8;
  output [word_size-1: 0] 	data_out;
  input 	[word_size-1: 0] 	data_in;
  input 			load;
  input 			clk, rst;
  reg 	[word_size-1: 0]	data_out;

  always @ (posedge clk or negedge rst)
    if (rst == 0) data_out <= 0; else if (load) data_out <= data_in;
endmodule

module D_flop (data_out, data_in, load, clk, rst);
  output 		data_out;
  input 		data_in;
  input 		load;
  input 		clk, rst;
  reg 		data_out;

  always @ (posedge clk or negedge rst)
    if (rst == 0) data_out <= 0; else if (load == 1)data_out <= data_in;
endmodule

 module Address_Register (data_out, data_in, load, clk, rst);
  parameter word_size = 8;
  output [word_size-1: 0] 	data_out;
  input 	[word_size-1: 0] 	data_in;
  input 			load, clk, rst;
  reg 	[word_size-1: 0]	data_out;
  always @ (posedge clk or negedge rst)
    if (rst == 0) data_out <= 0; else if (load) data_out <= data_in;
endmodule

module Instruction_Register (data_out, data_in, load, clk, rst);
  parameter word_size = 8;
  output [word_size-1: 0] 	data_out;
  input 	[word_size-1: 0] 	data_in;
  input 			load;
  input 			clk, rst;
  reg 	[word_size-1: 0]	data_out;
  always @ (posedge clk or negedge rst)
    if (rst == 0) data_out <= 0; else if (load) data_out <= data_in; 
endmodule

module Program_Counter (count, data_in, Load_PC, Inc_PC, clk, rst);
  parameter word_size = 8;
  output [word_size-1: 0] 	count;
  input 	[word_size-1: 0] 	data_in;
  input 			Load_PC, Inc_PC;
  input 			clk, rst;
  reg 	[word_size-1: 0]	count;
  always @ (posedge clk or negedge rst)
    if (rst == 0) count <= 0; else if (Load_PC) count <= data_in; else if  (Inc_PC) count <= count +1;
endmodule

module Multiplexer_5ch (mux_out, data_a, data_b, data_c, data_d, data_e, sel);
  parameter word_size = 8;
  output [word_size-1: 0] 	mux_out;
  input 	[word_size-1: 0] 	data_a, data_b, data_c, data_d, data_e;
  input 	[2: 0] sel;
 
  assign  mux_out = (sel == 0) 	
? data_a: (sel == 1) 
? data_b : (sel == 2) 
? data_c: (sel == 3) 
? data_d : (sel == 4) 
? data_e : 'bx;
endmodule

module Multiplexer_3ch (mux_out, data_a, data_b, data_c, sel);
  parameter 	word_size = 8;
  output 		[word_size-1: 0]	 mux_out;
  input 		[word_size-1: 0] 	data_a, data_b, data_c;
  input 		[1: 0] sel;

  assign  mux_out = (sel == 0) ? data_a: (sel == 1) ? data_b : (sel == 2) ? data_c: 'bx;
endmodule

ALU

ALU被描述为电平敏感的组合逻辑,这个周期操作只要数据通路或选择总线发生变化就会被激活。使用参数可以增强可读性,并减少代码编写错误的可能性。

//Verilog HDL

/*ALU Instruction		Action
ADD			Adds the datapaths to form data_1 + data_2.
SUB			Subtracts the datapaths to form data_1 - data_2.
AND			Takes the bitwise-and of the datapaths, data_1 & data_2.
NOT			Takes the bitwise Boolean complement of data_1.
*/
// Note: the carries are ignored in this model.
 
module Alu_RISC (alu_zero_flag, alu_out, data_1, data_2, sel);
  parameter word_size = 8;
  parameter op_size = 4;
  // Opcodes
  parameter NOP 	= 4'b0000;
  parameter ADD 	= 4'b0001;
  parameter SUB 	= 4'b0010;
  parameter AND 	= 4'b0011;
  parameter NOT 	= 4'b0100;
  parameter RD  		= 4'b0101;
  parameter WR		= 4'b0110;
  parameter BR		= 4'b0111;
  parameter BRZ 		= 4'b1000;

  output 			alu_zero_flag;
  output [word_size-1: 0] 	alu_out;
  input 	[word_size-1: 0] 	data_1, data_2;
  input 	[op_size-1: 0] 	sel;
  reg 	[word_size-1: 0]	alu_out;

  assign  alu_zero_flag = ~|alu_out;
  always @ (sel or data_1 or data_2)  
     case  (sel)
      NOP:	alu_out = 0;
      ADD:	alu_out = data_1 + data_2;  // Reg_Y + Bus_1
      SUB:	alu_out = data_2 - data_1;
      AND:	alu_out = data_1 & data_2;
      NOT:	alu_out = ~ data_2;	 // Gets data from Bus_1
      default: 	alu_out = 0;
    endcase 
endmodule

控制器

规模庞大的控制器可以根据图7.12所示的ASM图进行简单设计。首先要声明端口和变量,然后使用由条件操作代码(?..:)表示的嵌套连续赋值语句来描述多路复用。这里使用了两种周期行为:电平敏感行为来描述输出信号和下一状态的组合逻辑,边沿敏感行为用来同步时钟变化。

//Verilog HDL
module Control_Unit (
  Load_R0, Load_R1, 
  Load_R2, Load_R3, 
  Load_PC, Inc_PC, 
  Sel_Bus_1_Mux, Sel_Bus_2_Mux,
  Load_IR, Load_Add_R, Load_Reg_Y, Load_Reg_Z, 
  write, instruction, zero, clk, rst);
 
  parameter word_size = 8, op_size = 4, state_size = 4;
  parameter src_size = 2, dest_size = 2, Sel1_size = 3, Sel2_size = 2;
  // State Codes
  parameter S_idle = 0, S_fet1 = 1, S_fet2 = 2, S_dec = 3;
  parameter  S_ex1 = 4, S_rd1 = 5, S_rd2 = 6;  
  parameter S_wr1 = 7, S_wr2 = 8, S_br1 = 9, S_br2 = 10, S_halt = 11;  
  // Opcodes
  parameter NOP = 0, ADD = 1, SUB = 2, AND = 3, NOT = 4;
  parameter RD  = 5, WR =  6,  BR =  7, BRZ = 8;  
  // Source and Destination Codes  
  parameter R0 = 0, R1 = 1, R2 = 2, R3 = 3;  

  output Load_R0, Load_R1, Load_R2, Load_R3;
  output Load_PC, Inc_PC;
  output [Sel1_size-1: 0] Sel_Bus_1_Mux;
  output Load_IR, Load_Add_R;
  output Load_Reg_Y, Load_Reg_Z;
  output [Sel2_size-1: 0] Sel_Bus_2_Mux;
  output write;
  input [word_size-1: 0] instruction;
  input zero;
  input clk, rst;
 
  reg [state_size-1: 0] state, next_state;
  reg Load_R0, Load_R1, Load_R2, Load_R3, Load_PC, Inc_PC;
  reg Load_IR, Load_Add_R, Load_Reg_Y;
  reg Sel_ALU, Sel_Bus_1, Sel_Mem;
  reg Sel_R0, Sel_R1, Sel_R2, Sel_R3, Sel_PC;
  reg Load_Reg_Z, write;
  reg err_flag;

  wire [op_size-1: 0] opcode = instruction [word_size-1: word_size - op_size];
  wire [src_size-1: 0] src = instruction [src_size + dest_size -1: dest_size];
  wire [dest_size-1: 0] dest = instruction [dest_size -1: 0];
 
  // Mux selectors
  assign  Sel_Bus_1_Mux[Sel1_size-1: 0] = Sel_R0 ? 0:
				 Sel_R1 ? 1:
				 Sel_R2 ? 2:
				 Sel_R3 ? 3:
				 Sel_PC ? 4: 3'bx;  // 3-bits, sized number

  assign  Sel_Bus_2_Mux[Sel2_size-1: 0] = Sel_ALU ? 0:
				 Sel_Bus_1 ? 1:
				 Sel_Mem ? 2: 2'bx;

  always @ (posedge clk or negedge rst) begin: State_transitions
    if (rst == 0) state <= S_idle; else state <= next_state; end

/*  always @ (state or instruction or zero) begin:  Output_and_next_state	

Note: The above event control expression leads to incorrect operation.  The state transition causes the activity to be evaluated once, then the resulting instruction change causes it to be evaluated again, but with the residual value of opcode.  On the second pass the value seen is the value opcode had before the state change, which results in Sel_PC = 0 in state 3, which will cause a return to state 1 at the next clock.  Finally, opcode is changed, but this does not trigger a re-evaluation because it is not in the event control expression.  So, the caution is to be sure to use opcode in the event control expression. That way, the final execution of the behavior uses the value of opcode that results from the state change, and leads to the correct value of Sel_PC.
说明:上述敏感列表将导致错误操作。状态转移触发语旬执行,接着指令(instruction)的变化会再次触发语句执行,但此时opcode的值不变。这样第二次看起米opcode仍保持state变化前的值,这使得状态3下的Sel_ PC=0,于是下一时钟会返回到状态1。最后opcode发生变化,但因其不在事件控制表达式中,不会再次触发语句执行。因此注意必须保证opeode在敏感列表中。这样最终动作将依据状态变化后的opcode值,并能得到正确的Sel_PC值。
*/ 

  always @ (state or opcode or src or dest or zero) begin: Output_and_next_state 
    Sel_R0 = 0; 	Sel_R1 = 0;     	Sel_R2 = 0;    	Sel_R3 = 0;     	Sel_PC = 0;
    Load_R0 = 0; 	Load_R1 = 0; 	Load_R2 = 0; 	Load_R3 = 0;	Load_PC = 0;

    Load_IR = 0;	Load_Add_R = 0;	Load_Reg_Y = 0;	Load_Reg_Z = 0;
    Inc_PC = 0; 
    Sel_Bus_1 = 0; 
    Sel_ALU = 0; 
    Sel_Mem = 0; 
    write = 0; 
    err_flag = 0;	// Used for de-bug in simulation		
    next_state = state;

     case  (state)	S_idle:		next_state = S_fet1;      
S_fet1:		begin       	  	  	
  next_state = S_fet2; 
      	  	  		  Sel_PC = 1;
      	  	  		  Sel_Bus_1 = 1;
      	  	   		  Load_Add_R = 1; 
    				end
      		S_fet2:		begin 		
  next_state = S_dec; 
  Sel_Mem = 1;
      	  	  		  Load_IR = 1; 
      	  	  		  Inc_PC = 1;
    				end

      		S_dec:  	 	case  (opcode) 
      		 		  NOP: next_state = S_fet1;
		  		  ADD, SUB, AND: begin
 		    		    next_state = S_ex1;
		    		    Sel_Bus_1 = 1;
		    		    Load_Reg_Y = 1;
		     		    case  (src)
		      		      R0: 		Sel_R0 = 1; 
		      		      R1: 		Sel_R1 = 1; 
		      		      R2: 		Sel_R2 = 1;
		      		      R3: 		Sel_R3 = 1; 
		      		      default : 	err_flag = 1;
		    		    endcase   
  end // ADD, SUB, AND

			 	  NOT: begin
			    	    next_state = S_fet1;
			    	    Load_Reg_Z = 1;
			    	    Sel_Bus_1 = 1; 
			    	    Sel_ALU = 1; 
		 	     	    case  (src)
			      	      R0: 		Sel_R0 = 1;			      
      				      R1: 		Sel_R1 = 1;
			      	      R2: 		Sel_R2 = 1;			      
 			      	      R3: 		Sel_R3 = 1; 
			      	      default : 	err_flag = 1;
			    	    endcase   
  			     	    case  (dest)
			      	      R0: 		Load_R0 = 1; 
			      	      R1: 		Load_R1 = 1;			      
      				      R2: 		Load_R2 = 1;
			      	      R3: 		Load_R3 = 1;			      
      				      default: 	err_flag = 1;
			    	    endcase   
  end // NOT
  				  
  RD: begin
			    	    next_state = S_rd1;
			    	    Sel_PC = 1; Sel_Bus_1 = 1; Load_Add_R = 1; 
  end // RD

			  	  WR: begin
			    	    next_state = S_wr1;
			    	    Sel_PC = 1; Sel_Bus_1 = 1; Load_Add_R = 1; 
  end  // WR

			  	  BR: begin 
			    	    next_state = S_br1;  
    Sel_PC = 1; Sel_Bus_1 = 1; Load_Add_R = 1; 
			    	  end  // BR
	
  				  BRZ: if (zero == 1) begin
			    	    next_state = S_br1; 
    Sel_PC = 1; Sel_Bus_1 = 1; Load_Add_R = 1; 
			    	  end // BRZ
			  	  else begin 
    next_state = S_fet1; 
    Inc_PC = 1; 
  end
        		  		  default : next_state = S_halt;
				endcase  // (opcode)

    	      	S_ex1:		begin 
  			  	  next_state = S_fet1;
			  	  Load_Reg_Z = 1;
			  	  Sel_ALU = 1; 
		 	   	  case  (dest)
  	    		    	    R0: begin Sel_R0 = 1; Load_R0 = 1; end
			    	    R1: begin Sel_R1 = 1; Load_R1 = 1; end
			    	    R2: begin Sel_R2 = 1; Load_R2 = 1; end
			    	    R3: begin Sel_R3 = 1; Load_R3 = 1; end
			    	    default : err_flag = 1; 
			   	  endcase  
				end 

    	      	S_rd1:		begin 
  next_state = S_rd2;
			  	  Sel_Mem = 1;
			  	  Load_Add_R = 1; 
			  	  Inc_PC = 1;
				end

    	      	S_wr1: 		begin
			  	  next_state = S_wr2;
			  	  Sel_Mem = 1;
			  	  Load_Add_R = 1; 
			  	  Inc_PC = 1;
				end 

      		S_rd2:		begin 
  			  	  next_state = S_fet1;
			  	  Sel_Mem = 1;
		 	   	  case  (dest) 
    			    	    R0: 		Load_R0 = 1; 
		 	    	    R1: 		Load_R1 = 1; 
		 	    	    R2: 		Load_R2 = 1; 
		 	    	    R3: 		Load_R3 = 1; 
			    	    default : 	err_flag = 1;
			  	  endcase  
				end

    	      	S_wr2:		begin 
     			  	  next_state = S_fet1;
			  	  write = 1;
		 	  	  case  (src)
    			    	    R0: 		Sel_R0 = 1;		 	    
    				    R1: 		Sel_R1 = 1;		 	    
   				    R2: 		Sel_R2 = 1; 		 	    
   				    R3: 		Sel_R3 = 1;			    
    				    default : 	err_flag = 1;
			  	  endcase  
				end

    	      	S_br1:		begin next_state = S_br2; Sel_Mem = 1; Load_Add_R = 1; end
    	      	S_br2:		begin next_state = S_fet1; Sel_Mem = 1; Load_PC = 1; end
    	      	S_halt:  		next_state = S_halt;
		default:		next_state = S_idle;
     endcase    
  end
endmodule

为了简单起见,存储单元用D触发器阵列描述。另一种方法是采用外部SRAM

//Verilog HDL

module Memory_Unit (data_out, data_in, address, clk, write);
  parameter word_size = 8;
  parameter memory_size = 256;

  output [word_size-1: 0] data_out;
  input [word_size-1: 0] data_in;
  input [word_size-1: 0] address;
  input clk, write;
  reg [word_size-1: 0] memory [memory_size-1: 0];

  assign data_out = memory[address];

  always @ (posedge clk)
    if (write) memory[address] = data_in;
endmodule

6、RISC_SPM:程序执行

下面给出RISC_SPM程序执行的验证平台。lest_RISC_SPM定义了用来显示存储器字数据的指针,使用一次性操作(initial)刷新存储器,并将小段程序和数据加载到存储器不同区域。该程序可执行以下操作:(1)读存储器并把数据加载到处理器的寄存器中;(2)执行减法修改循环计数; (3)在循环过程中将寄存器内容相加;(4)当循环指针为0时停止( halt状态)。程序执行结果如图7.13所示。

在这里插入图片描述

testbench文件代码:

//Verilog HDL

module test_RISC_SPM ();
  reg rst;
  wire clk;
  parameter word_size = 8;
  reg [8: 0] k;

  Clock_Unit M1 (clk);
  RISC_SPM M2 (clk, rst);

// define probes
  wire [word_size-1: 0] word0, word1, word2, word3, word4, word5, word6;
  wire [word_size-1: 0] word7, word8, word9, word10, word11, word12, word13;
  wire [word_size-1: 0] word14;

  wire [word_size-1: 0] word128, word129, word130, word131, word132, word255;
  wire [word_size-1: 0] word133, word134, word135, word136, word137;
  wire [word_size-1: 0] word138, word139, word140;
  assign word0 = M2.M2_SRAM.memory[0];
  assign word1 = M2.M2_SRAM.memory[1];
  assign word2 = M2.M2_SRAM.memory[2];
  assign word3 = M2.M2_SRAM.memory[3];
  assign word4 = M2.M2_SRAM.memory[4];
  assign word5 = M2.M2_SRAM.memory[5];
  assign word6 = M2.M2_SRAM.memory[6];
  assign word7 = M2.M2_SRAM.memory[7];
  assign word8 = M2.M2_SRAM.memory[8];
  assign word9 = M2.M2_SRAM.memory[9];
  assign word10 = M2.M2_SRAM.memory[10];
  assign word11 = M2.M2_SRAM.memory[11];
  assign word12 = M2.M2_SRAM.memory[12];
  assign word13 = M2.M2_SRAM.memory[13];
  assign word14 = M2.M2_SRAM.memory[14];
   
  assign word128 = M2.M2_SRAM.memory[128];
  assign word129 = M2.M2_SRAM.memory[129];
  assign word130 = M2.M2_SRAM.memory[130];
  assign word131 = M2.M2_SRAM.memory[131];
  assign word132 = M2.M2_SRAM.memory[132];
  assign word133 = M2.M2_SRAM.memory[133];
  assign word134 = M2.M2_SRAM.memory[134];
  assign word135 = M2.M2_SRAM.memory[135];
  assign word136 = M2.M2_SRAM.memory[136];
  assign word137 = M2.M2_SRAM.memory[137];
  assign word138 = M2.M2_SRAM.memory[138];
  assign word139 = M2.M2_SRAM.memory[139];
  assign word140 = M2.M2_SRAM.memory[140];


  assign word255 = M2.M2_SRAM.memory[255];

 initial #2800 $finish;
 
//Flush Memory
initial begin: Flush_Memory
  #2 rst = 0; for (k=0; k<=255; k=k+1)M2.M2_SRAM.memory[k] = 0; #10 rst = 1;
end

initial begin: Load_program
  #5
			 // opcode_src_dest
  M2.M2_SRAM.memory[0] = 8'b0000_00_00;		// NOP
  M2.M2_SRAM.memory[1] = 8'b0101_00_10;		// Read 130 to R2
  M2.M2_SRAM.memory[2] = 130;
  M2.M2_SRAM.memory[3] = 8'b0101_00_11;		// Read 131 to R3
  M2.M2_SRAM.memory[4] = 131;
  M2.M2_SRAM.memory[5] = 8'b0101_00_01;		// Read 128 to R1
  M2.M2_SRAM.memory[6] = 128;
  M2.M2_SRAM.memory[7] = 8'b0101_00_00;		// Read 129 to R0
  M2.M2_SRAM.memory[8] = 129;

  M2.M2_SRAM.memory[9] = 8'b0010_00_01;		// Sub R1-R0 to R1

  M2.M2_SRAM.memory[10] = 8'b1000_00_00;		// BRZ 
  M2.M2_SRAM.memory[11] = 134;				// Holds address for BRZ


  M2.M2_SRAM.memory[12] = 8'b0001_10_11;		// Add R2+R3 to R3
  M2.M2_SRAM.memory[13] = 8'b0111_00_11;		// BR
  M2.M2_SRAM.memory[14] = 140;

  // Load data
  M2.M2_SRAM.memory[128] = 6;
  M2.M2_SRAM.memory[129] = 1;
  M2.M2_SRAM.memory[130] = 2;
  M2.M2_SRAM.memory[131] = 0;
  M2.M2_SRAM.memory[134] = 139;
  //M2.M2_SRAM.memory[135] = 0;
  M2.M2_SRAM.memory[139] = 8'b1111_00_00;		// HALT
  M2.M2_SRAM.memory[140] = 9;				//  Recycle
end 
endmodule

module Clock_Unit(output reg clock);
	parameter delay = 0;
	parameter half_cycle = 10;
	initial begin
		#delay clock = 0;
		forever #half_cycle clock = ~clock;
		end
endmodule

仿真结果:

在这里插入图片描述
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章