带你从零开始设计一个简单CPU

前言

这篇文章的目的是以最简单的方法讲解CPU的结构,
带你感受关于CPU的智慧和魅力。
大部分图片来自Crash老师的计算机科学速成课。

一 CPU的基本组成

CPU即中央处理器(central processing unit)是计算机系统的运算和控制核心
让我们来看看它的主要组成结构吧。

ALU(算术逻辑单元): 是能够进行算术运算逻辑运算的组合逻辑电路。
寄存器(随机存取存储器):也叫主存,是与CPU直接交换数据的内部存储器。
控制器:把内存上的指令、数据等读入寄存器。
时钟:负责管理CPU的节奏(频率)。

二 CPU的基石

大家应该都知道晶体管是CPU的基石,但你知道么?其实晶体管还有两位“老前辈”,继电器热电子管曾经发挥着它的作用。
1.继电器
让我们把继电器简化一下。
在这里插入图片描述

控制电路上有一个线圈,当我们给它通电时线圈会产生吸力,将机械继电器关上。从而实现了 关/开 这两种状态。这两种状态就可以和二进制联系起来从而表示数据了。(关于为什么能表示数据后面二进制会讲)

但是继电器不断的动就一定会产生磨损,需要经常更换。
于是科学家们发明了不需要动的热电子管。

2.热电子管:它是一个真空管。
我们把它也简化一下
在这里插入图片描述
真空管中含有两个电极,一个电极会发热射出电子,另一个带正电的电极会吸引电子从而形成电流。中间设置了一个控制电路来开关。

热电子管是不需要动了,但是它就像灯泡一样,一直发热肯定会烧坏。
而且非常容易碎,于是晶体管就出场了。

3.晶体管
简化一下它的结构。
在这里插入图片描述半导体指常温下导电性能介于导体与绝缘体之间的材料,通过门电路我们就可以控制它的导电性,而且半导体的能耗低,导电性转换的很,因此一直使用到现在。

下面我们会讲计算机的算数大脑(ALU),关于为什么 关/开 能代表数据,也会在下面的部分讲解。

三 ALU(运算器)

1.二进制

(1)二进制在计算机中

上面的部分我们已经提到了,晶体管可以有开关两种状态,这时我们就可以将开对应1,关对应0 。正好可以利用我们学过的二进制。

电路合上电流,代表‘
电路打开电流,代表‘

(2)为什么是二进制

第一点:只有两种状态,便于区分信号,不容易受外界干扰。

  • 25
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 23
    评论
课程程设计,简易议计算器!实验已经成功目录 1、引言……………………………………………………………………………………………… 2 1.1 计算器意义………………………………………………………………………………2 1.2 电子计算器的特殊键 …………………………………………………………………2 2 、单片机概述.……………………………………………………………………………………2 3 、芯片简介 ………………………………………………………………………………………3 3.1 MSC-51芯片简介…………………………………………………………………………3 4 、相关知识 ………………………………………………………………………………………6 4.1数码管显示…………………………………………………………………………………6 4.2矩阵按键 …………………………………………………………………………………6 5 、计算器硬件电路设 …………………………………………………………………………7 6 、计算器程序设计………………………………………………………………………………7 6.1存储单元分配………………………………………………………………………………7 6.2 主程序设计…………………………………………………………………………………7 6.3 数码管显示数据转换子程序CONV ……………………………………………………7 6.4 数码管动态显示子程序………………………………………………………………… 7 7 、系统硬件设计……………………………………………………………………………………7 7.1 系统总框图如下……………………………………………………………………………8 7.2 计算器硬件线路图…………………………………………………………………………8 7.3 系统工作原理 ………………………………………………………………………………9 8、汇编语言程序源代码……………………………………………………………………………10 9 、结语………………………………………………………………………………………………17 10、设计实物图……………………………………………………………………………18 摘要 近年来随着科技的飞速发展,单片机的应用正在不断深入,同时动传统控制检测技术日益更新。在实时检测和自动控制的单片机应用系统中,单片机往往作为一个核心部件来使用,仅单片机方面知识是不够的,还应根据具体硬件结构软硬件结合,加以完善。 本任务是个简易的两位数的四则运算,程序都是根据教材内和网络中的程序参考编写而成,在功能上还并不完善,限制也较多。本任务重在设计构思与团队合作,使得我们用专业知识、专业技能分析和解决问题全面系统的锻炼。 关键词: 单片机 计算器 范围 加减乘除 1 引言 1.1 计算器的历史 说起计算器,值得我们骄傲的是,最早的计算工具诞生在中国。中国古代最早采用的一种计算工具叫筹策,又被叫做算筹。这种算筹多用竹子制成,也有用木头,兽骨充当材料的。约二百七十枚一束,放在布袋里可随身携。直到今天仍在使用的珠算盘,是中国古代计算工具领域中的另一项发明,明代时的珠算盘已经与现代的珠算盘几乎相同。17世纪初,西方国家的计算工具有了较大的发展,英国数学家纳皮尔发明的"纳皮尔算筹",英国牧师奥却德发明了圆柱型对数计算尺,这种计算尺不仅能做加减乘除、乘方、开方运算,甚至可以计算三角函数,指数函数和对数函数,这些计算工具不仅动了计算器的发展,也为现代计算器发展奠定了良好的基础,成为现代社会应用广泛的计算工具。 1.2 电子计算器的特殊键 在使用电子计算器进行四则运算的时候,一般要用到数字键,四则运算键和清除数据键。除了这些按键,还有一些特殊键,可以使计算更加简便迅速。 2 单片机概述 单片机微型计算机是微型计算机的一个重要分支,也是颇具生命力的机种。单片机微型计算机简称单片机,特别适用于控制领域,故又称为微控制器。 通常,单片机由单块集成电路芯片构成,内部包含有计算机的基本功能部件:中央处理器、存储器和I/O接口电路等。因此,单片机只需要和适当的软件及外部设备相结合,便可成为一个单片机控制系统。 单片机经过1、2、3、3代的发展,目前单片机正朝着高性能和多品种方向发展,它们的CPU功能在增强,内部资源在增多,引角的多功能化,以及低电压底功耗。 3 芯片简介 3.1 MSC-51芯片简介 MCS-51单片机内部结构 AT89C51是与MCS-51系列单片机兼容的典型产品,我们以这一代表性的机型进行系统的讲解。 AT89C51单片机包含中央处理器、程序存储器(ROM)、数据存储器(RAM)、定时/计数器、并行接口、串行接口和中断系统等几大单元及数据总线、地址总线和控制总线等三大总线,现在我们分别加以说明: •中央处理器: 中央处理器(CPU)是整个单片机的核心部件,是8位数据宽度的处理器,能处理8位二进制数据或代码,CPU负责控制、指挥和调度整个单元系统协调的工作,完成运算和控制输入输出功能等操作。 •数据存储器(RAM) AT89C51内部有128个8位用户数据存储单元和128个专用寄存器单元,它们是统一编址的,专用寄存器只能用于存放控制指令数据,用户只能访问,而不能用于存放用户数据,所以,用户能使用的RAM只有128个,可存放读写的数据,运算的中间结果或用户定义的字型表。 图1 •程序存储器(ROM): AT89C51共有4096个8位掩膜ROM,用于存放用户程序,原始数据或表格。 •定时/计数器(ROM): AT89C51有两个16位的可编程定时/计数器,以实现定时或计数产生中断用于控制程序转向。 •并行输入输出(I/O)口: AT89C51共有4组8位I/O口(P0、 P1、P2或P3),用于对外部数据的传输。 •全双工串行口: AT89C51内置一个全双工串行通信口,用于与其它设备间的串行数据传送,该串行口既可以用作异步通信收发器,也可以当同步移位器使用。 •中断系统: AT89C51具备较完善的中断功能,有两个外中断、两个定时/计数器中断和一个串行中断,可满足不同的控制要求,并具有2级的优先级别选择。 •时钟电路: AT89C51内置最高频率达12MHz的时钟电路,用于产生整个单片机运行的脉冲时序,但AT89C51单片机需外置振荡电容。 单片机的结构有两种类型,一种是程序存储器和数据存储器分开的形式,即哈佛(Harvard)结构,另一种是采用通用计算机广泛使用的程序存储器与数据存储器合二为一的结构,即普林斯顿(Princeton)结构。INTEL的MCS-51系列单片机采用的是哈佛结构的形式,而后续产品16位的MCS-96系列单片机则采用普林斯顿结构。 下图是MCS-51系列单片机的内部结构示意图2。 图2 MCS-51的引脚说明: MCS-51系列单片机中的8031、AT89C51及8751均采用40Pin封装的双列直接DIP结构,右图是它们的引脚配置,40个引脚中,正电源和地线两根,外置石英振荡器的时钟线两根,4组8位共32个I/O口,中断口线与P3口线复用。现在我们对这些引脚的功能加以说明: MCS-51的引脚说明: MCS-51系列单片机中的8031、AT89C51及8751均采用40Pin封装的双列直接DIP结构,右图是它们的引脚配置,40个引脚中,正电源和地线两根,外置石英振荡器的时钟线两根,4组8位共32个I/O口,中断口线与P3口线复用。现在我们对这些引脚的功能加以说明:如图3 图3 Pin9:RESET/Vpd复位信号复用脚,当AT89C51通电,时钟电路开始工作,在RESET引脚上出现24个时钟周期以上的高电平,系统即初始复位。初始化后,程序计数器PC指向0000H,P0-P3输出口全部为高电平,堆栈指针写入07H,其它专用寄存器被清“0”。RESET由高电平下降为低电平后,系统即从0000H地址开始执行程序。然而,初始复位不改变RAM(包括工作寄存器R0-R7)的状态,AT89C51的初始态。 AT89C51的复位方式可以是自动复位,也可以是手动复位,见下图4。此外,RESET/Vpd还是一复用脚,Vcc掉电其间,此脚可接上备用电源,以保证单片机内部RAM的数据不丢失。 图4 •Pin30:ALE/ 当访问外部程序器时,ALE(地址锁存)的输出用于锁存地址的低位字节。而访问内部程序存储器时,ALE端将有一个1/6时钟频率的正脉冲信号,这个信号可以用于识别单片机是否工作,也可以当作一个时钟向外输出。更有一个特点,当访问外部程序存储器,ALE会跳过一个脉冲。 如果单片机是EPROM,在编程其间, 将用于输入编程脉冲。 •Pin29: 当访问外部程序存储器时,此脚输出负脉冲选通信号,PC的16位地址数据将出现在P0和P2口上,外部程序存储器则把指令数据放到P0口上,由CPU读入并执行。 •Pin31:EA/Vpp程序存储器的内外部选通线,8051和8751单片机,内置有4kB的程序存储器,当EA为高电平并且程序地址小于4kB时,读取内部程序存储器指令数据,而超过4kB地址则读取外部指令数据。如EA为低电平,则不管地址大小,一律读取外部程序存储器指令。显然,对内部无程序存储器的8031,EA端必须接地。 在编程时,EA/Vpp脚还需加上21V的编程电压。 4 相关知识 4.1数码管显示 在本任务中用4位数码管显示当前数值的千,百,十,个,由于数码管个数多,如采用静态显示方式,则占用单片机的I/O口线太多,如果用定时器/计数器的串行移位寄存器工作方式及外接串入并出移位寄存器74LS164的方式,则电路复杂。所以,在数码管个数较多时,常采用动态显示方式。 如图1-1所示为单片机应用系统中的一种数码管动态显示电路图,4位数码管的相同段并联在一起,由一个8位I/O(P1口)输出字形码控制显示某一字形,每个数码管的公共端由另外一个I/O口(P0口)输出的字位码控制,即数码管显示的字形是由单片机I/O口输出的字形码确定,而哪个数码管点亮是由单片机I/O口输出的字位码确定的。4个数码管分时轮流循环点亮,在同一时刻只有1个数码管点亮,但由于数码管具有余辉特性及人眼具有视觉暂留特性,所以适当地选取循环扫描频率,看上去所有数码管是同时点亮的,察觉不出闪烁现象。动态显示方式所接数码管不能太多,否则会因每个数码管所分配的实际导通时间太少,使得数码管的亮度不足。在本任务中,为了简便,字形码和字位码都没由加驱动电路,在实际应用中应加驱动电路。数码管有共阴极和共阳极两种,对于共阳数码管,字形驱动输出0有效,字位驱动输出1有效;而对于共阴数码管则相反,即:字形驱动输出1有效,字位驱动输出0有效。 4.2矩阵按键 键盘是单片机系统中最常用的人机对话输入设备,用户通过键盘向单片机输入数据或指令。键盘控制程序需完成的任务有:监测是否有键按下,有键按下时,在无硬件去抖的动电路时,应用软件延时方法消除按键抖动影响;当有多个键同时按下时,只处理一个按键,不管一次按键持续多长时间,仅执行一次按键功能程序。 矩阵按键扫描程序是一种节省IO口的方法,按键数目越多节省IO口就越可观,思路:先判断某一列(行)是否有按键按下,再判断该行(列)是那一只键按下。但是,在程序的写法上,采用了最简单的方法,使得程序效率最高。本程序中,如果检测到某键按下了,就不再检测其它的按键,这完全能满足绝大多数需要,又能节省大量的CPU时间。 本键盘扫描程序的优点在于:不用专门的按键延时程序,提高了CPU效率,也不用中断来扫描键盘,节省了硬件资源。另外,本键盘扫描程序,每次扫描占用CPU时最短,不论有键按下或者无键按下都可以在很短的时间完成一次扫描。 本键盘扫描子程序名叫key,每次要扫描时用lcall key调用即可。 5 计算器硬件电路设计 AT89C51单片机的P2口作键盘口,其中P2.4-P2.7为键盘扫描输出线,P2.0-P2.3为键盘扫描输入线。键盘由4*4共16个按键组成,10个数字键(由0-9组成)5个运算符号(加减乘除等于)组成,1个清除键(作用相当于整体复位)。4个数码管用于显示当前数值的千,百,十,个,采用动态显示方式,P1口接4个数码管的七段,P0口分别接4个数码管的公共端,P1口输出数码管的字形码,P0口输出数码管的字位码。 6 计算器程序设计 6.1存储单元分配 30H单元:数值个位显示单元;31H单元:数值十位显示单元;32H单元:数值百位显示单元;33H单元:数值千位显示单元;23H单元:第一操作数存储单元;24H单元:第二操作数存储单元;25H单元:键值暂存单元;27H单元:清除键状态;34H-37H单元:结果数据转换暂存单元;38H-39H单元:结果高低8位暂存单元;R5单元:操作数计数单元;R4单元:操作数数值位数计数单元;R3单元:运算符号存储单元。 6.2 主程序设计 主程序进行程序中用到的一些存储单元的初始化,数值显示和4*4键盘扫描。首先,进行存储单元初始化,给数码管显示单元30H-33H赋予“0000”字形数据,将数值计数单元,存储单元,23H-25H,34H-37H,38H,39H,3AH,3BH,3CH,赋予初值零。之后,调用键盘扫描子程序,和数码管显示数据转换程序,数码管动态显示子程序。主程序不断进行键盘扫描,数码管显示数据转换子程序和动态显示子程序。 6.3 数码管显示数据转换子程序CONV 由于数值单元存放的是二进制数,而用户熟悉的是十进制数,所以应将数值单元中的二进制转换为十进制数,即BCD码。要通过数码管显示出当前数值,还必须将BCD码进一步转换为七段码,转换的最终结果数据存放于显示缓冲区30H-33H单元中,其中30H单元存放数值的个位七段码,31H单元存放数值的十位七段码,32H单元存放数值的百位七段码,33H单元存放数值的千位七段码。 6.4 数码管动态显示子程序 本任务由P1口输出字形码,P0口输出字位码。先将存放于30H单元的数值个位七段码由P1口输出,同时P0口输出使数值个位显示数码管点亮的字位码。由于采用的是共阳数码管,所以只有该位数码管对应的P0.0为1,其他位P0.1-P0.3位0,点亮延时10MS。然后P1口输出数值十位七段码,P0.1位1,数值十位数码管点亮,延时10MS。接着P1口输出数值百位七段码,P0.2为1,数值百位数码管点亮,延时10MS。最后P1口输出数值千位七段码,P0.3为1,数值千位数码管点亮,延时10MS。 7 系统硬件设计 选用设备AT89C51单片机一片,选用设备:AT89C51单片机一片,17个键盘,4位共阳极的七段数码管一个,连线若干,电容3个,电阻5个,晶振1个。 7.1 系统总框图如下:
`define NOP 5'b00000 `define HALT 5'b00001 `define LOAD 5'b00010 `define STORE 5'b00011 `define LDIH 5'b10000 `define ADD 5'b01000 `define ADDI 5'b01001 `define ADDC 5'b10001 `define SUB 5'b01011 `define SUBI 5'b10011 `define SUBC 5'b10111 `define CMP 5'b01100 // control `define JUMP 5'b11000 `define JMPR 5'b11001 `define BZ 5'b11010 `define BNZ 5'b11011 `define BN 5'b11100 `define BNN 5'b11101 `define BC 5'b11110 `define BNC 5'b11111 // logic / shift `define AND 5'b01101 `define OR 5'b01111 `define XOR 5'b01110 `define SLL 5'b00100 `define SRL 5'b00110 `define SLA 5'b00101 `define SRA 5'b00111 // general register `define gr0 3'b000 `define gr1 3'b001 `define gr2 3'b010 `define gr3 3'b011 `define gr4 3'b100 `define gr5 3'b101 `define gr6 3'b110 `define gr7 3'b111 // FSM `define idle 1'b0 `define exec 1'b1 /******* the whole module CPU is made of Instuction_Mem module, PCPU module and Data_Mem module ********/ module CPU( input wire clk, clock, enable, reset, start, input wire[3:0] select_y, output [7:0] select_segment, output [3:0] select_bit ); wire[15:0] d_datain; wire[15:0] i_datain; wire[7:0] d_addr; wire[7:0] i_addr; wire[15:0] d_dataout; wire d_we; wire[15:0] y; reg [20:0] count = 21'b0; Instruction_Mem instruction(clock,reset,i_addr,i_datain); PCPU pcpu(clock, enable, reset, start, d_datain, i_datain, select_y, i_addr, d_addr, d_dataout, d_we, y); Data_memory data(clock, reset, d_addr, d_dataout, d_we, d_datain); Board_eval eval(clk, y, select_segment, select_bit); endmodule /************************ Instruction memeory module *****************************/ module Instruction_Mem ( input wire clock, reset, input wire[7:0] i_addr, output [15:0] i_datain ); reg[15:0] i_data[255:0]; // 8 bits pc address to get instructions reg[15:0] temp; always@(negedge clock) begin if(!reset) begin i_data[0] <= {`LOAD, `gr1, 1'b0, `gr0, 4'b0000}; i_data[1] <= {`LOAD, `gr2, 1'b0, `gr0, 4'b0001}; i_data[2] <= {`ADD, `gr3, 1'b0, `gr1, 1'b0, `gr2}; i_data[3] <= {`SUB, `gr3, 1'b0, `gr1, 1'b0, `gr2}; i_data[4] <= {`CMP, `gr3, 1'b0, `gr2, 1'b0, `gr1}; i_data[5] <= {`ADDC, `gr3, 1'b0, `gr1, 1'b0, `gr2}; i_data[6] <= {`SUBC, `gr3, 1'b0, `gr1, 1'b0, `gr2}; i_data[7] <= {`SLL, `gr2, 1'b0, `gr3, 1'b0, 3'b001}; i_data[8] <= {`SRL, `gr3, 1'b0, `gr1, 1'b0, 3'b001}; i_data[9] <= {`SLA, `gr4, 1'b0, `gr1, 1'b0, 3'b001}; i_data[10] <= {`SRA, `gr5, 1'b0, `gr1, 1'b0, 3'b001}; i_data[11] <= {`STORE, `gr3, 1'b0, `gr0, 4'b0010}; i_data[12] <= {`HALT, 11'b000_0000_0000}; end else begin temp = i_data[i_addr[7:0]]; end end assign i_datain = temp; endmodule /**************************** PCPU module ***************************/ module PCPU( input wire clock, enable, reset, start, input wire [15:0] d_datain, // output from Data_Mem module input wire [15:0] i_datain, // output from Instruction_Mem module input wire [3:0] select_y, // for the board evaluation output [7:0] i_addr, output [7:0] d_addr, output [15:0] d_dataout, output d_we, output [15:0] y ); reg [15:0] gr [7:0]; reg nf, zf, cf; reg state, next_state; reg dw; reg [7:0] pc; reg[15:0] y_forboard; reg [15:0] id_ir; reg [15:0] wb_ir; reg [15:0] ex_ir; reg [15:0] mem_ir; reg [15:0] smdr = 0; reg [15:0] smdr1 = 0; reg signed [15:0] reg_C1; //有符号 reg signed [15:0] reg_A; reg signed [15:0] reg_B; reg signed [15:0] reg_C; reg signed [15:0] ALUo; //************* CPU control *************// always @(posedge clock) begin if (!reset) state <= `idle; else state <= next_state; end always @(*) begin case (state) `idle : if ((enable == 1'b1) && (start == 1'b1)) next_state <= `exec; else next_state <= `idle; `exec : if ((enable == 1'b0) || (wb_ir[15:11] == `HALT)) next_state <= `idle; else next_state <= `exec; endcase end assign i_addr = pc; // 准备下一条指令的地址 //************* IF *************// always @(posedge clock or negedge reset) begin if (!reset) begin id_ir <= 16'b0; pc <= 8'b0; end else if (state ==`exec) // Stall happens in IF stage, always compare id_ir with i_datain to decide pc and id_ir begin // 当即将被执行的指令要用到之前load写入的值时, stall two stages , id and ex. /* 指令中后第二、三个操作数均为寄存器时,需要判断LOAD的第一个操作数是否与这些指令的后两个寄存器有冲突 为一部分算数运算指令和逻辑运算指令 */ if((i_datain[15:11] == `ADD ||i_datain[15:11] == `ADDC ||i_datain[15:11] == `SUB ||i_datain[15:11] == `SUBC ||i_datain[15:11] == `CMP ||i_datain[15:11] == `AND ||i_datain[15:11] == `OR ||i_datain[15:11] == `XOR) &&( (id_ir[15:11] == `LOAD && (id_ir[10:8] == i_datain[6:4] || id_ir[10:8] == i_datain[2:0])) ||(ex_ir[15:11] == `LOAD && (ex_ir[10:8] == i_datain[6:4] || ex_ir[10:8] == i_datain[2:0])) ) ) // end if begin id_ir <= 16'bx; pc <= pc; // hold pc end /* 指令中第二个操作数为寄存器变量并参与运算时,需要判断LOAD的第一个操作数是否与这些指令的第二个操作数的寄存器有冲突 为移位指令和STORE指令 */ else if (( i_datain[15:11] == `SLL ||i_datain[15:11] == `SRL ||i_datain[15:11] == `SLA ||i_datain[15:11] == `SRA ||i_datain[15:11] == `STORE) &&((id_ir[15:11] == `LOAD &&(id_ir[10:8] == i_datain[6:4])) ||(ex_ir[15:11] == `LOAD &&(ex_ir[10:8] == i_datain[6:4])) ) ) begin id_ir <= 16'bx; pc <= pc; // hold pc end /* 跳转指令系列,id和ex阶段都需要stall,mem阶段跳转 */ else if(id_ir[15:14] == 2'b11 || ex_ir[15:14] == 2'b11) begin id_ir <= 16'bx; pc <= pc; // hold pc end /* mem阶段跳转 */ else begin // BZ & BNZ if(((mem_ir[15:11] == `BZ) && (zf == 1'b1)) || ((mem_ir[15:11] == `BNZ) && (zf == 1'b0))) begin id_ir <= 16'bx; pc <= reg_C[7:0]; end // BN & BNN else if(((mem_ir[15:11] == `BN) && (nf == 1'b1)) || ((mem_ir[15:11] == `BNN) && (nf == 1'b0))) begin id_ir <= 16'bx; pc <= reg_C[7:0]; end // BC & BNC else if(((mem_ir[15:11] == `BC) && (cf == 1'b1)) || ((mem_ir[15:11] == `BNC) && (cf == 1'b0))) begin id_ir <= 16'bx; pc <= reg_C[7:0]; end // JUMP else if((mem_ir[15:11] == `JUMP) || (mem_ir[15:11] == `JMPR)) begin id_ir <= 16'bx; pc <= reg_C[7:0]; end // 非跳转指令且没有检测到冲突 else begin id_ir <= i_datain; pc <= pc + 1; end end // end else end // else reset end // end always //************* ID *************// always @(posedge clock or negedge reset) begin if (!reset) begin ex_ir <= 16'b0; reg_A <= 16'b0; reg_B <= 16'b0; smdr <= 16'b0; end else if (state == `exec) //Data forwarding happens in ID stage, always check id_ir to decide reg_A/B begin ex_ir <= id_ir; // ********************reg_A 赋值******************* // /* 其他无冲突的情况 */ // reg_A <= r1: 要用到 r1 参与运算的指令,即除 "JUMP" 外的控制指令和一些运算指令,将寄存器r1中的值赋给reg_A if ((id_ir[15:14] == 2'b11 && id_ir[15:11] != `JUMP) || (id_ir[15:11] == `LDIH) || (id_ir[15:11] == `ADDI) || (id_ir[15:11] == `SUBI)) reg_A <= gr[id_ir[10:8]]; else if (id_ir[15:11] == `LOAD) reg_A <= gr[id_ir[6:4]]; // case for data forwarding, 当前指令第2个操作数用到之前指令第1个操作数的结果 else if(id_ir[6:4] == ex_ir[10:8]) reg_A <= ALUo; else if(id_ir[6:4] == wb_ir[10:8]) reg_A <= reg_C1; else if(id_ir[6:4] == mem_ir[10:8]) reg_A <= reg_C; //reg_A <= r2: 如果运算中不用到 r1,要用到 r2, 则将 gr[r2] else reg_A <= gr[id_ir[6:4]]; //************************* reg_B赋值************************// if (id_ir[15:11] == `STORE) begin reg_B <= {12'b0000_0000_0000, id_ir[3:0]}; //value3 smdr <= gr[id_ir[10:8]]; // r1 end // case for data forwarding, 当前指令第3个操作数用到之前指令第1个操作数的结果 else if(id_ir[2:0] == ex_ir[10:8]) reg_B <= ALUo; else if(id_ir[2:0] == wb_ir[10:8]) reg_B <= reg_C1; else if(id_ir[2:0] == mem_ir[10:8]) reg_B <= reg_C; /* 其他无冲突的情况 */ else if ((id_ir[15:11] == `ADD) || (id_ir[15:11] == `ADDC) || (id_ir[15:11] == `SUB) || (id_ir[15:11] == `SUBC) || (id_ir[15:11] == `CMP) || (id_ir[15:11] == `AND) || (id_ir[15:11] == `OR) || (id_ir[15:11] == `XOR)) reg_B <= gr[id_ir[2:0]]; end end //************* ALUo *************// always @ (*) begin // {val2, val3} if (ex_ir[15:11] == `JUMP) ALUo <= {8'b0, ex_ir[7:0]}; // 跳转指令 r1 + {val2, val3} else if (ex_ir[15:14] == 2'b11) ALUo <= reg_A + {8'b0, ex_ir[7:0]}; //算数运算,逻辑运算,计算结果到ALUo, 并计算cf标志位 else begin case(ex_ir[15:11]) `LOAD: ALUo <= reg_A + {12'b0000_0000_0000, ex_ir[3:0]}; `STORE: ALUo <= reg_A + reg_B; `LDIH: {cf, ALUo} <= reg_A + { ex_ir[7:0], 8'b0 }; `ADD: {cf, ALUo} <= reg_A + reg_B; `ADDI:{cf, ALUo} <= reg_A + { 8'b0, ex_ir[7:0] }; `ADDC: {cf, ALUo} <= reg_A + reg_B + cf; `SUB: {cf, ALUo} <= {{1'b0, reg_A} - reg_B}; `SUBI: {cf, ALUo} <= {1'b0, reg_A }- { 8'b0, ex_ir[7:0] }; `SUBC:{cf, ALUo} <= {{1'b0, reg_A} - reg_B - cf}; `CMP: {cf, ALUo} <= {{1'b0, reg_A} - reg_B}; `AND: {cf, ALUo} <= {1'b0, reg_A & reg_B}; `OR: {cf, ALUo} <= {1'b0, reg_A | reg_B}; `XOR: {cf, ALUo} <= {1'b0, reg_A ^ reg_B}; `SLL: {cf, ALUo} <= {reg_A[4'b1111 - ex_ir[3:0]], reg_A << ex_ir[3:0]}; `SRL: {cf, ALUo} <= {reg_A[ex_ir[3:0] - 4'b0001], reg_A >> ex_ir[3:0]}; `SLA: {cf, ALUo} <= {reg_A[ex_ir[3:0] - 4'b0001], reg_A <<< ex_ir[3:0]}; `SRA: {cf, ALUo} <= {reg_A[4'b1111 - ex_ir[3:0]], reg_A >>> ex_ir[3:0]}; default: begin end endcase end end //************* EX *************// always @(posedge clock or negedge reset) begin if (!reset) begin mem_ir <= 16'b0; reg_C <= 16'b0; dw <= 0; nf <= 0; zf <= 0; smdr1 <= 16'b0; end else if (state == `exec) begin mem_ir <= ex_ir; reg_C <= ALUo; if (ex_ir[15:11] == `STORE) begin dw <= 1'b1; smdr1 <= smdr; end // 设置标志位zf, nf, 算数和逻辑运算 else if(ex_ir[15:14] != 2'b11 && ex_ir[15:11] != `LOAD) begin zf <= (ALUo == 0)? 1:0; nf <= (ALUo[15] == 1'b1)? 1:0; dw <= 1'b0; end else dw <= 1'b0; end end // PCPU module 的输出 assign d_dataout = smdr1; assign d_we = dw; assign d_addr = reg_C[7:0]; //************* MEM *************// always @(posedge clock or negedge reset) begin if (!reset) begin wb_ir <= 16'b0; reg_C1 <= 16'b0; end else if (state == `exec) begin wb_ir <= mem_ir; if (mem_ir[15:11] == `LOAD) reg_C1 <= d_datain; else if(mem_ir[15:14] != 2'b11) reg_C1 <= reg_C; end end //************* WB *************// always @(posedge clock or negedge reset) begin if (!reset) begin gr[0] <= 16'b0; gr[1] <= 16'b0; gr[2] <= 16'b0; gr[3] <= 16'b0; gr[4] <= 16'b0; gr[5] <= 16'b0; gr[6] <= 16'b0; gr[7] <= 16'b0; end else if (state == `exec) begin // 回写到 r1 if ((wb_ir[15:14] != 2'b11) &&(wb_ir[15:11] != `STORE) &&(wb_ir[15:11] != `CMP) ) gr[wb_ir[10:8]] <= reg_C1; end end // 板极验证 assign y = y_forboard; // 板极验证需要的输出 always @(select_y) begin case(select_y) 4'b0000: y_forboard <= {8'B0,pc}; 4'b0001: y_forboard <= id_ir; 4'b0010: y_forboard <= reg_A; 4'b0011: y_forboard <= reg_B; 4'b0100: y_forboard <= smdr; 4'b0101: y_forboard <= ALUo; 4'b0110: y_forboard <= {15'b0, cf}; 4'b0111: y_forboard <= {15'b0, nf}; 4'b1000: y_forboard <= reg_C; 4'b1001: y_forboard <= reg_C1; 4'b1010: y_forboard <= gr[0]; 4'b1011: y_forboard <= gr[1]; 4'b1100: y_forboard <= gr[2]; 4'b1101: y_forboard<= gr[3]; 4'b1110: y_forboard <= gr[4]; 4'b1111: y_forboard <= gr[5]; endcase end endmodule /**************************** Data memory module ******************************/ module Data_memory ( input wire clock, reset, input wire [7:0] d_addr, input wire [15:0] d_dataout, input wire d_we, output [15:0] d_datain ); reg[15:0] temp; reg[15:0] d_data[255:0]; always@(negedge clock) begin if(!reset) begin d_data[0] <= 16'hFc00; d_data[1] <= 16'h00AB; end else if(d_we) begin d_data[d_addr] <= d_dataout; end else begin temp = d_data[d_addr]; end end assign d_datain = temp; endmodule /**************************** Board evaluation module ******************************/ module Board_eval ( input wire clock, input wire [15:0] y, output reg [7:0] select_segment, output reg [3:0] select_bit ); parameter SEG_NUM0 = 8'b00000011, SEG_NUM1 = 8'b10011111, SEG_NUM2 = 8'b00100101, SEG_NUM3 = 8'b00001101, SEG_NUM4 = 8'b10011001, SEG_NUM5 = 8'b01001001, SEG_NUM6 = 8'b01000001, SEG_NUM7 = 8'b00011111, SEG_NUM8 = 8'b00000001, SEG_NUM9 = 8'b00001001, SEG_A = 8'b00010001, SEG_B = 8'b11000001, SEG_C = 8'b01100011, SEG_D = 8'b10000101, SEG_E = 8'b01100001, SEG_F = 8'b01110001; // 位选 parameter BIT_3 = 4'b0111, BIT_2 = 4'b1011, BIT_1 = 4'b1101, BIT_0 = 4'b1110; reg [20:0] count = 0; always @ (posedge clock) begin count <= count + 1'b1; end always @ (posedge clock) begin case(count[19:18]) 2'b00: begin select_bit <= BIT_3; case(y[15:12]) 4'b0000: select_segment <= SEG_NUM0; 4'b0001: select_segment <= SEG_NUM1; 4'b0010: select_segment <= SEG_NUM2; 4'b0011: select_segment <= SEG_NUM3; 4'b0100: select_segment <= SEG_NUM4; 4'b0101: select_segment <= SEG_NUM5; 4'b0110: select_segment <= SEG_NUM6; 4'b0111: select_segment <= SEG_NUM7; 4'b1000: select_segment <= SEG_NUM8; 4'b1001: select_segment <= SEG_NUM9; 4'b1010: select_segment <= SEG_A; 4'b1011: select_segment <= SEG_B; 4'b1100: select_segment <= SEG_C; 4'b1101: select_segment <= SEG_D; 4'b1110: select_segment <= SEG_E; 4'b1111: select_segment <= SEG_F; endcase end 2'b01: begin select_bit <= BIT_2; case(y[11:8]) 4'b0000: select_segment <= SEG_NUM0; 4'b0001: select_segment <= SEG_NUM1; 4'b0010: select_segment <= SEG_NUM2; 4'b0011: select_segment <= SEG_NUM3; 4'b0100: select_segment <= SEG_NUM4; 4'b0101: select_segment <= SEG_NUM5; 4'b0110: select_segment <= SEG_NUM6; 4'b0111: select_segment <= SEG_NUM7; 4'b1000: select_segment <= SEG_NUM8; 4'b1001: select_segment <= SEG_NUM9; 4'b1010: select_segment <= SEG_A; 4'b1011: select_segment <= SEG_B; 4'b1100: select_segment <= SEG_C; 4'b1101: select_segment <= SEG_D; 4'b1110: select_segment <= SEG_E; 4'b1111: select_segment <= SEG_F; endcase end 2'b10: begin select_bit <= BIT_1; case(y[7:4]) 4'b0000: select_segment <= SEG_NUM0; 4'b0001: select_segment <= SEG_NUM1; 4'b0010: select_segment <= SEG_NUM2; 4'b0011: select_segment <= SEG_NUM3; 4'b0100: select_segment <= SEG_NUM4; 4'b0101: select_segment <= SEG_NUM5; 4'b0110: select_segment <= SEG_NUM6; 4'b0111: select_segment <= SEG_NUM7; 4'b1000: select_segment <= SEG_NUM8; 4'b1001: select_segment <= SEG_NUM9; 4'b1010: select_segment <= SEG_A; 4'b1011: select_segment <= SEG_B; 4'b1100: select_segment <= SEG_C; 4'b1101: select_segment <= SEG_D; 4'b1110: select_segment <= SEG_E; 4'b1111: select_segment <= SEG_F; endcase end 2'b11: begin select_bit <= BIT_0; case(y[3:0]) 4'b0000: select_segment <= SEG_NUM0; 4'b0001: select_segment <= SEG_NUM1; 4'b0010: select_segment <= SEG_NUM2; 4'b0011: select_segment <= SEG_NUM3; 4'b0100: select_segment <= SEG_NUM4; 4'b0101: select_segment <= SEG_NUM5; 4'b0110: select_segment <= SEG_NUM6; 4'b0111: select_segment <= SEG_NUM7; 4'b1000: select_segment <= SEG_NUM8; 4'b1001: select_segment <= SEG_NUM9; 4'b1010: select_segment <= SEG_A; 4'b1011: select_segment <= SEG_B; 4'b1100: select_segment <= SEG_C; 4'b1101: select_segment <= SEG_D; 4'b1110: select_segment <= SEG_E; 4'b1111: select_segment <= SEG_F; endcase end endcase end endmodule 2).test `timescale 1ns / 1ps //////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: // // Create Date: 21:22:32 12/29/2014 // Design Name: CPU // Module Name: C:/Users/liang/Desktop/embed/CPU/CPU/CPUTest.v // Project Name: CPU // Target Device: // Tool versions: // Description: // // Verilog Test Fixture created by ISE for module: CPU // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // //////////////////////////////////////////////////////////////////////////////// module CPU_test; // Inputs reg clock; reg enable; reg reset; reg [3:0] select_y; reg start; // Outputs wire [15:0] y; // Instantiate the Unit Under Test (UUT) CPU cpu ( .clock(clock), .enable(enable), .reset(reset), .start(start), .select_y(select_y) ); initial begin // Initialize Inputs clock = 0; enable = 0; reset = 0; select_y = 0; start = 0; // Wait 100 ns for global reset to finish #100; forever begin #5 clock <= ~clock; end // Add stimulus here end initial begin // Wait 100 ns for global reset to finish #100; $display("pc: id_ir : ex_ir :reg_A: reg_B: reg_C: cf: nf: zf: regC1: gr1: gr2: gr3: gr4: gr5:"); $monitor("%h: %b: %b: %h: %h: %h: %h: %h: %h: %h: %h: %h: %h: %h: %h", cpu.pcpu.pc, cpu.pcpu.id_ir, cpu.pcpu.ex_ir, cpu.pcpu.reg_A, cpu.pcpu.reg_B, cpu.pcpu.reg_C, cpu.pcpu.cf, cpu.pcpu.nf, cpu.pcpu.zf, cpu.pcpu.reg_C1, cpu.pcpu.gr[1], cpu.pcpu.gr[2], cpu.pcpu.gr[3], cpu.pcpu.gr[4], cpu.pcpu.gr[5]); enable <= 1; start <= 0; select_y <= 0; #10 reset <= 0; #10 reset <= 1; #10 enable <= 1; #10 start <=1; #10 start <= 0; end endmodule

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

诺伯里-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值