计算机组成原理-Verilog课程设计制作8位CPU简析

前言

        课设时间已经过去一周了,有人还是不甚了解自己在写什么(QwQ),我说一些我对需要我们做的部分的见解吧。整个文章分为模块篇、信号篇、top连线篇和查错篇。(很多人不会以为我只会传C++的实验吧)

一、模块篇

        Verilog语言和常规语言有很大的不同,常规语言我们做的是一个顺序执行的过程,从头往后写就完事了,Verilog,与其说是语言,不如说是“电路连线模拟器”。

        连线之前我们应该干什么?应该了解每个模块的作用,先写模块。很多人上来就跟风写信号,最后发现信号改了又改加了又加,那肯定更容易出问题。

 

         根据这个数据通路,我们可以整理一下,M是ram、rom(写在ram中了)存储器,老师已经写好给我们了,而剩下的就是AR、PC、DR、IR、TR、R、AC、ALU、Z,什么顺序无所谓,我们一个个分析过来。他们有什么用,我们需要知道吗?啊不需要,我们现在在写模块,有什么用,以后再说。(以下过程暂不赘述clk和rst信号)

        AR需要输入选择信号arload、自增信号arinc、输入数据(其实是指令,不过无所谓,后面再说)来自哪?来自dbus(16位总线)。输出什么?addr(这些是老师已经写了的,有什么用?现在不用知道)

        PC信号输入选择信号pcload、自增信号pcinc、输入数据来自哪?还是dbus,那会不会和AR抢起来了?关我x事,不管。然后输出给pcdbus。那眼尖的人就看到了,啊这个pcbus是什么,pcbus不在PC里,和我们有关系吗?没有关系。

        那可能聪明的人可能就发现了(可能是悟到的也可能是看老师给的代码看懂了),xxload代表选择信号,当xxload=1的时候,那个寄存器可以把输入的值存进去,不然就不理输入数据。xxinc就是能让他被选择的时候自增一位,然后输出信号;输出会给xxdbus,不会直接给dbus,如果遇到xxbus挡住了(为0)就卡在那里不动,等xxbus同意输出了再进入dbus。

        这里注意了,我们的信号不是电流,是数据,数据就像爱情(bushi),他不会凭空消失,他只会转移(准确地说是只会复制,比如xxdbus的数据给dbus之后原先的数据一直都在)。不是说我PC输出了一下,后面没有继续输入了,我的pcdbus就没有数据了。

        那么看不懂没关系,后面讲到信号也都懂了。言归正传讲寄存器。

        DR寄存器:输入信号:选择信号drload、输入数据dbus[7:0],输出信号drdbus

        IR寄存器:输入信号:选择信号irload、输入数据drdbus,输出信号是谁?老师没给,我们就暂时定义为irdbus,有用吗?下回再说!

        TR寄存器:输入信号:选择信号trload、输入数据drdbus,输出信号trdbus

        R寄存器:输入信号:选择信号rload、输入数据dbus[7:0],输出信号rdbus

        AC寄存器:输入信号:选择信号acload输入数据来自ALU的。。。贵客,既然他是alu的输出,我们就叫他aludbus吧。输出信号acdbus

        Z寄存器:输入信号:选择信号zload、输入数据aludbus,输出信号老师没给,但是根据我们的命名规则我们应该叫他什么?没错,zdbus

        ALU:输入信号:选择(选择运算)信号alus、输入数据acdbus、dbus[7:0],输出aludbus。有人说:啊,图上有两根输出啊。但是你仔细想想,这两根输出不是一样的值嘛?而至于怎么通过alus(运算符)和两个输入的数据得到输出的数据(像极了给你一个符号和两个数字算结果),可以“复习”一下实验四,这里不做赘述。

        那么全部的小模块都完成了,这个数据通路图就出来了,实验已经完成了四分之一(哦耶~)。但是为什么我都把上面的数据通路弄出来了,还不能得到正确的结果呢?图上的控制信号(各种load、bus、alus)什么时候是什么应该怎么判断呢?很明显,我们需要一个新的模块(你可以理解为一个比较大的,盖在这个数据通路上的名叫control的模块,所有控制信号都从他出来),所有的选择信号都由他输出,那么他通过什么样的输入来判断这些输出呢?马上进入下一章。

二、信号篇

        在讲巨大的control模块之前,我们先来说一个不在这个模块中的信号,也是困扰了一些人一章的信号(dbus)。

        dbus有6条来源:membus守卫的数据、pcbus守卫的数据、drlbus、drhbus守卫的数据、trbus守卫的数据、rbus守卫的数据和acbus守卫的数据。当其中任何一个bus为1时,bus所控制的信号就会覆盖掉dbus中的值(如果dbus是16位,我想覆盖低8位,那么高8位还是原来的值)。所以老师已经在cpu.v最下面给了我们6行描述dbus的内容的语句,如果控制信号全部正确,我们应让同一时刻所有xxbus只有0-2(2是同时覆盖低高8位)个为1。

        那么初步了解后,我们对于control所面对的任务也有了些许了解。我们先做一个小小的练习热热身。请问:如何将PC寄存器的低八位的内容传递给IR,即实现IR<-PC[7:0]?(大家不用考虑每个寄存器具体用途

        从PC到IR需要经过寄存器DR,那么DR寄存器和IR寄存器的内容肯定都要被选中,即drload和irload都为1。PC的数据需要出来,那么pcbus=1,放他进入总线,然后dr就能接收并传递给IR。总结:PC到IR需要令pcbus=1,drload=1,irload=1

        了解之后我们就可以尝试去理解每个寄存器的功能(通过老师已经给我们的部分代码)。注意:以下正式开始control的编写。

        首先,我们的行为分为两部分:取值阶段、执行阶段。即:把指令取过来,执行指令。取指阶段大家都是一样的(有人会问三字指令呢?也是一样的,虽然要取三次,但是只有第一次取的八位是用于区分我们要执行什么命令的,后面两次取的八位仅仅是数据而不是命令)。

        我们可以直接白嫖老师的分析并且稍作理解!

         我就直白一点说吧,通过PC->AR,PC+1->PC,PC->AR,我们可以看出,PC是一个计数器,用来确定我们现在应该读取第几个指令,而AR的作用则是沟通M存储器,这个地方,看看就好。然后M的数据经过DR最后传递到了IR。IR叫什么?指令存储器!破案了!control的输入数据是什么?IR的数据,代表什么?代表指令

        control的实例化就可以完成了,输入irdbus,输出一大堆。

        那么每个指令应该做什么呢?比如从ir获得的指令是00000011,查表我们就知道了要执行AC->R,我们也知道了要打开acbus,并且让rload=1。

        那么我们怎么让我们的CPU在正确的时间做正确的事情呢?这就要引入节拍的概念了。我们定义了三组wire信号,用于分别控制正确的时间、正确的事情、正确的时间+正确的事情。

        正确的时间:t0,t1,t2...t7。

        正确的事情:inop、iread、iinac...所有指令加个i即可,表示的意思就是正确的事情!这个信号根据输入的irdbus信号定,老师已经给出模板。

        正确的时间+正确的事情:fetch1、fetch2、add1、ldac1、ldac2、ldac3......。

        那么打个比方,我们已经知道fetch1阶段pcbus和arload都要等于1了,就可以让pcbus和arload=fetch1,那有人问了,如果其他阶段也要这个信号等于1呢?比如fetch3,那么就让pcbus=fetch1||fetch3。即fetch1和fetch3,都是我pcbus打开的正确时间,打开这个事情也是正确的!

        注意:暂时没有使用到的信号要定义其等于0,后面再做更改,这很重要。比如drhbus和drlbus没有定义为0也没有初始化,那么他们的状态就是不确定的,那么取指令的时候dbus中的内容就很可能引用到drdbus,而此时drdbus还没有赋值,即访问空的区域!(数据结构:空指针恐吓)最后输出的结果就是一堆蓝线加一堆红线。

        那么我们剩下的事情就是化身勤劳码农,把所有的ixxx信号、xxx1、xxx2等信号写完,并且根据自己总结出来的控制信号的表格对每个控制信号单独寻找他能工作的时间事件就行了。

        附各小模块功能:TR寄存器(没记错的话)在选做指令中使用,用于得到16位的地址(先读低八位,通过DR给TR,然后再读高八位,DR吃独食,然后一起给dbus总线,并传递给PC)

        PC:存储吓一条指令地址(计数器)。

        AR:存储需要读取的信息(包括指令、数据)的地址

        DR:传递指令、地址。

        IR:存储指令

        ALU:计算

        AC:存储计算结果

        R:一个存储数据的工具罢了、

        Z:判断AC是否为0

        关于PC和AR,一般情况下,他们的值可能是只差1或0的,比如我现在进行第0条指令,那么我的AR就是0,读取第0条指令的内容,同时我的PC是1,代表我接下来要读第1条指令了,把PC的值直接给AR(1),然后执行第0条指令。

        但是在涉及跳转指令的时候,比如我要实现M[16]->AC(第0条指令),那么我的fetch周期内让我的PC=1,AR=1了,那么接下来就要获取第1、2号位置存储的00010000(16)和00000000,这代表存储器地址的低八位和高八位,读取完后PC则到了3,AR则等于16(获取第16号空间的数据),然后再传递给AC。具体的流程大家可以结合数据通路和老师给的流程看看。

         看什么信号?

        很多人还是不清楚看什么信号。首先我们要知道CPUState,这个信号把整个过程分为三块,in(01) check(10) run(11)。check阶段check_out指令会输出所有你存入的指令信息。run阶段,acdbus是我们的AC输出结果,rdbus是R的输出结果,是必看的,如果出了问题,可以看一下rambus。(每次从M取出的值,包括数据、高8位地址和低8位地址)理论上讲,rambus的数据和check_out变化顺序是相同的,但是每次变化的长短可能不同(涉及三字指令的话)。

        然后就是看各个节拍的控制信号,这里建议把所有的控制信号led先连上,如果不想连的话也可以每次打开仿真的时候额外加一下control/cpu内的控制信号。

        如果出现某一块的总线红了的情况,可以将dbus和各个三态门的控制信号(xxbus)取出,看看也没有出现错误导致访问未知。

三、top连线篇

        top怎么连线?老师没有给我们画这部分的数据通路图,我们可以想象一下,一共三个模块:CPU、ram(M)、light_show。

        ram功能:存储数据的工具罢了,通过输入cpu中read信号和AR给的地址信号读取相应的信息并输出到rambus中。并通过cpu给的write信号和输出的来自dbus的信号存储数据(貌似是选做)

        light_show功能:将所有信号和LED灯相连接,比如irload=1,那么irload_led=1。比如acdbus=00000011=(3),那么我的某两个HEX信号就为对应能让开发板输出数字3的信号(不懂没事,给他就完事了)

        那么我们的cpu怎么实例化?输出什么?全部控制信号(light要用)以及data_out等信号。

        注意:如果有选做用到Z的同学,我们的top里面已经定义了一个Z信号了,直接在实例化的时候.zdbus(Z)就完事了。

        然后我们需要修改一下light_show的实例化和内容。其实老师注释里面应该是全部的信号内容,照着补充就行。内容则仿照light_show底部的几个信号写一下就行。HEX相关的已经全部帮我们写好。

四、查错篇

        我提供两种很好用的查错方式:

        第一,我们可以使用老师提供的增加输入样例的方式查看波形图。打个比方,我这输出的irload_led怎么有问题啊?那我看谁?cpu里面的irload信号、control里面的irload信号,从他出生到应用都看清楚看明白,就知道在哪个地方连线有问题了,如果出生之前就有问题,至少我们能知道control里面他写错了。

 点击左边的加号可以打开具体的模块,选中后可以在右上角这里选择这个模块内的对应的信号,非常好用!

我看到有些同学班里没有发这种方式,我就在这里补充一下具体流程。

        首先肯定是选择我们想要的模块,然后选择右上角选择信号,右键 Add Wave(可以使用Ctrl和shift快速选择多个),这时候波形图的最下方就出现了我们想看到的信号。但是这个信号没有任何的波形,我们点击左上角工具栏Simulation->Restart 直接OK,然后Simulation->Run->Run_All,点击否。接下来会给你弹出你的文件,让你感觉,啊?我是不是写错了。没有,点击弹出的文件右上角的一个小叉叉就行了。

        第二,很多人只会根据wrong去双击定位查错,其实很多warning也是可以确定错误的,而很多人自从很久没有wrong之后就忘了这一点。如果你的某处线没有接对但是符合语法,完全有可能没有wrong但是warning给你报错了。对于warning中经常出现不用理会的,我们看着看着就眼熟了,其他感觉,诶,我好像没有以这种姿势出现过warning,那你就双击进去看一下,当然有些也是双击不了的,那就很难受了。

结语

        祝大家都能成功完成课设!

        附两张仿真成功大图。

猜你喜欢

转载自blog.csdn.net/qq_51135645/article/details/121863701