如何制作一个简单的16位CPU(快来这里看教程)
如何做一个简单的16位CPU,首先要明确CPU是做什么的。我想你们都比我清楚,百度的信息也很全面。
想做CPU,首先要了解电脑的组成(或者说它的替代品,因为不是只有电脑有CPU,现在电子产品很先进,很多设备比如手机,洗衣机,甚至电视机,你的汽车都要装CPU),数字电路的基础,最好有一些编程基础(当然没有也无所谓, 这些知识很容易获得,在各种书里都会提到,在接下来的过程中也会提到)
我们要实现的是一个带RISC指令集的CPU,最后还要自己为这个CPU设计指令和代码。
首先,我们来听一个关于CPU诞生的故事:
日本客户希望英特尔帮助他们设计和生产8种用于台式计算器的ASIC芯片。英特尔工程师发现这样做有两个大问题。第一,英特尔一直在全力研发三种内存芯片,没有人力去设计八种新的芯片。第二,用八种芯片实现计算器,会大大超出预算成本。
Intel的一个名字叫Ted?泰德霍夫的工程师仔细分析了日本同行的设计,他发现了一个现象。这八个芯片各实现一个特定的功能。当用户使用计算器时,这些功能并不是同时都需要的。比如用户需要计算100个数的和,他会反复输入一个数,再加,一共做100次,然后打印出来。负责输入、加法和打印的电路不会同时工作。这样,当一个芯片在工作时,其他芯片可能是空闲的。
霍夫有了一个想法:为什么不能用一个通用芯片和一个程序来实现几个芯片的功能?当需要某项功能时,只需在通用芯片上加载一段程序代码(称为子程序),其功能将与专用芯片完全相同。
经过几天的思考,霍夫绘制了一个新的计算器架构图,其中包括四个芯片:一个通用处理器芯片,实现所有的计算和控制功能;用于存储数据的读写存储器(RAM)芯片;用于存储程序的只读存储器(ROM)芯片;一种输入输出芯片,可以输入数据、操作命令、打印结果等。
看了这个故事,可以得出结论,CPU是用来替代ASIC的器件(这只是我的理解,不同的人有不同的理解,所以不同的人有不同的看法,下一个例子我也会阐述我的想法)。
然后考虑下面的例子:
实施例1-1:
移动eax,0
重复:inc eax
jmp重复
实施例1-2:
int main()
{
无符号int I=0;
while(1)
我;
}
实施例1-3:
可以看到,上面三个例子都是从0开始产生递增序列,前两个例子会继续溢出,从0开始(这个取决于计算机的字长,也就是CPU的多少位,eax是32位寄存器,所以必须加到4294967295再返回0,后面的C程序取决于不同的编译器和平台),后面的例子取决于你用什么样的加法器和多少个D触发器。
这就是问题所在。如果我想要一个递减序列呢?前两个例子很容易解释,所以我只需要直接修改代码:
实施例2-1:
移动eax,0
重复:十二月十日
jmp重复
实施例2-2:
int main()
{
无符号int I=0;
while(1)
I-;
}
你所要做的就是敲击键盘,修改代码,它就会如你所愿地执行。
但是后一个例子呢?也许你已经想到了一个办法:如例2-3所示。
实施例2-3:
问题是你在键盘上敲两下是改变不了实际电路的!上面(例1-3)是加法器,这里却变成了减法器(例2-3)!
这种情况下,你要再做一个电路,一个做加法,一个做减法,但是两个电路意味着你要用更多的电路和芯片,你要花更多的钱。如果你不能同时使用这两条电路,你花了两笔钱却只做了一件事!
这个问题能解决吗?答案是肯定的!
请看例3:
在这个例子中,使用了一个加法器和一个减法器,不亚于上面的电路(显然。要不要把减法器当加法器用?不可能!当然,加一个负数的补码真的是减去一个数,但这里不考虑这个问题。)多了一个复用器,少了一个D触发器。总的来说优势很明显(两块电路板和一块电路板的区别)。
sel信号用于选择(0表示增加,1表示减少)。
如果我们把sel信号看成一个“程序”,这个电路就像一个“CPU”,可以根据“程序”进行不同的“运算”。这样,这个电路就可以通过“程序”(sel信号)重复使用。
根据以上结论,我认为(仅个人~):程序是硬件电路的延伸!
而CPU的基本思想,我想就是这样了。
接下来,我们将分析CPU的结构和组件,然后实现这个CPU。
什么是单周期CPU,什么是多周期CPU,什么是RISC,什么是CISC?
首先大家要有时钟的概念:这个问题不好解释。可以理解为家里的机械钟,接上电池后滴答滴答走,其“滴答”的速度就是频率,滴答的时间就是周期,而人的工作、下班、吃饭、学习、娱乐都是根据时钟的指示进行的(不算熬夜的网瘾少年)。一般来说,时钟信号都是由晶体振荡器产生的,0101(低电平和高电平)交替信号。
数字电路需要一个“时钟”来驱动,就像演奏交响乐时前面需要一个指挥一样。每个人都会随着指挥的节拍演奏,就像数字电路中的所有元件都会随着时钟的节拍工作一样。
以下是理想的时钟信号:(注意是理想的)。
当然,实际的时钟信号可能与理想相差甚远,上升沿可能是倾斜的,占空比可能不是50%,存在抖动和偏移(相对于两个器件而言),这可能由于导体的寄生电容效应而失真。
如果你看不懂上面那段也没关系~ ~反正我就是想告诉你,实际的时钟信号肯定不是那么标准的。
cpu的工作频率是外部频率和倍频的乘积(cpu的频率怎么算,其实这个我不太懂),因为cpu是通过外部晶振产生时钟信号,再通过内部电路(锁相环)倍频到需要的频率。当然有人问,何苦呢?直接在电路外面做一个时钟晶振就可以产生这么高的时钟信号了。这个是可以的,在一些简单的系统中(比如51单片机)是这样的,但是计算微机的cpu比较复杂,因为一些原因必须在cpu中完成。
先简单说一下CPU的两个指令集:CISC和RISC。
说说我的看法(个人看法,如有错误请指正):
RISC是精简指令集计算机,典型的例子是MIPS处理器。
Cisc是复杂指令集Compute,典型的例子就是x86系列处理器(当然x86指令还是cisc的原始指令,但是实际的处理器结构已经变成了risc结构,更容易实现流水线等特性。如果你在电脑前使用一系列英特尔处理器,它使用的指令集看起来仍然像CISC指令,但实际上你的cpu的结构已经是risc了)。
一般cisc的处理器需要用微指令运行,而risc都是用硬连线实现的。也就是说,在执行你的程序之前,cisc的处理器要从另一个rom中读取一些数据来“指示”处理器如何处理你的命令,所以CISC的效率比较低,而RISC完全通过组件之间的连接来实现一些功能,大大提高了工作效率,为流水线结构的出现提供了基础。Cisc的寄存器数量很少,所以指令可以实现一些特殊的功能,比如8086的一些寄存器:
Ax,bx,cx,dx,si,di等。段寄存器包括:cs,ds,es,ss等。相对指令函数比较特殊,比如xlat以bx中的值为基址,以al中的值为偏移量,将内存中寻址的数据发送给al(以ds为段寄存器)。
另一方面,Risc处理器有更多的通用寄存器,而指令的功能可以稍微弱一些,例如:
以nios嵌入式处理器为例,nios处理器有32个通用寄存器(r0~r31),但指令功能比x86弱,x86直接用mov指令进行内存访问,nios处理器用load读内存,用store写内存。
它们以不同的方式响应中断。举个典型的例子,x86处理器把中断向量表放在内存的最低地址(0-1023,每个中断向量占用4个字节),可以容纳256个中断(以实模式下的8086为例)。响应中断时,与中断号对应的地址的cs和ip值被载入cs和ip寄存器,原始地址被保存。并保存状态寄存器然后进入中断处理,而risc有一个常用的中断响应函数,会根据中断号找到程序向系统注册的函数的地址,调用这个函数。一般来说,代替使用的cisc指令长度是不确定的,比如x86的XROAX,机器码对应的bx是0x31d8、,push ax是0x50,pop cx是0x59。Risc指令确实是定长的,比如32位。
如果有什么不清楚的。理解这些概念需要一点时间。
CPU的基本结构和必要部件
此示例引自DE2开发板套件光盘上的实验练习9。从图中我们可以看到,一个CPU包括通用寄存器组R0~R7,一个ALU(算术逻辑单元),指令寄存器IR,控制器(一般这部分是有限状态机或者是用微指令实现的),数据通路(图中的连接)。
当然,一个真正的CPU不可能只包含这么几个组件。这是一个模型CPU,这意味着它只解释了CPU的原理。一个真正复杂的CPU涉及到很多复杂的结构和时序,比如在虚拟模式下使用一些特殊的寄存器,为了支持分页使用页表寄存器,为了加快内存访问使用TLB,为了加快数据和指令访问使用数据缓存和指令缓存。当然,那要以后再考虑,就从这个简单的部分开始吧。
以下指令可以在示例中实现:
mv指令将Ry的数据传输到Rx,mvi立即将数字d传输到Rx,add将Rx和Ry的和放入Rx,sub同上,但执行的是减法。
先解释一下mv指令是如何执行的:mv指令将Ry的值移入Rx寄存器,两者都是由一组D触发器组成的,D触发器的个数取决于寄存器的宽度,就像32位机和64位机一样,所以两者寄存器使用的D触发器个数是不一样的。
当执行mv rx和Ry时,中间的多路复用器(图中最大的多路复用器)选通Ry,使Ry寄存器驱动总线。此时总线上的信号就是ry的值;那么我们可以看到R0~R7上分别有R0in~R7in信号。该信号是使能信号。当这个信号有效时,这个触发器会在上升沿输入din数据,所以你一定想到了Rx触发器上的Din信号会在这个时候变得有效,所以Ry的值会在一个时钟周期后发送到Rx。
和mv指令类似,mvi指令也发送一个数据到Rx,只不过这次数据存在于指令中,是一个立即数,所以Rx的Din信号会变得有效,多路复用器会选择IR中的数据,因为mvi指令的立即数存在于指令中。并做一些处理,比如展开。
add指令将使多路复用器首先选择Rx,然后Ain信号将有效。在这样的时钟周期之后,Rx数据将被发送到Alu的A寄存器。此时,多路复用器选择Ry,并且addsub信号是add以指示ALU执行加法运算。Gin有效地允许G寄存器存储运算结果,然后在另一个时钟周期后G中的数据将是Rx和Ry之和。此时多路复用器再次选择Gin,Rx的Din有效。一个时钟周期后,数据将存储在Rx中。
sub的过程和add类似,但addsub的信号是sub指令ALU做减法。
我做的CPU模型
下面我会把我做的CPU模型的RTL网表发出来,我会上传代码,但是这个模型只能模拟,因为设计的时候概念有问题,有异步设计,把状态机的输出作为另一个器件的时钟端有错误,所以这个模型只能用于模拟。我用synplify pro合成RTL,状态转移图被Quartus FSM Viewer截取。
首先是整个系统的概述:
这比上面的简单模型复杂多了!不过不用担心,其实这只是上面的CPU变复杂了一点而已。这个CPU与上面的不同之处在于,它是一个多周期CPU,而上面的实验练习是一个单周期CPU。
下图显示了程序计数器(PC),即常见x86处理器中的ip(指令计数器):
红色部分是pc,后面是三态桥,连接到总线。这里的数据有时被发送到地址总线,以便在存储器中找到数据,从而完成取指令过程。有时它必须被发送到通用寄存器的数据端,用于将pc的值发送到其他寄存器。
下面是IR(指令寄存器),这是多周期处理器的典型特征,因为处理器在第一个周期就把机器码从内存中取出来存放在这个寄存器中,后面的状态都是通过这个寄存器中的数据作为指令来执行操作。
下面介绍一下ALU。ALU是算术逻辑单元,即算术逻辑单元。这个装置的功能是进行算术和逻辑运算。典型算术运算示例
如:1 ^ 1=2,11x23=253,而典型的逻辑运算如:1和1=1,0或0=0,1“3=8”都是逻辑运算。
从图中可以看到,ALU的输出用一条长线连接到后面。如果你参考整个CPU的图,你会发现这些线是连接到通用寄存器的,通用寄存器是用来把运算结果存回去的。比如你用ADeax,1,eax的值加1再放回eax,所以ALU的运算结果要反馈回通用寄存器,ALU的输入也要有通用寄存器的输出。
下面将介绍ADDRMUX:
该组件用于选择地址。右边的输出是CPU的地址总线,CPU的地址总线已经送出CPU(也就是你可以看到芯片外面的管脚),CPU的地址总线送到内存的地址端。
现代的计算机系统其实挺复杂的,所以其实你的计算机的CPU是通过北桥芯片访问内存的(当然CPU内部也有内存控制器)。左边是地址的来源,既包括通用寄存器,也包括程序计数器,还有一个是直接从IR发出的,因为有些立即数也包含内存地址信息。
最后,介绍通用寄存器:
例如,通用寄存器的功能是存储中间值或进行运算。
添加eax,2
相当于eax 2然后送回eax。
最后介绍一下状态机。这部分是CPU的“灵魂”。如果CPU有一个由上述部件组成的“身体”,这个部分就是CPU的“灵魂”:
状态机基本上与系统的所有组件相连,因为上面提到的所有动作的执行都需要状态机的控制。状态机实际上是由一部分触发器组成的存储电路和另一部分组合逻辑组成的子状态解码电路组成,根据当前状态和输入进行解码的部分用来控制各个部件。以下是教科书中典型的有限状态机结构:
我们使用的状态机的状态转换图如下:
因为这个处理器的设计非常简单,所以状态不多。当处理器经历上述状态时,处理器完成一条指令的执行。
一些CISC处理器由微指令控制,类似于状态机。这种结构出现在一些较旧的处理器中。因为当时的设计工具和方法没有现在先进,所以改变硬件往往很困难,成本也很高。所以,如果使用微指令,硬件结构准备充分。如果有必要更改,只需修改微指令。现在电子技术发达,设计工具齐全,所以有很多处理器是直接用硬连线实现的。
好马配有马鞍。有了处理器,就要配好程序。我们用一个自己设计的处理器总结一下,从1加到100。因为我们没有设计编译器或者汇编器,所以程序只能用机器码写。示例程序如下:
我们不妨先写程序的汇编代码:
mov [ADDR],r0;r0=0
mov r1,100
lop:添加r2、r1
子r1,1
cmp r1,0
jz扩展
mov r4,4
jmp r4(lop)
分机:mov [ADDR],r2
jmp $
首先在内存中清空数据存放的地址,以便存放后面发送的结果,然后在r1寄存器中存放周期数(即和的上限)。然后将r1的值加到r2上,R2实际上是一个用于求和的寄存器。最后,我们将把r2中的值发送到内存中的一个地址进行存储。
然后从r1中减去1,看是不是0?如果是0,表示求和结束;如果不为0,则表示将继续。之后,程序跳转到ext部分,将结果存储在内存中的一个地址中(示例中给出为49152,即二进制的1100000000000000b)。最后用jmp $停止这行程序,防止程序跑路(跑路程序危害很大!有可能把数据当成代码,也有可能把代码当成数据!)
转换成VerilogHDL语言如下:
模块存储器
(
输入[15:0]地址,
inout [15:0]数据,
输入rw
);
reg[15:0]data _ ram[0:16 ' b 1111 _ 1111 _ 1111 _ 1111];
整数I;
初始开始
for(I=0;I"=16 ' b 1111 _ 1111 _ 1111 _ 1111;i=i 1)
data _ ram[I]=$ random();
data _ ram[0]=16 ' b 100000010000000;//mov [ADDR],r0;r0=0
data _ ram[1]=16 ' b 110000000000000;//ADDR
data _ ram[2]=16 ' b 1000000010001000;//mov r1,100
data _ ram[3]=100;//100
//data _ ram[2]=16 ' b 1110011001000000;
data _ ram[4]=16 ' b 0010000100010001;//lop:添加r2,r1
data _ ram[5]=16 ' b 1110000011001000;//sub r1,1
data _ ram[6]=16 ' b 000000000000001;//1
data _ ram[7]=16 ' b 1110000000001000;//cmp r1,0
data _ ram[8]=16 ' b 000000000000000;//0
data _ ram[9]=16 ' b 1110011010000000;//jz ext
data _ ram[10]=16 ' b 000000000000011;//3偏移(外部)
data _ ram[11]=16 ' b 1000000010100000;//mov r4,4
data _ ram[12]=16 ' b 000000000000100;
data _ ram[13]=16 ' b 0110011001100000;//jmp r4(lop)
data _ ram[14]=16 ' b 1000000100000010;//ext:mov [ADDR],r2
data _ ram[15]=16 ' b 110000000000000;//ADDR
data _ ram[16]=16 ' b 1110011001000000;//jmp $
data _ ram[17]=16 ' b 111111111111110;//-2偏移量($)
结束
始终@(地址或读写或数据)
中频(rw)
data _ ram[addr]=data;
赋值数据=rw?16 ' hzzzz:data _ ram[addr];
末端模块
在设计中,CPU外围还需要一个存储设备。我用HDL建模。初始化的时候,每个内存地址上对应的数据被初始化为随机的,然后只有从0开始的一系列地址被初始化为我写的代码。注释中给出了与机器代码相对应的组装说明。
然后结果,结果应该是r2从0变到5050 (1,2,3。100=5050)
R1从100变到0,变到0后,程序会进入无限循环,停在jmp $处。这是模拟开始的时间:
你可以看到,初始化后d0~d7都变成0,是r0~r7寄存器的Q端,而state_current和state_next是状态机的当前状态和二级状态,cpu的所有部件都由这个状态机控制。状态名称出现的顺序与上面FSM查看器的连接顺序相同。
而且大家可以看到,d2从0变成了0x64,也就是十进制100,表示已经进行了第一次加法。
让我们看看模拟的结尾:
此时d1变为0,d2变为0x13ba(十进制5050),说明程序已经在我们设计的处理器中运行并成功获得结果!
最后,我给出一些我使用的指令(非常类似于x86):
Add dst,src将src和dst相加,并发送到dst寄存器。
Mov [addr]],src将src的值发送给具有addr位地址的存储单元。
Sub dst,src从dst中减去src,并发送给dst。
Cmpst,src从dst中减去src然后不发送给dst,只改变标志位。
Jz dst当zf=1时(即最后一次算术运算的结果为0),跳转到dst。
最后,提及:
我用的是synplify合成的电路,然后用德彪西modelsim模拟。
请参考:
CPU逻辑设计,朱、
实验练习9来自DE2的开发光盘。
作者:驱魔人千寻
版权声明:文章转自网络。版权归原作者所有。如有侵权,请联系我们删除!
japan quarterly 日本季刊
推荐阅读
- 侠盗猎车圣安地斯秘籍/侠盗猎车圣安地斯,作弊码
- 去一趟巴厘岛旅游要多少钱简介
- 苹果6s怎么升级系统,苹果6s怎么设置铃声
- 如何涂指甲油,怎样涂指甲油干得快
- 纪念碑谷艾达的梦攻略第四关,纪念碑谷艾达的梦攻略
- udk虚幻4引擎(游戏开发包工具)软件介绍(udk虚幻4引擎(游戏开发包工具))
- OG梅奥为什么离开nba Pubmed GIST文献月评第十九期(Jun 2018)
- 12306用户名和密码忘记怎么找回账号,12306用户名和密码忘记怎么找回
- 哈尔滨市极乐寺简介
- 二人麻将打法技巧,迅速如何掌握二人麻将技巧
- 故宫门票多少钱一张2021,故宫门票多少钱
- 如何删除微信中的表情包,微信如何删除自己保存的表情
- dnf已经有红字的怎么把红字洗掉,dnf已经洗出红字的装备怎么洗掉
- 海蛏子的家常做法,海蛏子的做法大全
- 微信聊天记录怎么恢复吗,微信聊天记录怎么恢复方法:
- qq飞车帧数如何能锁,QQ飞车帧数如何修改
- 1盎司相当于多少克黄金,盎司等于多少克及一盎司黄金等于多少克
- 怎么恢复路由器出厂,怎样恢复路由器出厂设置
- 触手tvlogo怎么买,如何录制触手TV文章
- 藏语常用问候语及礼貌语 旅行必备