微程式控制型簡單CPU模型Verilog HDL實現
一、設計目標
- 掌握微程式控制器的基本原理
- 設計可以實現實現基本的指令運算指令、資料傳輸指令、輸入輸出指令、轉移指令;並且具有中斷和原碼一位乘法功能
- 使用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 之中,歡迎大家參考。