前段时间做了组成原理的课设,任务是设计一个自己的计算机,我从开始设计到完成总共花了10天左右的时间,真可谓是绞尽脑汁,痛彻心扉啊!为了以后的学弟学妹能够少走些弯路,我这里就分享一下我设计的经验。
- 计算机模型的选择:
首先是设计计算机的模型,是做硬布线计算机还是做微程序计算机。这是两类不同的设计方式,当然,这里我认为两种方式都可以,实现起来难度是差不多的。因为真正困难的地方不在这里。如果是硬布线的话就需要考虑控制信号的组合逻辑设计及时序逻辑的设计,如果是微程序控制的话就需要考虑控点的时序,这才是最困难的地方,我后面再讲。
选择了模型后就可以开始一个部件一个部件的构造了,先在纸上画出来,然后再去实验室拿芯片开始连线。我设计的是微程序控制的计算机。
对于微程序控制的计算机来说还需要决定一个地方,那就是是采用主存和控存分开的方式实现,还是合并的方式实现的问题。我认为分开比较好,因为这样思路更加清晰,而且比较容易找出设计当中的问题,在接时序的时候也更加方便得排查错误。当然,这纯属一家之言,我就有一个好朋友用的是主控存合并的方式实现的,照样实现的非常不错。
- 画布线图:
下一步是画电路图,并选择芯片,你可以在纸上画,也可以在QuartusII这样的软件里画,不过我建议先在纸上画好,因为在纸上人比较容易理清思路来,而且最后你还不一定因为芯片问题需要使用FPGA下载,所以应该先在纸上画好整个布线图。本来我想把布线图传上来的,不过朋友说这样会使后来的童鞋不动脑筋的照搬,所以我这里就不放上来了。多研究一下组成原理书上的CPU实现图就知道该如何画这个布线图了,我的建议是一个部件一个部件的画,比如先画ALU,再画内存部分,最后画控存部分。
- 挑选芯片:
尽量使用常见的且性能较好的逻辑简单的芯片,我列举一下我整个系统所用的芯片如下:
161芯片 1片 (做程序计数器PC的)
157芯片 1片 (做内存地址的二路选择器,一边选PC,一边选IR下址)
6116芯片 1片 (内存,11位地址线,8位数据线)
244芯片 3片 (三态门,控制输入输出等地方)
373芯片 6片 (做AC、DR1、DR2、IR、控存寄存器的)
395芯片 1片 (做微程序计数器uPC)
2816芯片 2片 (控存,11位地址线,8位数据线)
181芯片 2片 (加法器)
74LS00芯片 1片 (二输入四与非门,每片4个)
74LS04芯片 2片 (反相器,每片6个)
74LS08芯片 2片 (二输入四与门,每片4个)
- 检验试验台和导线:
不要忽视这个环节,因为有的时候可能你的设计都是正确的,结果一根导线断了就让你1整天毫无进展。
检验的时候一定要细心,稍不留意就会出错,导线的检查也很重要,每根导线都要测试一下,要不然错了都不知道错在哪里。检查方法也很简单,只需要把导线的一头接开关,另一头接灯,然后拨动开关观察灯的显示,这样可以同时测试开关和灯的好坏。芯片的检查就要复杂点,需要按照实验指导书后面的芯片功能表进行测试。总之,检查的过程是小心驶得万年船。
- 设计数据通路部分:
不带控制电路的数据通路图如下:
设计数据通路时考虑到LDAC指令以及做运算的指令、回存指令能否实现,其实在157选择器的另外一端接的输入是IR的低4位地址回送回内存,但最初做数据通路时157始终选择161端来的地址。左边244的功能能够控制6116读出的数据送入累加器AC,右边244的功能能够控制数据回存进内存时6116控制信号的配合。功能描述如下:
数据从6116导入到AC——6116置读状态,AC导入数据,DR1锁存,DR2高阻。
算术运算和逻辑运算——DR2置高阻,把数据从6116导入DR1,74181调至相应的运算,运算完把DR1锁存,DR2导入数据后锁存。(此时AC一直处于锁存状态)。
计算结果回写内存——AC、DR1锁存,DR2锁存(有数据输出),6116处于写状态(调到相应的地址)。
计算结果回写AC——DR2、DR锁存,AC导入数据,完成后锁存。
数据通路设计好后就是连线了,连线完成后的测试才是重点,这里遇到很多困难,比如刚连接好数据通路,开机运行发现6116输出显示的灯不停的跳到,我把161的输出接灯,发现地址一直在变动,然后我将161的CP端接手动时序,这样一次一次按就发现地址每次跳变2位,而不是我希望的1位。我询问了周围的同学,他们也遇到了同样的问题,然后我在书上查了很久,终于发现了这个问题是由于CP端电压不稳定造成的,于是我把CP端接一盏灯,然后再接时序,这样就每次只跳变一位了。还有如6116的输入其实是非常麻烦的,因为每次我都需要把其他电路断开,然后拔插开关进行输入和输出,后来我参考了其他人的解决方案,把6116的IO端接244,然后通过244接开关进行输入,这样就不必每次都拔插开关了。再如在给6116输入数据的时候,我发现0000号单元一直打不进数据,这样排查了很久也没有找到问题的所在,直到一天晚上我突然发现我再恢复6116读写控制端的时候把写信号无意中接了低电平,导致0000号单元每次都变为0,所以以后每次我恢复6116读写信号时都会先把读写控制端接高电平再恢复,这样问题就解决了。
- 添加控制电路:
添加控制电路之后的电路图如下:
IR选373,理由是指令寄存器需要锁存,且指令长度为8位,373正好满足条件。
μPC选择395,理由是395有清零功能,置数功能,正好可以将操作码送入到控存,也可以很容易满足执行完一条机器指令后返回取值公操作。
控存CM选择2816,理由是2816可以断电下保存数据,而6116不行,这样可以节省大量的用于写控存的时间,也减少了错误的产生。
此时内存地址输入需要一个二选一芯片,供取指令输入地址(PC 输入)和取数据输入地址(从指令的后四位),157可以完成此功能。
PC选用161,可以自加1(顺序执行),也可以置数(完成跳转指令)。
连接完控存,测试控制电路是否正确——给控存写入数据0000单元写入指令(随便写的一个,此时只是验证控制电路作为一个数据通路是否正确),如0111 0000 0000 0101,将μPC清零,观察CM输出是否为上述存入数据。结果正确。
控制电路的连接也出了很多问题,比如2816控制信号的输入就一直是一个头疼的问题,因为16开关需要拔插,而且实验台上已经没有地方插244芯片了,所以就无法像内存输入数据那样用244进行隔离,就只能每次都拔插16个开关了。而且先需要把395高阻后,将2816地址线接开关来控制地址的输入。
- 设计控制点:
我设计的电路需要的控点有:
PC<161>的CP端————自加1
二选一<157>的S端————控制选择哪一路信号输出
MM<6116>的 ————控制读写
左244的 ————控制高阻
右244的 ————控制高阻
AC<373>的使能G————锁存
DR<373>使能G————锁存
DR2<373>的输出控制————高阻
DR2<373>的使能G————锁存
74181的M、S3、S2、S1、S0、端
IR<373>的使能G————锁存
μPC<395>的 ————清零
μPC<395>的 ————时序
其余控制信号或默认接Vcc或GND,或接时序。其中,AC、DR1、IR的输出控制接GND,MM的接GND,μPC的接T1 ,CM<2816>的接Vcc,、接GND, μPC的接T4。
这样我们就有了17个控制点(MM的读写用一个控制信号可以完成),而CM最多只能产生16个控制信号,所以需要对控制点进行压缩合并。
我们发现,左244和DR2的输出控制在同一时刻只能有一个打开,否则会发生数据对撞,而且允许在任意时刻都有一个打开;右244只有在回写MM的时候才需要打开,因此可以与MM的读写控制合并。
- 设计机器指令和微指令的格式:
机器指令格式
操作码(op) |
地址码字段(A ) |
机器指令长度为8bit,其中op长度为4位,地址码长度为4位,寻址方式只能为直接寻址。
微指令格式
各个控制信号所代表的含义如下:
控制信号 |
0 |
1 |
C1~C6 |
运算控制信号 |
|
C7 |
DR2锁存 |
DR2使能 |
C8 |
DR1锁存 |
DR1使能 |
C9 |
AC锁存 |
AC使能 |
C10 |
左244开,DR2高阻 |
左244关,DR2不高阻 |
C11 |
MM处于写状态,右244开 |
MM处于读状态,右244关 |
C12 |
157选161 |
157选IR下地址字段 |
C13 |
161不自增 |
161自增1 |
C14 |
IR锁存 |
IR使能 |
C15 |
395清0 |
395置数 |
我们设计的指令系统很简单,每条机器指令对应一条微指令,从而省去了微指令当中的下地址字段。
- 设计微指令:
取指指令周期流程图
由此流程图设计出来取指微指令为:0111 0100 0001 0000
LDAC 指令周期流程图
由此设计出来的机器指令LDAC对应的微指令为:0000 1111 1111 1010
SAVE指令周期流程图
由此得到机器指令SAVE对应的微指令为:0000 1010 0101 1111
ADD指令周期流程图
由此可设计出ADD指令对应的微指令:0000 1111 1101 1001
跟加法类似(仅仅只有74181的设置不同而已),可以设计出SUB、同或、异或、与、或指令对应的微指令——
SUB 0000 1111 1100 0110
同或 0000 1111 1111 1001
异或 0000 1111 1111 0110
与 0000 1111 1111 1011
或 0000 1111 1111 1110
而单目运算的又有所不同,因为不用从MM导入数据,所需要的控制型号就要少的多。
X2 (乘2)指令的指令周期图
由指令周期图可设计出乘2的微指令为 0000 1111 0101 1100
同理可以设计出 取非的微指令: 0000 1111 0111 0000
- 设计时序
我是这么设想的,我的每条指令有两个CPU周期,每个CPU周期包含4个时钟周期。
在T1时刻的上升沿清空161,T1时刻完成控制信号;T2时刻的上升沿将左244打开,T2时刻DR1使能;T3时刻DR2不高阻,并使能,而且停机指令就设计在T3时刻完成;T4时刻上升沿完成161加1操作,并在T4时刻进行6116写数信号的传输,同时使能AC,在T4时刻下降沿使395置数。这样一来每条机器指令我让第一个CPU周期为取指周期,第二个CPU周期为执行周期。而且控制的161的加一操作放在第一个CPU周期内完成,控制信号的给出放在第二个CPU周期完成,这样就很好的完成了一条指令的执行,而下一条指令的装入,即395置数操作放在第二个CPU周期T4下降沿完成,就使下一控制信号不会和前一个控制信号想冲突。
开机,给μPC一个清零信号,经过一个时钟周期,CM将输出0000的信号,即取指公操作的信号。也就是说,T1用来发出信号,T2到来时,控制信号已经到了各个控制点,所以我们把T1专门用来取控制信号。
161计数器的CP:在取指令操作结束时,需要给一个信号(让161自加1),而在执行指令的时候,不需要CP信号,故可以把C13·T4接CP。
157二选一:取指时,157始终选择PC;执行时,157始终选择IR。故可以直接把C12接157的S控制。
MM<6116>:在执行SAVE指令时,需要把MM调到写状态,且只有到了T4,数据才会到MM;在执行其他指令时,MM一直处于读状态。因此可以把C11+送给和右244,将取非送。
左244:每次输入数据到DR1,在T2时刻能够把数据送到DR1,所以可以把C10·送给左244的控制。
右244:为了防止在回写内存的时候,右244先高阻导致写入数据被破坏掉,我们把取非送给右244的控制端,而不是直接接在上。
AC:在把计算结果回存到AC时,结果会在T4到达AC;在从导入AC时,也是T4到达AC。因而可以把C9·T4接AC的使能G。
DR1:当有数据要经过DR1时,都会在T2已经到达DR1,所以可以把C8·T2接DR1的使能G。
74181的M、S3-S0、直接接控制信号,因为有数据就可以计算,只要在适当的时候把正确的计算结果取出即可。
DR2:由于计算时,数据到达DR1时,T2已用完(即使没用完也不能此时把计算结果导入DR2,因为这样会导致DR的结果跟输入到DR的数据冲突,把破坏掉的数据再次送到74181进行计算),可以在T3导入数据,把C7·T3接DR2的使能G。而回写AC和内存时,在T4又需要DR2输出数据,因此可以把C10 ·(T3+T4)的非接DR2的输出控制。
IR:直接连C14。
μPC<395>的:在CM的输出加上373做寄存器,并把373的使能G接T1,即只有在T1时刻到来,下一条微指令的信号才能散发出来,从而避免了在同一条指令执行的CPU周期中,有两条微命令的信号到达过控制点。
取指时,需要置1,使下一条指令的操作码能够从395进来;执行指令时,需要置0,使执行指令完成后回到取指公操作。所以可以将C15+送,而=T4。
在逻辑上运行过程为:取指令时,取指信号发送到各个控制点,C13=1,T4时置数,把从MM送来的指令操作码送入μPC;在执行指令的T4时钟周期中,T4尚未结束,取指信号已经散发到各个控制点,此时是取指信号,C13=1,T4结束时,下降沿触发μPC置数,刚才的执行指令(同一条执行指令)的操作码又被送入到μPC,导致CM的输出一直在取指公操作和第一条机器指令的微指令之间循环。
开机自动清零:PC和μPC在开机时需要清零,把start取非接PC和μPC的清零端,结果CM输出出错,发现μPC 给地址不稳定,测试start信号,发现start在开机时给一个高电位,随后并非没有信号输出,而是一直处于低电位,因此需要把start取非后送PC的,跟C15+ (原来接μPC的信号)相与送μPC的——因为只有开机时为0,可以给出清零信号,随后一直为H,跟原来的信号相与,不改变以前的信号控制。
运行结束自动停止:把多余的一位控制信号作为stop信号,送时序的stop接口,即只有stop微指令的C16为1,stop的微指令为1000 1100 0001 0010,机器指令的最后一条加上stop 的操作码,就可以自动停止时序的产生,从而产生停机效果。但是这样会导致运行完一次程序之后,需要重启或者手动给一个置C15为0的操作,因此可以把C16·T3送stop,这样就既可以停止时序的产生,又可以保证停止时序之后,stop置低,从而保证可以在此运行此程序,也避免了当开机时373随机输出C16为1时需要手动操作的麻烦。
- 测试:
首先进行测试的是3+4-5,这个测试比较简单,不过确实实现时间最长的一个测试,因为只要当一切都被设计好了后才能进行这个测试,所以直到第二周三晚上我才把3+4-5做完,可一做完,结果成功显示在面板上时我就知道我成功了。因为实现3+4-5后,其他的表达式都只是重新输入2816和6116内容了,只是机械劳动,所有的技术性问题已经解决了。最后测试的式子是(15+16)×2取非与7减二。答案是全一。
2816内容:
地址 |
内容 |
指令 |
0000 |
0000 1000 0010 1110 |
取指公操作 |
0001 |
0101 1111 1111 0000 |
LDAC |
0010 |
1001 1011 1111 0000 |
ADD |
0011 |
0110 0011 1111 0000 |
SUB |
0100 |
0111 1111 1111 0000 |
或 |
0101 |
0000 1110 1111 0000 |
非 |
0110 |
1101 1111 1111 0000 |
与 |
0111 |
1111 1010 0101 0000 |
SAVE |
1000 |
0100 1000 0011 0001 |
STOP |
0111 |
0011 1010 1111 0000 |
×2 |
1000 |
1001 1111 1111 0000 |
同或 |
1001 |
0110 1111 1111 0000 |
异或 |
6116内容:
地址 |
内容 |
指令 |
0000 |
0000 0000 |
|
0001 |
1001 1000 |
LDAC |
0010 |
0101 0100 |
ADD |
0011 |
0000 0010 |
×2 |
0100 |
0000 1010 |
非 |
0101 |
1101 0110 |
与7 |
0110 |
0011 1100 |
SUB |
0111 |
1011 1110 |
SAVE |
1000 |
0000 0001 |
STOP |
1001 |
1111 0000 |
15 |
1010 |
0000 1000 |
16 |
1011 |
1110 0000 |
7 |
1100 |
0100 0000 |
2 |
1101 |
结果 |
|
…… |
0000 0000 |
- 最后上传几张热腾腾的照片吧:
草图,真的很草!!!
手绘的电路连线图:
数据通路部分的连线:
加上控制器部分的时候,这个计算机已经连他爹都不认识了:
最后加上时序就大功告成了:
如果有不懂的童鞋,欢迎来问哦~~我很喜欢交朋友滴!!