《微机原理与应用第二次实验报告.docx》由会员分享,可在线阅读,更多相关《微机原理与应用第二次实验报告.docx(25页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、微机原理与应用第二次实验报告电92 雷云泽 一、实验目的1.了解计算机的组成与各部件的功能2.熟悉简单计算机的指令集,学习编写汇编语言程序和机器码程序3.熟悉各类型指令执行的数据通道4.设计一个8位单周期简单计算机系统二、实验任务包括练习编程、简单计算机系统A、B、C的设计1.编程练习先采用汇编语言格式编写程序,检查程序的思路、流程,在无误情况下,转换成机器码。程序一:完成将两个固定数据(如0x95,0x35E)进行加、减、与、或、比较运算,将结果顺序存放在地址分别为0x200x21、0x220x23、0x240x25、0x260x27、0x280x29的10个RAM单元中。程序二:完成将两个
2、固定数据(如0x95,0x35E)进行加、减运算,将运算结果顺序显示在数码管上,两个结果显示之间需加一定延时(软件延时),可以通过给一个寄存器赋初值,对这个寄存器进行减运算,直至结果为0)。程序三:从键盘输入一个表达式,如149-862=,通过执行ROM中的程序代码,将运算结果显示在数码管上。2.简单计算机系统设计A利用设计的ROM、ROM、ALU、控制器、PC程序指针计数器模块,构成简单计算机系统A,在ROM中存放编程练习中的程序1,并进行系统的仿真和调试,下载到实验板上进行测试、运行。注意在各模块的连接中,需根据各指令数据通路的要求增加多路选择器等部件,避免信号相连时的冲突。3.简单计算机
3、系统设计B在简单计算机系统A的基础上,增加I/O端口及其映射模块、数码管输出接口,将I/O端口及其映射模块中的IO07.0、IO17.0与数码管输出接口的datainL7.0、datainH7.0相连,构成简单计算机系统B。在ROM中存放编程练习中的程序2,并进行系统的仿真和调试,下载到实验板上进行测试、运行。4.简单计算机系统设计C在简单计算机系统B的基础上,增加44键盘输入接口模块,将I/O端口及其映射模块中IO37.0IO57.0分别与键盘输入接口模块的srcL7.0、srcH7.0、dstL7.0、ALUOP7.0相连,构成简单计算机系统C,在ROM中存放编程练习中的程序3,并进行系统
4、的仿真和调试,下载到实验板上进行测试、运行。三、任务实现1.编程练习编程的基本思路是,读取数值之前都要将待存放数据的寄存器清空,可用ANDI实现,对寄存器的操作都需要指定地址,指定地址时可能需要一个空的寄存器加上一个立即数来表示。程序一:ANDI R0,R0,0x00 #寄存器清零ADDI R0,R0,0x95 #赋值第一操作数低八位ANDI R1,R1,0x00 #寄存器清零ADDI R1,R1,0x5E #赋值第二操作数低八位ANDI R2,R2,0x00 #寄存器清零ADDI R2,R2,0x00 #赋值第一操作数高八位ANDI R3,R3,0x00 #寄存器清零ADDI R3,R3,0
5、x03 #赋值第二操作数高八位ADD R1,R0,R1 #低八位不带进位相加ADDC R3,R2,R3 #高八位带进位相加ANDI R0,R0,0x00 ANDI R2,R2,0x00SW R1,R0,0x00 #低八位结果写入0x00SW R3,R2,0x01 #高八位写入0x01ANDI R0,R0,0x00 #寄存器清零ADDI R0,R0,0x95 #以下运算均与加法运算类似ANDI R1,R1,0x00ADDI R1,R1,0x5EANDI R2,R2,0x00ADDI R2,R2,0x00ANDI R3,R3,0x00ADDI R3,R3,0x03SUB R1,R0,R1SUBC
6、R3,R2,R3ANDI R0,R0,0x00ANDI R2,R2,0x00SW R1,R0,0x00SW R3,R2,0x01ANDI R0,R0,0x00ADDI R0,R0,0x95ANDI R1,R1,0x00ADDI R1,R1,0x5EANDI R2,R2,0x00ADDI R2,R2,0x00ANDI R3,R3,0x00ADDI R3,R3,0x03AND R1,R0,R1AND R3,R2,R3ANDI R0,R0,0x00ANDI R2,R2,0x00SW R1,R0,0x00SW R3,R2,0x01ANDI R0,R0,0x00ADDI R0,R0,0x95ANDI R
7、1,R1,0x00ADDI R1,R1,0x5EANDI R2,R2,0x00ADDI R2,R2,0x00ANDI R3,R3,0x00ADDI R3,R3,0x03OR R1,R0,R1OR R3,R2,R3ANDI R0,R0,0x00ANDI R2,R2,0x00SW R1,R0,0x00SW R3,R2,0x01ANDI R0,R0,0x00ADDI R0,R0,0x00ANDI R1,R1,0x00ADDI R1,R1,0x03SLT R2,R0,R1ANDI R3,R3,0x00SW R2,R3,0x00需要注意的地方: 要写入数据的寄存器一定要先用ANDI语句清零 ANDI语句
8、不能用来往寄存器里存数据,应该在寄存器已用ANDI语句清零后再用ADDI语句将已知的操作数存入,不要误写作ANDI。 为了保证执行带进借位加/减法时,进借位信号能够发挥作用,最好将低八位运算和高八位运算写在一起,即ADD的下一行是ADDC,SUB的下一行是SUBC,因为ADDI指令也需要写进借位寄存器,如果在ADD后面有一个ADDI,然后再执行ADDC,则ADDC所接受的进位信号是由ADDI产生的,因而导致加法进位出错。程序二:ANDI R0,R0,0x00 #清空寄存器ADDI R0,R0,0x95 #读入第一操作数低八位ANDI R1,R1,0x00 #清空寄存器ADDI R1,R1,0x
9、5E #读入第二操作数低八位ANDI R2,R2,0x00 #清空寄存器ADDI R2,R2,0x00 #读入第一操作数高八位ANDI R3,R3,0x00 #清空寄存器ADDI R3,R3,0x03 #读入第二操作数高八位ADD R1,R0,R1 #低八位相加ADDC R3,R2,R3 #高八位带进位相加ANDI R0,R0,0x00ANDI R2,R2,0x00SW R1,R0,0x00 #低八位写IOSW R3,R2,0x01 #高八位写IOADDI R2,R2,0x40 #R2寄存器数据为64ADDI R0,R0,0x01 #R0寄存器加1BEQ R0,R2,0x01 #如果R0=R2
10、,则跳过下一行JMP 0x0F #跳到给R0+1那一行ANDI R0,R0,0x00 #以下为减法执行过程,和加法类似ADDI R0,R0,0x95ANDI R1,R1,0x00ADDI R1,R1,0x5EANDI R2,R2,0x00ADDI R2,R2,0x00ANDI R3,R3,0x00ADDI R3,R3,0x03SUB R1,R0,R1SUBC R3,R2,R3ANDI R0,R0,0x00ANDI R2,R2,0x00SW R1,R0,0x00SW R3,R2,0x01需要注意的地方:加减法执行过程都与程序1相似,不同之处是这个程序要求加减法结果能够延时先后出现,思路是首先给一
11、个寄存器赋值(本程序中为0x40,即十进制64),然后将另一个寄存器加1,并与64比较,如果不等于64,则跳回寄存器加1语句并再次比较,如果等于64,则跳过跳转语句,往下执行减法运算语句。在使用BEQ和JMP指令时,有一点必须注意,即ROM的延时问题。在ROM模块的仿真中可以发现,ROM的读取需要时钟上升沿的参与,即在ROM的输入端给出地址后,还必须等待时钟周期上升沿的来到,相应地址的数据才会出现在输出端口。因此当使用指令JMP时,并不会在下一个上升沿就跳转到JMP所指定的指令,而是继续执行JMP的下一行指令,之后才跳转到所希望跳转的指令。BEQ或BNE指令的执行过程也与此类似,并不会在BEQ
12、/BNE指令到来后,在下一个时钟周期就跳转到相应的指令,而是继续执行下一行指令,然后再发生跳转,也因此会跳转到预计指令的下一行,即实际上会跳到PC+imm+2这一行指令,而不是PC+imm+1。因此,在写汇编语言需要用到JMP/BEQ/BNE时,必须在它的下一行添加冗余指令0x0000,以免执行到不想执行的指令,同时,BEQ/BNE跳转的行数也必须算清楚,否则将不会跳转到所希望的那一行。冗余指令0000未在上文列出,需要在代码写好转换成16进制后,手工在相应的指令下添加。冗余指令0000未定义其功能,不会产生任何实际的操作。程序三:ANDI R0,R0,0x00 #寄存器全部清零ANDI R1
13、,R1,0x00ANDI R2,R2,0x00ANDI R3,R3,0x00LW R0,R0,0x06 #将IO6端口的ALUOP读入ADDI R1,R1,0x0A #加号对应的ALUOPBEQ R0,R1,0x10 #跳转到加法运算语句ANDI R1,R1,0x00ADDI R1,R1,0x0B #减号对应的ALUOPBEQ R0,R1,0x1C #跳转到减法运算语句ANDI R1,R1,0x00ADDI R1,R1,0x0C #and对应的ALUOPBEQ R0,R1,0x28 #跳转到与运算语句ANDI R1,R1,0x00ADDI R1,R1,0x0E #比较对应的ALUOPBEQ R
14、0,R1,0x34 #跳转到比较运算语句ANDI R1,R1,0x00ADDI R1,R1,0x0F #or对应的ALUOPBEQ R0,R1,0x3E #跳转到或运算语句ANDI R0,R0,0x00 #加法运算语句,寄存器清零ANDI R1,R1,0x00ANDI R2,R2,0x00ANDI R3,R3,0x00LW R0,R0,0x02 #被加数低八位,自IO2LW R1,R1,0x04 #加数低八位,自IO4LW R2,R2,0x03 #被加数高八位,自IO3LW R3,R3,0x05 #加数高八位,自IO5ADD R0,R0,R1 #低八位不带进位相加ADDC R2,R2,R3 #
15、高八位带进位相加ANDI R1,R1,0x00ANDI R3,R3,0x00SW R0,R1,0x00 #将低八位运算结果输出至IO0SW R2,R3,0x01 #将高八位运算结果输出至IO1JMP 0xFF #跳转至ROM末尾ANDI R0,R0,0x00 #减法运算语句,寄存器清零ANDI R1,R1,0x00ANDI R2,R2,0x00ANDI R3,R3,0x00LW R0,R0,0x02 #被减数低八位,自IO2LW R1,R1,0x04 #减数低八位,自IO4LW R2,R2,0x03 #被减数高八位,自IO3LW R3,R3,0x05 #减数高八位,自IO5SUB R0,R0,
16、R1 #低八位不带借位相减SUBC R2,R2,R3 #高八位带借位相减ANDI R1,R1,0x00ANDI R3,R3,0x00SW R0,R1,0x00 #将低八位运算结果输出至IO0SW R2,R3,0x01 #将高八位运算结果输出至IO1JMP 0xFF #跳转至ROM末尾ANDI R0,R0,0x00 #与运算语句,寄存器清零ANDI R1,R1,0x00ANDI R2,R2,0x00ANDI R3,R3,0x00LW R0,R0,0x02 #第一运算数低八位,自IO2LW R1,R1,0x04 #第二运算数低八位,自IO4LW R2,R2,0x03 #第一运算数高八位,自IO3L
17、W R3,R3,0x05 #第二运算数高八位,自IO5AND R0,R0,R1 #低八位相与AND R2,R2,R3 #高八位相与ANDI R1,R1,0x00ANDI R3,R3,0x00SW R0,R1,0x00 #将低八位运算结果输出至IO0SW R2,R3,0x01 #将高八位运算结果输出至IO1JMP 0xFF #跳转至ROM末尾ANDI R0,R0,0x00 #比较运算语句,寄存器清零ANDI R1,R1,0x00ANDI R2,R2,0x00ANDI R3,R3,0x00LW R0,R0,0x02 #第一运算数低八位,自IO2LW R1,R1,0x04 #第二运算数低八位,自IO
18、4LW R2,R2,0x03 #第一运算数高八位,自IO3LW R3,R3,0x05 #第二运算数高八位,自IO5SLT R2,R2,R3 #高八位执行比较运算ANDI R1,R1,0x00ANDI R3,R3,0x00SW R2,R3,0x00 #将比较结果输出至IO0JMP 0xFF #跳转至ROM末尾ANDI R0,R0,0x00 #或运算语句,寄存器清零ANDI R1,R1,0x00ANDI R2,R2,0x00ANDI R3,R3,0x00LW R0,R0,0x02 #第一运算数低八位,自IO2LW R1,R1,0x04 #第二运算数低八位,自IO4LW R2,R2,0x03 #第一
19、运算数高八位,自IO3LW R3,R3,0x05 #第二运算数高八位,自IO5OR R0,R0,R1 #低八位相或OR R2,R2,R3 #高八位相或ANDI R1,R1,0x00ANDI R3,R3,0x00SW R0,R1,0x00 #将低八位运算结果输出至IO0SW R2,R3,0x01 #将高八位运算结果输出至IO1JMP 0xFF #跳转至ROM末尾需要注意的地方:首先必须把从键盘模块传来的ALUOP,即运算符存入寄存器中,然后给另一个寄存器赋加、减、与、或、比较相应的运算符,将存有ALUOP的寄存器分别与这些寄存器中的值进行比较,若相等则跳转到相应的代码处,每种运算的代码都在结束处
20、有一条指令JMP 0xFF,跳转到ROM的末尾,以避免执行到别的代码。而程序计数器指针模块PC在设计的时候,令其在计数到达0xFF时就不再计数。因此ROM中的程序只会执行一次。无论执行哪一种运算,最后都在执行完以后跳转到ROM末尾并保持停止。另外,在使用JMP/BEQ/BNE指令时仍需注意延迟的问题,必须在它们的下一行添加冗余指令0000,同时跳转行数的计算也必须将冗余指令所在的行考虑在内。2.简单计算机系统设计A/B【模块连接】首先,必须将已经设计好的模块按其功能和数据流向组装起来。由于任务A和B所使用的模块相似,因此将这两个任务一起完成。待组装的模块有:PC程序指针计数器模块instrco
21、nunit:程序存放ROM模块rom_256_16:控制器模块ctrlunit:寄存器组模块reg4_8:运算器模块ALU:标志寄存器flag:IO端口映射模块IO_PORT:下面分别就每一个模块的连接情况进行描述 程序指针计数器模块instrconunit程序指针计数器模块必须接受时钟信号,和控制器模块所给出的跳转信号BRANCH,JUMP,以及立即数imm,此外由于PC模块计数到255后就停止不动,因此必须有一个复位信号,使得每次CPU执行新运算时,PC模块都要从0开始计数,因此PC模块有一个同步复位端RESET,在任务A/B中暂时用不上。跳转信号BRANCH和JUMP都由控制器模块根据指
22、令给出,因此连接到控制器模块相应的输出端口上,imm是立即数,在BRANCH=1时发挥作用,要接到程序存储模块rom256_16的输出端的低八位。它的输出端是一个八位二进制数,作为程序存储模块的地址输入,接到rom256_16的输入端。 程序存储模块rom256_16ROM中的数据是在编译时通过指定的hex文件写入的,在下载到实验板上形成系统后不可更改,因此需要在生成ROM模块时指定.hex文件,并将编好的程序放入其中。此外,ROM需要一个时钟信号输入,跟PC模块共用同一个时钟信号即可,事实上,在本系统中,CPU内部所有需要时钟的模块都使用同一个时钟信号,这样CPU内部模块都工作在同一个频率下
23、,避免时钟不匹配可能会产生的一些问题。地址输入端接PC模块的地址输出。ROM模块的输出即为一条条指令,控制着整个系统的工作,因此它的连接十分关键。我们不妨先分析一下指令的结构可见,16位指令的高四位,即q15.12是操作码,标志着这是哪一种指令,而q11.10,q9.8在R型和I型指令中是寄存器的地址,在J型指令中无意义,q7.6在R型指令中是目标寄存器的地址,在I型和J型指令中是立即数的一部分,q5.0在R型指令中无意义,在I型和J型指令中是立即数的一部分。ALU的运算数有时全部来自寄存器,有时来自立即数,说明ALU的输入端必须有一个多路开关对这两者进行选通,同时,控制单元必须输出相应的选通
24、信号使得ALU接收到正确的数据。另外,目标寄存器的地址有时是q7.6(R型指令),有时是q9.8(I型指令),所以在寄存器组模块的写入地址端也必须有相应的多路开关进行选通。另外,寄存器组模块的输入数据有时来自ALU,有时来自RAM(IO),因此也需要多路开关。指令的具体连接在各个模块中分别讲述。 控制器模块ctrlunit这个模块通过接收指令的高四位,即操作码,来判断执行的是什么指令,并且发出相应的控制信号,调动各个模块正确地工作。它有三个输入端,OP3.0是操作码输入端,接ROM模块输出端的高四位q15.12,ZERO是零信号输入端,当ALU运算结果为0时,会输出一个ZERO=1的信号,接到
25、控制器模块的ZERO端口,用来判断是否满足BEQ/BNE指令的跳转条件。en是使能端,始终接高电平。输出一共有九个控制信号,输入为J型指令的JMP时,JUMP=1,使得PC模块在下一个时钟上升沿到来时跳转到imm(立即数)所指定的值,因此JUMP接PC模块的JUMP端口。BRANCH接PC模块的BRANCH,当BRANCH=1时,PC=PC+1+imm,即要跳过imm条指令,BRANCH=0时,PC正常跳转。ALUC2.0接ALU的运算符输入端口ALUC2.0,控制ALU执行何种运算,在本例中,ALU可以执行七种运算,不带进位加,不带借位减,与,或,带进位加,带借位减,比较。因此ALUC2.0
26、也有七种值,分别对应七种运算。ALUSRCB接ALU第二操作数多路开关的选通信号输入端,如下图所示ALUSRCB=1时,多路开关将q7.0,即立即数送到输出端,这时ALU的第二操作数来自立即数,易知执行的一定是I型指令。ALUSRCB=0时,多路开关将Q27.0送到输出端,ALU的第二操作数来自寄存器。WRITEMEM是RAM(IO)写允许信号,接IO端口映射模块IO_PORT的写允许WE端口,WRITEMEM=1时,IO模块处于可写状态,WRITEMEM=0时则不可写。只有当执行SW指令时WRITEMEM才为1.WRITEREG是寄存器写允许信号,接寄存器组模块的写操作使能端REG_WE,当
27、WRITEREG=1时,寄存器处于可写状态,在时钟信号作用下,将数据输入端DI7.0的数据写入ND1.0所指定的寄存器中。当WRITEREG=0时,寄存器不可写。只有当执行R型指令和I型指令中的ANDI,ORI,ADDI,LW指令时,WRITEREG才为1.MEMTOREG是从IO到寄存器的数据与ALU运算结果的选通信号,因为写入REG的数据有时来自ALU运算结果,有时来自IO,因此必须设置控制信号MEMTOREG来选通。如图当MEMTOREG=1时,多路开关选出ramq7.0送到输出端DI7.0,这个数据来自IO,当MEMTOREG=0时,则选出S7.0,这是ALU的运算结果。多路开关的输出
28、DI7.0接寄存器组模块的数据输入端DI7.0。只有当指令为LW时,MEMTOREG才为1.REGDES是目标寄存器地址选通信号,接多路开关选通端,对R型指令的目标寄存器地址q7.6和I型指令的目标寄存器地址q9.8进行选通。当REGDES=1时,选出q7.6,当REGDES=0时,选出q9.8。输出端接寄存器组模块的写地址输入端ND7.0。当执行R型指令时REGDES=1,除此之外REGDES=0.WRFLAG是标志寄存器写允许信号,接在标志寄存器模块的使能端。当WRFLAG=1时,标志寄存器可写,把ALU输出的carry_out写入。WRFLAG=0时,标志寄存器不可写。当执行的是ADD,
29、SUB,ADDI,ADDC,SUBC时WRFLAG=1,其他指令时为0. 寄存器组模块reg4_8寄存器组模块有两个读地址端口和一个写地址端口,即N11.0,N21.0和ND1.0,分别接指令输出的q11.10,q9.8,和写入地址选通模块的输出(前文已述),输出Q17.0和Q27.0则是读地址N11.0和N21.0所指定的寄存器的内容,由于读是异步的,因此只要N1和N2给出的地址发生改变,则输出端Q1和Q2的值也会马上改变,不需要时钟信号的参与。然而写入操作则是同步的,需要时钟信号的上升沿。在已经接好了写允许信号WRITEREG,待写入数据也已经通过多路开关的选通接到了DI7.0上之后。必须
30、考虑写入数据时的时钟配合问题。以R型指令ADD R0,R1,R2为例,从理论上来看,当这条指令到达后,寄存器组模块的输出端Q1,Q2会马上给出R1,R2寄存器的值,因为读是异步的,然后这两个值被送到ALU的操作数输入端,同时控制器模块也异步地给出了运算符,由于ALU是组合电路,所以马上得到运算结果,通过选通模块的选通(也是组合电路)回到寄存器组模块的数据输入端DI,如果不考虑传输延迟的话,这个指令一出现,马上待写入数据就会出现在REG的输入端。这样,在下一个时钟周期到来时,数据就会被写进去。但是我们必须考虑到传输延迟时间,事实上,当下一个周期到来时,同时指令也转向了下一条,所有组合电路所给出的
31、值都将发生改变,也就是REG输入端口的待写入数据会被下一条指令所生成的数值所取代。如果时钟周期的上升沿在数据消失之后才抵达,那数据就不会被写进寄存器,因此,寄存器组模块的时钟直接接PC模块的时钟是不安全的。为了解决这个问题,最简单的办法就是给寄存器组模块的时钟加上一个反相器,如下图所示。这样,当时钟周期上升沿来到时,对于寄存器组模块来说是一个下降沿,不会触发数据的写入。数据和写允许信号来到后,经过半个周期,时钟周期的下降沿来到,对寄存器来说是上升沿,数据被写入,由于经过了半个周期,数据已经稳定下来,同时离下一条指令的到来还有半个周期,寄存器输入端的待输入数据和写允许信号在写入的过程中都能保持稳
32、定存在。这样就能确保数据的写入是有效的。事实上,所有需要写入数据的模块,我都采取了类似的方法,即给时钟加上反相器后再连接到模块的时钟输入端口。这个方法的要点其实就是把数据和写允许信号的到来与写操作错开半个周期。输出端的Q1接到ALU的第一操作数输入端口,Q2与立即数q7.0经过选通后接到ALU的第二操作数输入接口。图中的reg1reg4是仿真时用来观察寄存器数值的,实际上并不需要。 标志寄存器flagFlag用来储存ALU产生的进/借位信号和零标志信号,但是这两个信号加起来只有两位,而FLAG的输入端不能悬空。因此将其他几位都接地,再与FLAG的输入端相连,与寄存器组模块类似,flag的时钟信
33、号也加有反相器,确保写入的有效性。从Flag的输出端接出进/借位信号,接到ALU的进/借位输入端上。如图。 IO端口映射模块IO_PORTIO模块是数据输入输出的通道,它的对外端口都是双向的,既可以输入,也可以输出。但实际上,在本系统的设计中,8个IO端口是分管输入输出的,IO2到IO6这5个IO端口分别负责从外部模块接收srcL,srcH,dstL,dstH和ALUOP,因此应该连接到键盘扫描模块的相应数据输出端口,但因为任务A和B尚未与键盘模块相连,因此暂时将它们分别与输入端口相连。而IO0和IO1则负责对外输出运算结果,但是IO_PORT的内部是组合电路,自身没有储存数据的功能,一旦数据
34、消失,IO端口就成为高阻态,因此必须给IO0和IO1接一个8位寄存器,同flag,REG等单元一样,寄存器的时钟也接上了反相器,寄存器的使能端,则使用所接IO端口内部三态门的开启信号,注意不能用IO_PORT的写允许信号WE(即WRITEMEM),因为无论写哪一个IO端口,WE都等于1,这样当写IO0寄存器时,IO1寄存器也处于写允许状态,而这时IO1处于高阻态,会将高阻态写进寄存器中而不是保持IO1寄存器原来的值。输出端口Dout7.0则将从IO端口读取的数据输送出去,事实上,这个端口只在LW指令执行时起作用,根据LW指令中的操作数,决定从哪个IO端口(IO2-IO6)读取数据,然后将它送到
35、Dout端,随后经过多路开关与ALU计算结果S7.0进行选通后送到寄存器数据输入端口(前文已述)。输入端,Din7.0是待写入IO端口寄存器的数值,这个端口只在SW时才起作用,SW执行时将Din写入IO0端口寄存器或IO1端口寄存器,Din来自寄存器组模块,接Q27.0。RE和WE分别是读允许和写允许信号,分别接控制器单元的MEMTOREG和WRITEMEM。地址输入端口addr7.0接ALU运算结果输出S7.0。输出端的io_write0和io_write1分别是IO0和IO1端口寄存器的使能信号,接到寄存器的使能端。io_read则是内部直接从MEMTOREG引出来的,在与键盘模块连接时将
36、要用到。连接情况如下图最后,连接模块时并没有用到RAM模块,因为这样连接好以后,数据的输入和输出都有来源和去向,用不到RAM模块,而且RAM模块有一个比较严重的缺陷,就是读取不是异步的,即不是给一个地址就马上能在输出端产生相应地址的数据,而是需要时钟周期上升沿的参与,这样就会产生一个周期的延时,不利于指令的时序配合,另外RAM模块只有一个输出端,而要输出的结果有低八位和高八位,这样在CPU外部还需要两个寄存器分别储存低八位和高八位运算结果,还不如直接集成到IO模块的输出端。【仿真波形】任务A和B都是将给定的两个数进行一定的运算然后输出结果,因此不需要从IO端口读入数据。仿真时可以不用在IO2-
37、IO6的端口加入数据。将任务A的16进制代码写入.hex文件中,进行时序仿真,得到如下波形:可见,输出端依次出现了任务A中执行的五种运算的结果。0x95和0x35E写成16位二进制分别是 和 ,进行加法后结果为 ,与仿真波形吻合。执行减法时,由于ALU设计时,如果被减数比减数小,则反向相减,因此执行减法的结果应为 ,也与波形吻合,至于减法的正确结果,留待任务C中处理.与运算结果为 ,与波形相同。或运算结果为 ,也与波形相同。比较运算结果直接输送给低八位,因为第一操作数比第二操作数小,所以结果应为1,也与波形相同,至于高位的则是因为未将上次运算结果清空所致。将任务B的16进制汇编代码写入.hex
38、文件中,全编译后做时序仿真,得到波形如下在所写的汇编语言中,先给寄存器R2赋值0x40,然后再给空寄存器R0加1,然后执行条件跳转语句BEQ,与R2比较,若相等则跳过下一行的无条件跳转语句JMP,执行下面的减法运算;若不相等,则不满足条件跳转语句BEQ的条件,执行下一行的无条件跳转语句JMP,跳回给R2加1的语句。这样R2不断从0加到0x40,实现了两种运算的延迟显示。从仿真波形可以看出,reg1的值不断增加,一直到0x40之前,输出寄存器的值都是加法运算的结果: ,直到加到0x40以后,开始执行减法运算语句,输出变为减法结果: (未进行进一步处理,高八位和低八位分别运算)。说明程序执行正确。
39、3.简单计算机系统设计C【模块连接】上文中,设计的计算机系统实际上已经可以执行任何写成的汇编代码,包括任务C的代码,但是,只能在仿真时执行,数据必须手工添加到输入端口,输出的结果也只能在仿真结果中查看。下一步就是为这个CPU添加相应的输入输出模块,并进行引脚锁定,下载到实验板上观察实际运行结果。在小学期中,已经设计了键盘扫描模块key_scan,它可以根据矩阵键盘的输入,提供运算符和两个操作数,并且还能识别输入的是数字、运算符还是等号,进行相应的状态跳转。当算式输入完成按下等号时,它还能给出握手信号,可以提供给CPU作为开始运算的标志。另外,在输入第一运算数、第二运算数以及按下等号后,键盘扫描
40、模块处于不同的状态,可以引出来作为显示模块用来对信号进行选通,确定现在数码管应该显示第一运算数、第二运算数还是运算结果。它需要一个时钟信号来驱动,易知这个时钟应该比CPU的时钟慢,这样才能保证数据能够有效地提供给CPU,而且高速的CPU驱动低速的周边设备也是计算机系统的设计原则。另外,由于在之前的设计中,键盘扫描模块给出的是BCD码,因此必须用一个组合电路将它转换为标准二进制码。在将数据提供给IO端口映射模块的过程中,如果将键盘扫描模块的输出转换为二进制后直接接IO端口,可能会与内部的三态门冲突引起编译错误,需要通过一组三态门缓冲器再与IO端口连接,三态门的使能端可以选用IO端口映射模块的读允
41、许信号io_read。输出方面,由于ALU设计时减法运算方面,对于被减数小于减数的情况,进行反向相减,因此对于16位减法,需要将两次执行减法时的运算结果和借位信号都引出来,并且用一个模块进行再处理。这样就得到了最终的运算结果,接着要把它转换为BCD码,这样才能被数码管显示模块使用。最后将第一运算数、第二运算数和运算结果的显示信号进行选通,并进行引脚锁定。键盘扫描模块如下图所示,下面分别讲述每个端口的功能和连接。输出端口中的SRCH7.0,SRCL7.0,DSTH7.0,DSTL7.0,ALU_OP7.0分别输出第一操作数的高低八位,第二操作数的高低八位以及运算符。除ALU_OP外,四个运算数都
42、与BCD转二进制模块bcd_bin连接,将它们转换为纯二进制。以下是这个模块的Verilog代码:module bcd_bin(en,srcL_in,srcH_in,dstL_in,dstH_in,srcL_out,srcH_out,dstL_out,dstH_out); input 7:0 srcL_in; input 7:0 srcH_in; input 7:0 dstL_in; input 7:0 dstH_in; input en; output reg 7:0 srcL_out=8b; output reg 7:0 srcH_out=8b; output reg 7:0 dstL_o
43、ut=8b; output reg 7:0 dstH_out=8b; reg 15:0 src=16b00000; reg 15:0 dst=16b00000; always (en) begin src=srcH_in7:4*1000+srcH_in3:0*100+srcL_in7:4*10+srcL_in3:0; dst=dstH_in7:4*1000+dstH_in3:0*100+dstL_in7:4*10+dstL_in3:0; srcL_out=src7:0; srcH_out=src15:8; dstL_out=dst7:0; dstH_out=dst15:8; endendmod
44、ule连接情况如下图所示。然后,将二进制的四个操作数以及ALU_OP连接到一组三态门缓冲器上,再与IO2-IO6五个IO模块相连接,三态门缓冲器的使能端用IO_PORT输出的io_read信号,即控制器模块发出的MEMTOREG信号,这样在往REG里读取数据时,这组三态门缓冲器和IO_PORT内部的三态门缓冲器同时打开,将数据输送到REG的数据输入端。注意,不能直接在三态门缓冲器的使能端接高电平,期望三态门始终开放,这样做会引起报错。连接情况如下图键盘扫描模块输出端的finish是握手信号,告诉CPU什么时候开始运算。输入第二个操作数时finish=1,这样PC模块就被清零,并且在输入第二个操
45、作数期间始终保持为0,按下等号后,finish恢复为0,这时key_scan的输出端口已经准备好了两个操作数和一个运算符,PC模块开始计数,并根据指令的执行进行跳转,CPU开始运算,完成运算后,PC指针停留在255并保持不动,直到下一个握手信号的到来。因此,finish接在PC的reset端。计算完成后,结果要输出给IO0和IO1的端口寄存器,如上文所说,为了得到减法运算的真实结果,必须将两次减法运算的借位信号也引出并储存起来,并且用一个模块进行处理。然而,CPU中的标志寄存器只能储存某一次的进/借位信号,后一次运算产生的进/借位信号会将前一次的刷新。因此必须使用两个寄存器分别储存两次运算的借位信号,并且,它们的使能端必须分开,不能采用CPU内部标志寄存器所用的使能信号WRFLAG,否则,两个寄存器的值总是一样的。因此,考虑使用指令来控制两个借位寄存器的可写性,当执行SUB指令时让第一个借位寄存器可写,而第二个不可写,执行SUBC指令时让第一个不可写,第二个可写,就可以实现两次运算借位信号的分别存储。其中使能信号输出模块flagen的Verilog代码如下: