首页 > 学院 > 开发设计 > 正文

微程序控制型简单CPU模型Verilog HDL实现

2019-11-08 19:39:04
字体:
来源:转载
供稿:网友

一、设计目标

 掌握微程序控制器的基本原理 设计可以实现实现基本的指令运算指令、数据传输指令、输入输出指令、转移指令;并且具有中断和原码一位乘法功能 使用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		endendmodul

七、仿真

内存指令:
                                MEM0 <= 8'b10100000; //立即数传值->R0				MEM1 <= 8'b10000000;				MEM2 <= 8'b10100100; //立即数传值->R1				MEM3 <= 8'b10000000;				MEM4 <= 8'b00110001; //R0+R1 ->R0

八、结语

 本文只实现了简单的功能加法、立即数传值指令,介绍如何使用Verilog 模拟微程序控制器,进而实现一个简单CPU的功能。实现微程序流程图中的所有指令的源代码,在我的GitHub 之中,欢迎大家参考。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表