微程序控制型简单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 之中,欢迎大家参考。


  • 12
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值