胡世昌
(沈阳师范大学 软件学院,沈阳 110034)
“不闻不若闻之,闻之不若见之,见之不若知之,知之不若行之。学至于行之而止矣。”在硬件课程教学中,人们一直在探索更好的实验改革方案[1-3]。对于计算机组成原理课程教学,“行之”的境界,应该是能够设计一台简单的计算机。这种设计应该是从“0”和“1”开始,根据数字电路基础知识,结合计算机组成原理课程的内容,实现满足要求的简单计算机,而不应是半成品的简单连接和验证。这种实现同样不应该受限于任何教材,讲解《计算机组成原理》时,有的讲解顺序是运算器→存储器→控制器[4],有的讲解顺序是存储器→运算器→控制器,先设计哪一个环节,都不能影响最终设计出一个简单计算机这样的目标。综合考虑成本、方便性,logisim-evolution仿真软件(https://github.com/reds-heig/logisim-evolution)是一个不错的选择。
机器的指令集选择图灵完备的单指令SUBLEQ[6]来实现算术运算和转移,单指令集计算机[7-8]适合教学。为简化设计,把减法指令分成有跳转(SJ)和无跳转(SN)2条指令,用与非指令(NA)实现逻辑运算[9],共需实现3条运算指令。
由于存储系统具有地址线16根、数据线8根,如果采用直接寻址,2个操作数的地址就需要访存4次才能取回对应地址的4个字节,为了减少存储器的访问次数、缩短运算指令的长度,采用基址寻址,为此需要增加设置基地址指令(BA)。
由此得到了必须具备的4条指令,需要2位二进制码对这4条指令进行编码,如表1所示。
表1 指令编码
“无跳转减”运算指令SN、“与非”运算指令NA的长度都是1个字节,指令格式为pp aaa bbb, D7D6两位(pp)表示操作码,D5D4D3三位(aaa)是A操作数的形式地址,D2D1D0三位(bbb)是B操作数的形式地址,它们作为有效地址的低3位,与基地址共同构成16位有效地址,基地址仅仅负责提供有效地址的高13位。“设置基地址”指令(BA)是3字节指令,它的3个字节分别是(x表示0或1):00xxxxxx,基地址低字节,基地址高字节。Subleq A,B,C是3字节指令,它的3个字节分别是:01aaabbb,C地址低字节,C地址高字节。
需要3个字节,第1字节的二进制表示为00xxxxxx,其中x表示该位取值0或1;采用低字节在前的方法表示数字,所以第2字节是00(H),第3字节是10(H)。于是得到设置基地址指令的十六进制表示为00 00 10(H)。
指令目标是1000地址单元的内容和1001地址单元的内容做减法,结果放在1001地址单元,不跳转。它是1字节指令,对应的二进制表示为10 000 001,转换成十六进制表示为81(H)。
指令系统中没有专门的停机指令,可跳到预先设定的某个循环地址,如0FFD,让机器进入无限循环。
2.3.1 设置基地址为0000
这是一个3字节指令,代码为00 00 00(H)。
2.3.2 无条件跳转到0FFD地址
利用Subleq指令实现,具体方法是让0000地址单元的内容减去0000地址单元的内容,这个结果显然必定为零,从而发生跳转。又因为0000地址单元在ROM中,所以不可能真正写入,也就不会改变程序。指令所对应的3个字节是:第1字节包含操作码,其二进制代码为01 000 000,对应十六进制40(H);第2字节包含跳转的低位地址,这里为FD(H);第3字节包含跳转的高位地址,这里为0F(H)。于是得到无条件跳转指令的十六进制代码为40 FD 0F(H)。
2.3.3 完整测试代码
实现“(1000)-(1001)→ 1001功能,之后无限循环”的整个程序代码由10个字节构成,内容是00 00 10 81 00 00 00 40 FD 0F(H)。
实现上述功能的算术逻辑单元只需8位减法器和8位与非门即可,运算方式选择输入端S0N1为0时做减法运算,为1时做与非运算;输出F表示运算结果,状态标志leq在运算结果小于或等于0时置1。
这个ALU虽然简单,但需要译码、寻址,所以能够作为实现的例子来使用,而且给学生留下了更多的改进空间,可以去实现更加复杂的ALU。
ALU需要2个输入寄存器保存输入数据,分别称为A和X,它们可以分别用来保存参与运算的2个操作数A和B。为保存Subleq的跳转目的地址C,设置专用寄存器CL和CH,CL用于保存C的低字节,CH用于保存C的高字节,它们连接到程序计数器PC的数据输入端,以便改变PC的值。
对应设置基地址指令base,相应设置了基址寄存器,基址寄存器的位数最少应该是13位,与指令中所给出的低3位结合,才能访问所有的内存空间。为了设计上的方便,本CPU中采用16位基址寄存器(低3位不用),对应2个8位寄存器BAL和BAH,其中BAL用于保存基地址的低字节,BAH用于保存基地址的高字节。
有效地址的高13位来自寄存器BAL和BAH,低3位来自指令中的aaa和bbb部分。实现电路如图2所示,有效地址的低3位由指令寄存器中直接给出,高13位则由BAL和BAH给出,共同形成A操作数和B操作数的有效地址。
这样就需要6个寄存器:A、X、CL和CH、BAL和BAH。此外,还需要指令寄存器IR、程序计数器PC和内存地址寄存器MAR。由于例题相对简单,内存数据寄存器MDR可以不用。
指令译码器是一个2线-4线译码器,指令寄存器(IR)的最高2位作为指令译码器的输入,指令译码器的4个输出分别代表BA、SJ、SN、NA指令,高电平有效。例如当BA=1时,表明指令译码器中保存的是设置基地址指令。
完整的CPU如图2所示,包括ALU、寄存器、指令译码器和控制单元CU(控制器)。控制单元的输入包括复位、时钟、使能信号、指令译码器的输出信号BA、SJ、SN、NA和ALU的状态标志leq,输出是用于三态门和寄存器的控制信号。为控制数据的流动,添加了必要的三态门,并给各个寄存器提供写使能控制信号,这些控制信号的来源是控制单元的输出。
确定了输入和输出信号之后,就可以设计控制器。控制单元要在时钟的控制下,根据不同的输入信号,有序地输出一系列相应的控制信号,控制ALU、寄存器、存储器和I/O设备去执行一系列的动作(微操作),来完成输入信号所规定的功能。所以设计控制单元的第一步,就是确定不同输入信号所对应的一系列微操作。
规定寄存器和RAM都由时钟上升沿触发,所有控制信号都是高电平有效。
第1个时钟(T0)上升沿之后,给出MARi信号,MAR寄存器准备接受内存地址输入。
第2个时钟(T1)上升沿之后,内存地址写入MAR,写入后内存地址才能出现在地址总线(AB)上。同时可以给出读内存命令(R1_W0=1,MEMREQ0=0)。
第3个时钟(T2)上升沿之后,“读内存”命令发挥作用,内存单元的内容被读出到数据总线(DB)上。需要注意的是,如果读信号在第3个时钟上升沿到来时已经消失(R1_W0=0,MEMREQ0=0),将会变成“写内存”命令,所以必须确保第3个时钟上升沿到来时,读信号依然有效。解决方案是把读命令加宽为2个时钟周期,在第2、3节拍中都确保读命令(R1_W0=0,MEMREQ0=0)有效。
本机只有取指和执行2个阶段,可用T触发器构成工作阶段寄存器STG,当STGi=1时,T触发器翻转。F0E1=0表示取指阶段,F0E1=1表示执行阶段;经1输入2输出译码器译码后,FE=1表示取指阶段,EX=1表示执行阶段。对应电路如图3所示。
7.3.1 取指令第1字节
表2 取指周期-读入第1个字节
7.3.2 根据指令操作码确定后续操作
T2上升沿,指令码进入CPU内部DB,还没有进入IR,此时不能译码;T3上升沿,指令码才能进入IR,所以在T3节拍才可以得到译码结果,并根据译码结果,从T3节拍开始执行不同的微操作序列。
T3~T8节拍中各个指令的微操作如下:
1)设置基地址指令(BA):从内存中取出2字节地址存入基址寄存器,之后进入下一条指令的取指周期,如表3所示。
表3 取指周期-设置基地址
2)Subleq 指令(SJ):从内存中取出2字节地址存入C寄存器,之后进入执行周期,完成减法运算(表4)。为了标识CPU进入了执行周期,设置STGi来改变执行阶段寄存器STG的值。
表4 取指周期-Subleq
3)无跳转减指令SN、与非指令NA:在T3~T8节拍中没有具体操作,只需更改STG即可(表5)。
表5 取指周期-SN和NA
BA指令没有执行周期,SJ、SN、NA指令的执行周期基本相同,如表6所示。
表6 执行周期
综合取指周期和执行周期的微操作信号表,可得到控制信号的逻辑表达式,例如
STGi=FE·(SJ·T8+(SN+NA)·T8)+
EX·(SJ+SN+NA)·T8,
PCi=EX·SJ·T8·leq。
其他表达式不一一列举。根据表达式,就可以得到相应的组合逻辑电路图,例如STGi以及设置指令周期状态的寄存器如图4所示。
首先根据前文分析中所得到的控制信号出现的先后顺序,写出控制存储器中每一个地址单元的内容,如表7所示。根据表中所列的控制信号,可知控制信号需要17位;因为控制存储器的地址编号有27个(对应表中的编号0~26),可知下地址字段需要5位;二者相加,可知控制存储器的位宽应该是22。
表7 微程序控制器的代码
表中第2列表示指令当前所处的工作阶段,其中FE表示取指周期的第1阶段(取指令的第1字节)。组合逻辑控制器的FE阶段只需要3个时钟周期,微程序控制器的FE阶段却需要4个时钟周期,这是因为组合逻辑电路可以在1个时钟周期(T3)内完成译码以及根据译码结果确定相应微操作的工作,而微程序控制方案中则需要2个时钟周期,前一时钟周期完成译码并根据译码结果确定下一个微地址,在下一时钟周期才能把所确定的微地址写入控制存储器CMAR(这是由存储器的工作特性所决定的)。
下地址字段决定了下一条指令的地址,用来修改CMAR,其含义有如下几类:(1)第3行表示根据指令寄存器中的操作码字段的译码结果决定下地址,BA转地址4,SJ转地址10,SN转地址17,SA转地址18。(2)第26行表示如果当前指令是SJ指令且计算结果小于等于0(leq),则转地址16(给PC赋值,以便实现指令跳转);否则直接转CMAR地址0(取下一条指令)。(3)转下地址字段给定地址(当前地址加1,或地址0、或地址19)。
控制存储器(控存)的下一条指令地址来自CMAR寄存器,其来源有3个:(1)当前地址为3时,选择来自指令操作码所决定的地址(4、10、17或18);(2)当前地址为26、指令为SJ、且leq为真时,选择地址16;(3)其余情况,直接来自控存的下地址字段。
当前地址为3和26的两种情况,需要借助硬件实现,如图5所示。
其余情况则直接使用下地址字段即可。由此可得控制存储器每个单元内容,如0地址单元的内容为10 0100 0000 0000 0000 0001(B),或者240001(H);1地址单元的内容为00 0011 0000 0000 0000 0010(B),或者030002(H);其他地址单元的内容依次类推。把所得到的每个地址单元的内容写入控制存储器,就完成了微程序控制器的设计。
各部件设计完毕以后,连接起来,就构成了一台具有CPU和存储器的简单的机器。在步骤3已经设计好了测试方案,只需运行测试代码即可验证所设计的计算机是否正确。在0000地址单元的起始部分(这里是ROM)填入用十六进制表示的指令序列00 00 10 81 00 00 00 40 FD 0F,并在1000地址单元存入数据A,1001地址单元存入数据B,运行程序,运行后应该在1001地址单元得到A-B的结果。如果结果正确,就说明这个由10个字节组成的程序确实实现了(1000)-(1001)→1001这个功能;之后机器进入无限循环。
本设计侧重于介绍一种低成本的、直观的方法,学会从基本逻辑电路开始,快速地设计一台简单计算机,它服务于《计算机组成原理》课程的教学,仅以计算机的设计和实现为目的,对运算程序在空间和时间上的资源消耗未加考虑。
在课程教学中,给了学生3种选择:独立设计(自行设计一个简单的计算机,指令集不要求完备)、读懂老师给的设计并改进、单纯读懂。要求不论是设计还是改进,都不能重复。43名学生中,在选择环节有8名选择了独立设计,2名选择改进,33名选择了单纯读懂;最终完成时,有2名学生放弃了独立设计。这说明,要求所有学生掌握计算机的设计还不现实,更加复杂的指令系统的设计暂时还不宜引入。
在计算机组成原理课程中,使用logisim-evlution设计简单计算机,有益且可行。在条件允许的情况下,本设计可以有更多改进的地方,比如考虑使用多总线、更多的通用寄存器、RAM采用下降沿触发、仿真cache的实现、增加指令等。在逻辑电路的实现过程中,会发现根据逻辑表达式画逻辑图这个步骤,可以借助自动化工具来完成,从而在时间允许的条件下引入硬件描述语言实现。