单字节指令:
操作码 OP 4位 |
目的寄存器 Rd 2位 |
源寄存器 Rs 2位 |
双字节指令:
操作码 OP 4位 |
目的寄存器 Rd 2位 |
源寄存器 Rs 2位 |
立即数字段 8位 |
本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 |
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的信号有效时,将数据从总线输入到寄存器中
运算器 2位 |
向总线输出 3位 |
从总线输入 3位 |
下地址 6位 |
S1 S0 |
XXX_B |
LDXXX |
uMA |
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 |
保留 |
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 之中,欢迎大家参考。