简单周期CPU的Verliog代码的实现和通过在proteus上进行C51仿真LED流水灯实验
@[toc](目录)
一.简单周期CPU的Verliog代码的实现
1.概述
本文所设计的单周期CPU的指令系统采用类似MIPS的设计风格,包括以下四类指令:
(1) 运算类指令;
(2) 传送类指令;
(3) 存储类指令;
(4) 控制类指令;
其中,所有指令的操作码部分用4位二进制表示,寄存器编号用3位二进制表示。在下述的具体设计表示中,以助记符表示的是汇编指令;以代码表示的则是二进制机器指令。
2.整体框架设计
本文所设计的单周期CPU的整体框架主要包括七部分:程序计数器、指令寄存器、寄存器组、算术逻辑单元、数据存储器、控制单元和顶层模块。具体框架如下:
3.具体实现
程序计数器
代码如下:
module PC(
input clk,
input [15:0] PCin,
input PCWre,
input Reset,
output reg [15:0] PCout
);
initial begin
PCout <= 0;
end
always@(posedge clk) begin
if(Reset == 0) begin
PCout <= 0;
end
else if(PCWre == 0) begin
PCout <= PCout;
end
else begin
PCout <= PCin;
end
end
endmodule
指令存储器
代码如下:
module InsMemory(
input InsMemRW,
input [15:0] address,
output reg [15:0] DataOut
);
reg [7:0] mem [0:127];
initial begin
DataOut = 16'b1111000000000000;
$readmemb("Instructions.txt", mem);
end
always@(*) begin
DataOut[15:8] <= mem[address];
DataOut[7:0] <= mem[address+1];
end
endmodule
寄存器组
代码如下:
module RegFile(
input CLK,
input RST,
input RegWre,
input [2:0] ReadReg1,
input [2:0] ReadReg2,
input [2:0] WriteReg,
input [15:0] WriteData,
output [15:0] ReadData1,
output [15:0] ReadData2
);
reg [15:0] regFile[0:7];
integer i;
assign ReadData1 = regFile[ReadReg1];
assign ReadData2 = regFile[ReadReg2];
always @ (negedge CLK) begin
if (RST == 0) begin
for(i=1;i<8;i=i+1)
regFile[i] <= 0;
end
else if(RegWre == 1 && WriteReg != 0) begin
regFile[WriteReg] <= WriteData;
end
end
endmodule
算数逻辑单元
代码如下:
module ALU(
input [2:0] ALUopcode,
input [15:0] rega,
input [15:0] regb,
output reg [15:0] result,
output zero,
output sign
);
assign zero = (result==0)?1:0;
assign sign = result[15];
always @( ALUopcode or rega or regb ) begin
case (ALUopcode)
3'b000 : result = rega + regb;
3'b001 : result = rega - regb;
3'b010 : result = regb << rega;
3'b011 : result = rega | regb;
3'b100 : result = rega & regb;
3'b101 : result = (rega < regb)?1:0;
3'b110 : begin
if (rega<regb &&(( rega[15] == 0 && regb[15]==0) ||
(rega[15] == 1 && regb[15]==1))) result = 1;
else if (rega[15] == 0 && regb[15]==1) result = 0;
else if ( rega[15] == 1 && regb[15]==0) result = 1;
else result = 0;
end
3'b111 : result = regb;
endcase
end
endmodule
数据存储器
代码如下:
module DataMemory(
input clk,
input [15:0] address,
input RD,
input WR,
input [15:0] DataIn,
output [15:0] DataOut
);
reg [7:0] ram[0:127];
integer i;
initial begin;
for(i=0;i<128;i=i+1)
ram[i]<=0;
end
assign DataOut[7:0] = (RD == 0)? ram[address+1]:8'bz;
assign DataOut[15:8] = (RD == 0)? ram[address]:8'bz;
always@(negedge clk) begin
if(WR == 0) begin
if(address>=0 && address<128) begin
ram[address] <= DataIn[15:8];
ram[address+1] <= DataIn[7:0];
end
end
end
endmodule
控制单元
代码如下:
module ControlUnit(
input [3:0] opcode,
input zero,
input sign,
output reg PCWre,
output reg ALUSrcA,
output reg ALUSrcB,
output reg DBDataSrc,
output reg RegWre,
output reg InsMemRW,
output reg RD,
output reg WR,
output reg RegDst,
output reg ExtSel,
output reg [1:0] PCSrc,
output reg [2:0] ALUOp
);
initial begin
RD = 1;
WR = 1;
RegWre = 0;
PCWre = 0;
InsMemRW = 1;
end
always@ (opcode) begin
case(opcode)
4'b0000:begin // add
PCWre = 1;
ALUSrcA = 0;
ALUSrcB = 0;
DBDataSrc = 0;
RegWre = 1;
InsMemRW = 1;
RD = 1;
WR = 1;
RegDst = 1;
ALUOp = 3'b000;
end
4'b0001:begin //addi
PCWre = 1;
ALUSrcA = 0;
ALUSrcB = 1;
DBDataSrc = 0;
RegWre = 1;
InsMemRW = 1;
RD = 1;
WR = 1;
RegDst = 0;
ExtSel = 1;
ALUOp = 3'b000;
end
4'b0010:begin //sub
PCWre = 1;
ALUSrcA = 0;
ALUSrcB = 0;
DBDataSrc = 0;
RegWre = 1;
InsMemRW = 1;
RD = 1;
WR = 1;
RegDst = 1;
ALUOp = 3'b001;
end
4'b0011:begin // or
PCWre = 1;
ALUSrcA = 0;
ALUSrcB = 0;
DBDataSrc = 0;
RegWre = 1;
InsMemRW = 1;
RD = 1;
WR = 1;
RegDst = 1;
ALUOp = 3'b011;
end
4'b0100:begin // ori
PCWre = 1;
ALUSrcA = 0;
ALUSrcB = 1;
DBDataSrc = 0;
RegWre = 1;
InsMemRW = 1;
RD = 1;
WR = 1;
RegDst = 0;
ExtSel = 0;
ALUOp = 3'b011;
end
4'b0101:begin //and
PCWre = 1;
ALUSrcA = 0;
ALUSrcB = 0;
DBDataSrc = 0;
RegWre = 1;
InsMemRW = 1;
RD = 1;
WR = 1;
RegDst = 1;
ALUOp = 3'b100;
end
4'b0110:begin //sll
PCWre = 1;
ALUSrcA = 1;
ALUSrcB = 0;
DBDataSrc = 0;
RegWre = 1;
InsMemRW = 1;
RD = 1;
WR = 1;
RegDst = 1;
ALUOp = 3'b010;
end
4'b0111:begin //slt
PCWre = 1;
ALUSrcA = 0;
ALUSrcB = 0;
DBDataSrc = 0;
RegWre = 1;
InsMemRW = 1;
RD = 1;
WR = 1;
RegDst = 1;
ALUOp = 3'b110;
end
4'b1000:begin //mov
PCWre = 1;
ALUSrcA = 0;
ALUSrcB = 0;
DBDataSrc = 0;
RegWre = 1;
InsMemRW = 1;
RD = 1;
WR = 1;
RegDst = 1;
ExtSel = 1;
ALUOp = 3'b111;
end
4'b1001:begin //movi
PCWre = 1;
ALUSrcA = 0;
ALUSrcB = 1;
DBDataSrc = 0;
RegWre = 1;
InsMemRW = 1;
RD = 1;
WR = 1;
RegDst = 0;
ExtSel = 1;
ALUOp = 3'b111;
end
4'b1010:begin //sw
PCWre = 1;
ALUSrcA = 0;
ALUSrcB = 1;
RegWre = 0;
InsMemRW = 1;
RD = 1;
WR = 0;
ExtSel =1;
ALUOp = 3'b000;
end
4'b1011:begin //lw
PCWre = 1;
ALUSrcA = 0;
ALUSrcB = 1;
DBDataSrc = 1;
RegWre = 1;
InsMemRW = 1;
RD = 0;
WR = 1;
RegDst = 0;
ExtSel = 1;
ALUOp = 3'b000;
end
4'b1100:begin //beq
PCWre = 1;
ALUSrcA = 0;
ALUSrcB = 0;
RegWre = 0;
InsMemRW = 1;
RD = 1;
WR = 1;
ExtSel = 1;
ALUOp = 3'b001;
end
4'b1101:begin //bgtz
PCWre = 1;
ALUSrcA = 0;
ALUSrcB = 0;
RegWre = 0;
InsMemRW = 1;
RD = 1;
WR = 1;
ExtSel = 1;
ALUOp = 3'b001;
end
4'b1110:begin //j
PCWre = 1;
RegWre = 0;
InsMemRW = 1;
RD = 1;
WR = 1;
ALUOp = 3'b010;
end
4'b1111:begin //halt
PCWre = 0;
RegWre = 0;
InsMemRW = 1;
RD = 1;
WR = 1;
end
default:begin
RD = 1;
WR = 1;
RegWre = 0;
InsMemRW = 0;
end
endcase
end
always@(opcode or zero or sign) begin
if(opcode == 4'b1110) // j
PCSrc = 2'b10;
else if(opcode == 4'b1100) begin
if(zero == 1)
PCSrc = 2'b01;
else
PCSrc = 2'b00;
end
else if(opcode == 4'b1101) begin
if(zero == 0 && sign == 0)
PCSrc = 2'b01;
else
PCSrc = 2'b00;
end
else begin
PCSrc = 2'b00;
end
end
endmodule
顶层模块实现
二.通过在proteus上进行C51仿真LED流水灯实验
1.建立项目
在完成安装后,进行项目的配置,配置时需注意一定要使用AT89C51作为芯片。
2.进行线路连接
1)首先进入原理图绘制界面,进行原理图设计,
先点击上方的原理图设计,在点击P,在界面中搜索AT89C51,找到其芯片,并按照相同的方式,找到LED-YELLOW(流水灯)和RES(电阻)。
- 再将芯片和8个流水灯放入设计界面,
3)再放入8个电阻并将其连线,
4)依次为他们编号并将电阻的值改为300,让流水灯更亮,
5)最后加入电源,并将名称改为VCC,
二.完成程序的编写
1.创建项目
在新建项目时,一定要选择AT89C51芯片,
2.进行程序编写
1)主要程序如下:
//51单片机编程常用的头文件
#include <reg51.h>
#include <intrins.h>
//延迟函数
void delay_ms(int a)
{
int i,j;
for(i=0;i<a;i++)
{
for(j=0;j<1000;j++) _nop_();
}
}
void main(void)
{
while(1)
{
P0=0xfe;
delay_ms(50);
P0=0xfd;
delay_ms(50);
P0=0xfb;
delay_ms(50);
P0=0xf7;
delay_ms(50);
P0=0xef;
delay_ms(50);
P0=0xdf;
delay_ms(50);
P0=0xbf;
delay_ms(50);
P0=0x7f;
delay_ms(50);
}
}
2)点击保存,并在命名的同时加上.c的后缀来表明这是一个c程序,
3)再右键点击Source Group1 并选择将这个文件添加到Source Group1,
4)再选择刚刚保存的文件,然后点击添加,
5)点击上方的options for target按钮,点击窗口里的output,选上 create hex file ,
6)最后进行编译,
三.开始进行仿真
1)双击AT89C51芯片,在program file处查找到刚才创造的hex文件,
2)点击运行,
四.总结
这次对于AT89C51芯片流水灯的仿真实验,让我对开发的大概流程和大致原理有了一定的了解,为接下来的学习打下了基础。