1. 程式人生 > >微程式控制型簡單CPU模型Verilog HDL實現

微程式控制型簡單CPU模型Verilog HDL實現

一、設計目標

  1.  掌握微程式控制器的基本原理
  2.  設計可以實現實現基本的指令運算指令、資料傳輸指令、輸入輸出指令、轉移指令;並且具有中斷和原碼一位乘法功能
  3.  使用Verilog HDL 在Max Plus2上實現CPU模型的模擬

注:我是在MaxPlus2上實現的,由於MaxPlus2太古老了,推薦大家使用Quartus。

二、指令設計

1、指令格式

單位元組指令:

操作碼 OP  4位

目的暫存器 Rd  2位

源暫存器 Rs  2位


雙位元組指令:

操作碼 OP  4位

目的暫存器 Rd  2位

源暫存器 Rs 2位

立即數字段  8位

2、指令集

本CPU模型有13條指令,其中0-9為單位元組指令,10-13為雙位元組指令。

使用Verilog實現的時候,會在記憶體之中預先設定好執行的指令,然後模擬模擬,觀察結果。

序號

指令助記符

功能

操作碼

舉例

機器碼

0

IN Rd

輸入

IN←Rd

0000

IN R2

0000 10 00

1

OUT Rs

輸出

OUT←Rs

0001

OUT R1

0001 00 01

2

MOV Rd, Rs

暫存器傳輸

Rd←Rs

0010

MOV R1,R2

0010 10 01

3

ADD Rd, Rs

加運算

Rd←Rs+Rd

並設定Cy,Zero標誌

0011

ADD R3,R0

0011 11 00

4

AND Rd, Rs

與運算

Rd←Rs & Rd

並設定Zero標誌

0100

AND R1,R0

0100 01 00

5

MUL

原碼一位乘法運算

 {HIGH,LOW}←RD*RS

0101

MUL RD RS

0101 01 10

6

STI

開中斷

0110

STI

0110xxxx

7

CLI

關中斷

0111

CLI

0111xxxx

8

IRET

中斷返回

1000

IRET

1000xxxx

9

HLT

停機

1001

HLT

1001xxxx

10

LDI

Rd←立即數

1010

LDI R1 59

101001xx

01011001

11

LAD

讀記憶體Rd←MEM

1011

LAD R1

10110100

12

STA

寫記憶體MEM←Rs

1100

STA R2

11001000

13

JMP target

無條件轉移

PC←地址

1101

JMP 59

110101xx

01011001

14

JC target

條件轉移,有進位時轉移

如果 FC= =1’b1,

 PC←立即數,程式實現轉移。

否則 不修改PC,程式順序執行。

1110

JC 50

11100000

01010000

三、CPU結構

結構圖


說明

這些控制訊號,會在以下程式中體現。

ALU運算器:

當ALU_B為1時,ALU輸出,否則處於高阻態

S1、S0控制ALU的運算種類

FC進位標誌暫存器: 

當做加法指令時,進位儲存在FC中

用於條件跳轉指令的判斷條件。

 

MAR地址暫存器、A、B運算暫存器:

輸出沒有三態控制,即只要輸入到暫存器,輸出就有值了。

 

程式計數器PC:

當LDPC為1時,在時鐘上升沿,接收資料。

當INC_PC為1時,在時鐘上升沿,實現PC+1。

當PC_B為1時,輸出資料。否則高阻態。

 

記憶體:

/CE=1 /WE=x,不操作。 

/CE=0 /WE=0 寫記憶體;/CE=0 /WE=1 讀記憶體。

讀記憶體,由記憶體到MDR,再由MDR到匯流排。

暫存器IR:   

 

暫存器R3~R0:

以R0為例:當R_B為1時,R輸出(根據指令判斷),

否則處於高阻態。

當LDR0為1時,在時鐘上升沿,接收資料。     

 

四、 時序

分為兩個節拍

T1:在T1上升沿,微程式控制器工作,設定微指令各欄位的值。根據各欄位的值,設定微控制訊號;各微控制訊號,控制各暫存器傳輸到匯流排BUS。

T2:在T2的上升沿,當LDXXX的訊號有效時,將資料從匯流排輸入到暫存器中


五、微程式控制器

1、微指令格式

運算器

2位

向匯流排輸出

3位

從匯流排輸入

3位

下地址

6位

S1 S0

XXX_B

LDXXX

uMA

2、欄位說明

XXX_B為1時,XXX部件輸出到總線上。

LDXXX為1時,當T2上升沿到來時,將總線上的資料輸入到XXX部件。


 LDXXX欄位

 0        

 0        

 0        

 NOP

 0

 0

 1

 LDA

 0

 1 

 0

 LDB

 0

 1

 1

 LDR

 1

 0

 0

 LDOUT    

 1

 0

 1

 LDMAR

 1

 1

 0

 LDIR

 1

 1

 1

 LDPC


 XXX_B欄位

 0        

 0        

 0        

  NOP       

 0

 0

 1

  ALU_B  

 0

 1

 0

  R_B

 0

 1

 1

  PC_B

 1

 0

 0

  STI

 1

 0

 1

  CLI

 1

 1

 0

  MEM_B 

 1

 1

 1

  IN_B


 C欄位

 

 0        

 0         

 0       

  NOP       

 0

 0

 1

 P<1>

 0

 1

 0

 P<2>

 0

 1

 1

 P<3>

 1

 0

 0

 P<4>

 1

 0

 1

 P<5>

 1

 1

 0

 保留

 1

 1

 1

 保留


3、微程式流程圖

注:以下是全部指令的流程圖,本文只實現簡單的ADD指令和LDI指令,作為例項。


六、Verilog HDL實現原始碼

    主要是通過Verilog 模擬微程式控制器,將各個控制訊號作為變數,在不同的時序 對 控制訊號進行 賦值、判斷,模擬微程式的流程。
module CPU(clk,reset,interrupt,T1,T2,PC,MAR,IR,uMA,A,B,ALU,R0,R1,R2,R3,LDR,LDIR,BUS,FC);
	input clk,reset,interrupt;
	output T1,T2,PC,MAR,IR,uMA,A,B,ALU,R0,R1,R2,R3,LDR,LDIR,BUS,FC;
	reg[7:0] MEM0,MEM1,MEM2,MEM3,MEM4; //記憶體中的普通程式
	reg[7:0] R0,R1,R2,R3,ALU,A,B,PC,BUS,MAR,IR;
	reg[1:0] S;  // 2位ALU控制欄位
	reg[2:0] LDXXX,XXX_B; // 三個控制欄位
	reg[5:0] uMA; // 6位微地址欄位
	//T1時刻直接XXX_B、設定LDXXX控制訊號, T2時刻根據LDXXX訊號 從BUS傳資料
	reg LDA,LDB,LDR,LDPC,LDOUT,LDMAR,LDIR,INC_PC,FC; // 微控制訊號
	reg T1;
	wire T2;

	//產生時序T1 T2;初始記憶體中的機器指令
	always @(posedge clk)
	begin
		if(reset)
			begin
				T1 <= 1'b0;
				//記憶體初始賦值(輸入機器指令)MEM
			    MEM0 <= 8'b10100000; //立即數傳值->R0
				MEM1 <= 8'b10000000;
				MEM2 <= 8'b10100100; //立即數傳值->R1
				MEM3 <= 8'b10000000;
				MEM4 <= 8'b00110001; //R0+R1 ->R0
			end
		else
			//設定時序
			T1 <= ~T1;
	end
	//設定時序
	assign T2=~T1;
	
	//T1 設定微程式碼各欄位
	always @(posedge T1)
	begin
		if(reset)
			uMA <= 6'b000000;
		else
			begin
				case(uMA)
						6'h00:
							begin
								S <= 2'b00;
								XXX_B <= 3'b101;
								LDXXX <= 3'b000;
								INC_PC <= 1'b0;
								uMA <= 6'h01;
							end
						6'h01:
							begin
								S <= 2'b00;
								XXX_B <= 3'b000;
								LDXXX <= 3'b000;
								INC_PC <= 1'b0;
								uMA <= 6'h02;
							end
						6'h02:
							begin
								S <= 2'b00;
								XXX_B <= 3'b011;
								LDXXX <= 3'b101;
								INC_PC <= 1'b1;
								uMA <= 6'h03;
							end
						6'h03:
							begin
								S <= 2'b00;
								XXX_B <= 3'b110;
								LDXXX <= 3'b110;
								INC_PC <= 1'b0;
								uMA <= 6'h04;
							end
						6'h04:
							begin
								S <= 2'b00;
								XXX_B <= 3'b000;
								LDXXX <= 3'b000;
								INC_PC <= 1'b0;
								case({IR[7],IR[6],IR[5],IR[4]})
									4'b0011:
										uMA <= 6'h08; //ADD
									4'b1010:
										uMA <= 6'h16; //LDI
								endcase
							end
						6'h08: //RD->A
							begin
								S <= 2'b00;
								XXX_B <= 3'b010;
								LDXXX <= 3'b001;
								INC_PC <= 1'b0;
								uMA <= 6'h09;
							end
						6'h09://RS->B
							begin
								S <= 2'b00;
								XXX_B <= 3'b010;
								LDXXX <= 3'b010;
								INC_PC <= 1'b0;
								uMA <= 6'h0A;
							end
						6'h0A:  //A+B->RD
							begin
								S <= 2'b01;
								XXX_B <= 3'b001;
								LDXXX <= 3'b011;
								INC_PC <= 1'b0;
								uMA <= 6'h01;
							end	
						6'h16:  //LDI
							begin
								S <= 2'b00;
								XXX_B <= 3'b011;
								LDXXX <= 3'b101;
								INC_PC <= 1'b1;
								uMA <= 6'h17;
							end
						6'h17:  
							begin
								S <= 2'b00;
								XXX_B <= 3'b110;
								LDXXX <= 3'b011;
								INC_PC <= 1'b0;
								uMA <= 6'h01;
							end
				endcase
			end
	end
	
	//設定每欄位的控制訊號
	always @(S or LDXXX or XXX_B)
	begin	
		//ALU運算控制
		case(S)
				2'b00:
					begin
						ALU <= ALU;
					end
				2'b01:
					begin
						{FC,ALU} <= A + B;
					end	
		endcase
		// A欄位控制 LDXX
		case(LDXXX)
				3'b000:
					begin
						{LDA,LDB,LDR,LDOUT,LDMAR,LDIR,LDPC} <= 7'b0000000;
					end
				3'b001:
					begin
						{LDA,LDB,LDR,LDOUT,LDMAR,LDIR,LDPC} <= 7'b1000000;
					end
				3'b010:
					begin
						{LDA,LDB,LDR,LDOUT,LDMAR,LDIR,LDPC} <= 7'b0100000;
					end
				3'b011:
					begin
						{LDA,LDB,LDR,LDOUT,LDMAR,LDIR,LDPC} <= 7'b0010000;
					end
				3'b100:
					begin
						{LDA,LDB,LDR,LDOUT,LDMAR,LDIR,LDPC} <= 7'b0001000;
					end
				3'b101:
					begin
						{LDA,LDB,LDR,LDOUT,LDMAR,LDIR,LDPC} <= 7'b0000100;
					end
				3'b110:
					begin
						{LDA,LDB,LDR,LDOUT,LDMAR,LDIR,LDPC} <= 7'b0000010;
					end
				3'b111:
					begin
						{LDA,LDB,LDR,LDOUT,LDMAR,LDIR,LDPC} <= 7'b0000001;
					end
		endcase
		// B欄位控制 XX_B
		case(XXX_B)
				3'b000:
					begin
						BUS <= BUS;
					end
				3'b001:
					begin
						BUS <= ALU;
					end
				3'b010:
					begin
						case({IR[1],IR[0]})
							2'b00:
								BUS <= R0;
							2'b01:
								BUS <= R1;
							2'b10:
								BUS <= R2;
							2'b11:
								BUS <= R3;
						endcase
					end
				3'b011:
					begin
						BUS <= PC;
					end
				3'b100:
					begin
						STI <= 1'b1;
						CLI <= 1'b0;
					end
				3'b101:
					begin
						STI <= 1'b0;
						CLI <= 1'b1;
					end
				3'b110:
				begin
					case(MAR)
						8'h00:
							BUS <= MEM0;
						8'h01:
							BUS <= MEM1;
						8'h02:
							BUS <= MEM2;
						8'h03:
							BUS <= MEM3;
						8'h04:
							BUS <= MEM4;
					endcase
				end
				3'b111:
					BUS <= IN;
		endcase	
	end
endmodul

七、模擬

記憶體指令:
                                MEM0 <= 8'b10100000; //立即數傳值->R0
				MEM1 <= 8'b10000000;
				MEM2 <= 8'b10100100; //立即數傳值->R1
				MEM3 <= 8'b10000000;
				MEM4 <= 8'b00110001; //R0+R1 ->R0



八、結語

 本文只實現了簡單的功能加法、立即數傳值指令,介紹如何使用Verilog 模擬微程式控制器,進而實現一個簡單CPU的功能。實現微程式流程圖中的所有指令的原始碼,在我的GitHub 之中,歡迎大家參考。