便捷的网站建设,宁波网站建设制作哪家好,wordpress固定链接规则文件,怎么创建网址如何制作一个简单的16位CPU#xff0c;首先我们要明确CPU是做什么的#xff0c;想必各位都比我清楚#xff0c;百度的资料也很全。。。。。如果想要制作一个CPU#xff0c;首先得明白下计算机的组成结构(或者计算机的替代品#xff0c;因为并不是只有计算机有CPU#xff… 如何制作一个简单的16位CPU首先我们要明确CPU是做什么的想必各位都比我清楚百度的资料也很全。。。。。如果想要制作一个CPU首先得明白下计算机的组成结构(或者计算机的替代品因为并不是只有计算机有CPU现在的电子产品都很先进很多设备例如手机、洗衣机甚至电 视和你家的汽车上面都得装一个CPU)数字电路基础还最好有点编程的基础(当然没有也没关系这些知识都很容易获得各种书上面都会提到并且在接下来的过程中我会提到这些知识)我们要实现的是一个RISC指令集的CPU并且我们最后要自己为这个CPU设计指令并且编码。首先我们来听个故事关于CPU的诞生的故事日本客户希望英特尔帮助他们设计和生产八种专用集成电路芯片用于实现桌面计算器。英特尔的工程师发现这样做有两个很大的问题。第一英特尔已经在全力开发 三种内存芯片了没有人力再设计八种新的芯片。第二用八种芯片实现计算器将大大超出预算成本。英特尔的一个名叫特德?霍夫(Ted Hoff)的工程师仔细分析了日本同行的设计他发现了一个现象。这八块芯片各实现一种特定的功能。当用户使用计算器时这些功能并不是同时都需要的。比 如如果用户需要计算100个数的和他会重复地输入一个数再做一次加法一共做100次最后再打印出来。负责输入、加法和打印的电路并不同时工作。 这样当一块芯片在工作时其他芯片可能是空闲的。霍夫有了一个想法为什么不能用一块通用的芯片加上程序来实现几块芯片的功能呢当需要某种功能时只需要把实现该功能的一段程序代码(称为子程序)加载到通用芯片上其功能与专用芯片会完全一样。经过几天的思考后霍夫画出了计算器的新的体系结构图其中包含4块芯片一块通用处理器芯片实现所有的计算和控制功能一块可读写内存(RAM)芯片 用来存放数据一块只读内存(ROM)芯片用来存放程序一块输入输出芯片实现键入数据和操作命令、打印结果等等功能。看完这个故事后可以总结CPU是一种用来代替专用集成电路的器件(这只是我的理解不同人有不同理解这个就智者见智了我在接下来的例子中也会说明我的想法)。然后考虑如下这个例子例1-1:mov eax,0repeat:inc eaxjmp repeat例1-2:int main(){unsigned int i 0;while(1)i;}例1-3:可以看到以上三个例子都产生了一个从0不断增加的序列而且前两个例子会一直加到溢出又从0开始(这个取决于计算机的字长也就是多少位的CPUeax是 32位寄存器所以必然是加到4294967295然后回0而后面那个c程序则看不同编译器和不同平台不一样)后面那个例子则看你用的是什么样的加法器和多少个D触发器那问题就来了我假设要一个递减的序列怎么办呢前两个例子很好解释我直接改代码不就得了例2-1:mov eax,0repeat:dec eaxjmp repeat例2-2:int main(){unsigned 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是Reduced Instruction Set Computer精简指令集计算机典型例子是MIPS处理器。CISC 是Complex Instruction Set Compute复杂指令集计算机典型例子是x86系列处理器(当然现在的x86指令还是当初cisc的指令但是实际处理器的结构都已经变成了 risc结构了risc的结构实现流水线等特性比较容易在计算机前的你如果用的是intel某系列的处理器则它使用的指令集看上去还是像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每个中断向量占四个字节)能容纳256 个中断(以实模式的8086举例)响应中断时将中断号对应的地址上的cs和ip的值装入到cs和ip寄存器而将原来的地址保存并且保存状态寄存器然后 进入中断处理而risc则拥有一个共同的中断响应函数这个函数会根据中断号找到程序向系统注册的函数的地址并且调用这个函数。一般来说而是用的 cisc指令的长度是不定的例如x86的xor ax,bx对应机器码是0x31d8、而push ax是0x50、pop cx是0x59。而risc的指令确是定长的例如32位。如果还有不清楚的。。。。。自行百度要理解这些概念需要一点时间一个CPU的基本结构以及必要组件这个例子引用自DE2开发板套件带的光盘上的Lab Exercise 9我们从图中可以看到一个CPU包含了通用寄存器组R0~R7一个ALU(算术逻辑单元)指令寄存器IR控制器(一般这部分是一个有限状态机或 者是用微指令实现)还有就是数据通路(图中的连线)。当然真正的CPU不可能只包含这么一点点组件这是一个模型CPU也就是说只是说明CPU的原 理真正复杂的CPU要涉及到很多复杂的结构和时序例如虚拟模式需要使用一些特殊的寄存器、为了支持分页需要使用页表寄存器等为了加速内存的访问需要 使用TLB加速数据和指令的访问而使用data cache和instruction cache等等。。。。。当然那都是后面该考虑的所以我们先从这个简单的部分开始讲起。例子中能实现如下指令mv指令将Ry的数据转移到Rx中mvi将立即数D转移到Rx当中add将Rx和Ry的和放到Rx中sub同上不过执行的是减法。首先来说明mv指令是如何执行的mv指令将Ry的值移入Rx寄存器当中这两个寄存器都是由一组D触发器构成而D触发器的个数取决于寄存器的宽度就像 32位机、64位机这样那他们的寄存器使用的D触发器的个数就是不一样的。当执行mv rx,ry时中间的多路器(图中最大的那个multiplexer)选通Ry让Ry寄存器驱动总线这个时候Bus上的信号就是Ry的值然后再看到 R0~R7上分别有R0in~R7in信号这个信号是使能信号当这个信号有效时在上升沿此触发器会将din的数据输入所以说到这里大家一定想到 了这个时候Rx触发器上的Din信号就会变为有效这样过了一个时钟周期后Ry的值就被送到了Rx当中。与mv指令类似mvi指令也将一个数据送入Rx当中只不过这次的数据存在指令当中是立即数所以Rx的Din信号会变为有效而多路器会选择IR中的数据因为mvi指令的立即数存在指令当中。并且进行一定处理例如扩展等。add 指令会让多路器先选择Rx然后Ain信号有效这样一个时钟周期后Rx数据被送入Alu的A寄存器当中这时多路器选择Ryaddsub信号为 add以指示ALU进行加法操作Gin有效让G寄存器存放运算结果然后再过一个时钟周期G当中的数据就是Rx与Ry的和这时多路器再选择 GinRx的Din有效过了一个时钟周期后数据就被存放到Rx当中了。sub的过程与add差不多不过addsub信号是sub指示ALU进行减法。我做的CPU模型下面我就将我做的CPU模型的RTL网表发出来代码我会上传的但是这个还只能进行仿真因为设计 的时候理念有问题出现了异步设计而且出现了将状态机的输出作为另一个器件的时钟端的错误所以这个模型只能用于仿真。我用的synplify pro综合出的RTL而状态转移图是用的Quartus的FSM Viewer截下来的。首先是整个系统的概览这个比上面的那个简单模型复杂多了吧但是别担心其实这个只是上面的那个CPU变得稍微复杂了一点这个和上面那个不同的地方还有这个CPU是一个多周期CPU而上面的Lab Exercise是一个单周期的CPU下图是程序计数器(PC)也就是常见x86处理器里面的ip(instruction poiniter)红色部分就是pc了后面是一个三态桥连接到了总线上面这里的数据有时候是要送到地址总线用于寻内存中的数据以便完成Instruction Fetch过程。有时候又要送到通用寄存器的数据端用于将pc的值送到其他寄存器。下面这个是IR(Instruction Register)这个是多周期处理器的典型特征因为处理器在第一个周期里面将机器码从内存取出然后存放到这个寄存器里面后面的几个状态都是通过这个寄存器里面的数据作为指示执行操作的。下面介绍一下ALUALU是Arithmetic Logic Unit即算术逻辑单元这个装置的作用是进行算术操作和逻辑操作。典型的算术操作例如11211x23253而典型的逻辑操作例如1 and 110 or 0 0,138这种属于逻辑操作。而从图中大家也看得到ALU的输出用一根很长的线连接到了后面参考整个CPU的图的话会发现这些线连到了通用寄存器上面这是为了让运算的结果存放回 去例如你用add eax,1的时候eax的值被加上1然后放回eax所以ALU的运算结果要用反馈送回到通用寄存器而ALU的输入也应该有通用寄存器的输出。下面再介绍ADDRMUX这个部件是用来选择地址的右边的输出是CPU的地址总线而CPU的地址总线就已经送出CPU了(也就是你能够在芯片的外表上看到引脚了)CPU的地址总线是送到存储器的地址端的而现代的计算机系统实际上是相当复杂的所以其实你家的计算机上CPU是通过北桥芯片访问内存的(当然也有将内存控制器做到 CPU里面的)左边是地址的来源地址的来源即有通用寄存器也有程序计数器还有一个是直接从IR里面送出这是因为有的立即数里面也包含内存地址信息。最后介绍通用寄存器通用寄存器的作用就是用来保存中间值或者用于运算例如add eax,2相当于eax2然后送回eax。最后介绍一下状态机这个部分就是CPU的“灵魂”如果说有了上面那些部件CPU有了一副“躯体”的话这一部分就是CPU的“灵魂”了状态机基本上与系统所有的组件都连接到一起了因为上面所说的所有动作的执行都需要状态机的控制状态机其实就是由一部分触发器构成的记忆电路和另外一部 分组合逻辑构成的次态译码电路构成还有根据当前状态和输入进行译码的部分用于控制各个部件,下面是教科书上的典型FSM结构而我们用的状态机状态转移图如下因为这个处理器设计的很简单所以没有出现很多状态当处理器经历完以上的状态之后处理器就执行完了一条指令。有的CISC的处理器用微指令进行控制作用和状态机相近这种结构出现在一些比较古老的处理器上因为那个时候的设计工具和方法没有现在的先进所以往往 改动硬件是困难的和高成本的所以用微指令的话做好了硬件的结构要是需要改动只要修改微指令就好了而现在的电子技术很发达设计工具也很完备所以 就有很多直接通过硬连线实现的处理器。好马配好鞍有了处理器我们就得给它配上一个好的程序下面我们就用自己设计的处理器进行求和从1加到100因为我们没有设计编译器也没有设计汇编器所以程序只能用机器码写出示例程序如下我们不妨先写出程序的汇编代码mov [ADDR],r0;r0 0mov r1,100lop:add r2,r1sub r1,1cmp r1,0jz extmov r4,4jmp r4(lop)ext:mov [ADDR],r2jmp $先将内存中存放数据的地址清零这样才能存放等下送来的结果然后将r1寄存器存入循环次数(也就是求和的上限)。然后再将r1的值加到r2中来,r2其实就是放求和的寄存器最后我们会将r2中的值送到内存中的某个地址存放的。然 后将r1减去1看看是否为0如果为0则说明求和结束了如果不是0则说明还要继续结束后程序就跳到ext部分将结果存放到内存中某个地址(例子中给 的是49152也就是二进制的1100000000000000b)最后jmp $是为了让程序停在这一行防止程序跑飞(跑飞的程序危害很大有可能吧数据当代码或者把代码当数据)转换成VerilogHDL语言如下module memory(input [15:0] addr,inout [15:0] data,input rw);reg [15:0] data_ram[0:16b1111_1111_1111_1111];integer i;initial beginfor (i 0; i 16b1111_1111_1111_1111; i i 1)data_ram[i] $random();data_ram[0] 16b1000000100000000; //mov [ADDR],r0;r0 0data_ram[1] 16b1100000000000000; //ADDRdata_ram[2] 16b1000000010001000; //mov r1,100data_ram[3] 100; //100//data_ram[2] 16b1110011001000000;data_ram[4] 16b0010000100010001; //lop:add r2,r1data_ram[5] 16b1110000011001000; //sub r1,1data_ram[6] 16b0000000000000001; //1data_ram[7] 16b1110000000001000; //cmp r1,0data_ram[8] 16b0000000000000000; //0data_ram[9] 16b1110011010000000; //jz extdata_ram[10] 16b0000000000000011; //3 offset(ext)data_ram[11] 16b1000000010100000;//mov r4,4data_ram[12] 16b0000000000000100;data_ram[13] 16b0110011001100000;//jmp r4(lop)data_ram[14] 16b1000000100000010;//ext:mov [ADDR],r2data_ram[15] 16b1100000000000000;//ADDRdata_ram[16] 16b1110011001000000;//jmp $data_ram[17] 16b1111111111111110;//-2 offset($)/*data_ram[0] 16b1000000010000000; //mov r0,immdata_ram[1] 16b0011111111111111; //immdata_ram[2] 16b0000000001111000; //mov r7,r0data_ram[3] 16b1000000010011000; //mov r3,0data_ram[4] 16b0000000000000000;data_ram[5] 16b1000000010100000; //mov r4,code of jmp r5data_ram[6] 16b0110011001101000; //jmp r5data_ram[7] 16b0000000101011100; //mov [r3],r4data_ram[8] 16b1000000011110000; //mov r6,[0]data_ram[9] 16b0000000000000000; //[0]data_ram[10] 16b1000000100000110; //mov [255],r6data_ram[11] 16b0000000011111111;data_ram[12] 16b0110011001011000; //jmp r3*/endalways (addr or rw or data)if (rw)data_ram[addr] data;assign data rw ? 16hzzzz : data_ram[addr];endmodule设计中CPU外围还需要一个内存设备(Memory)我用HDL对其建模初始化的时候每个内存地址上对应的数据都初始化为随机的然后只有从0开始的一系列地址被初始化为我写的代码机器码对应的汇编指令在注释中已经给出。然后是结果结果应该是r2从0变化到5050(123......1005050)而r1则从100变化到0变化到0后程序将进入死循环停止在jmp $那一条。这是仿真开始的时候大家可以看到初始化后d0~d7都变成了0这是r0~r7寄存器的Q端而state_current和state_next则是状态机的现态和状态机 的次态cpu的各个部件都通过这个状态机受到控制。状态名出现的顺序和上面的FSM Viewer的连线顺序是一样的。而且大家可以看到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中去cmp dst,src 将dst减去src 然后不送到dst中 只改变标志位jz dst 当zf1时(即上次的算术操作结果为0)则跳转到dst中去最后再提一下我是用synplify综合的电路然后用debussymodelsim仿真的相关资料请参考CPU逻辑设计朱子玉李亚民著Lab Exercise 9出自DE2的开发光盘作者大法师千寻-END-感谢您的阅读 请点击右下角“好看”