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

仿真結果:

在這裏插入圖片描述
在這裏插入圖片描述

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