项目代码下载
请大家首先准备好本项目所用的源代码。如果已经下载了,那就不用重复下载了。如果还没有下载,那么,请大家点击下方链接,来了解下载本项目的CPU源代码的方法。
下载本项目代码
准备好了项目源代码以后,我们接着去讲解。
本节前言
在上一节,我提到,本节要来跟大家讲解,本系统里面,所执行的几条代码。
我的这个小系统,所写入的指令并不多,一共就6条有效指令。是什么指令呢?我们还是请看正式的内容讲解。
一. 打开内存初始化文件
首先呢,还是请大家打开Quartus II 13.1 软件。接下来,请大家跟我一起来操作。
第一步,依次点击选择【File】,【Open】菜单命令,打开【Open File】对话框,如下图所示。
我是将指令,写入了指令内存里面。具体地,是写入了本系统的一个RAM当中,这个RAM的名字是【ram_disk_256x16】,关于这个虚拟RAM,我们之前讲过它的生成方法的。忘记了的,可以参阅下方链接所示的文章。
调用IP核生成指令内存
然后呢,我这里所说的,将指令写入指令内存,不是说,将所要写的指令,直接写入虚拟RAM【ram_disk_256x16】里面,不是这个意思。而是说,将这个指令,写入一个内存初始化文件当中。
内存初始化文件,英文名字,应该叫做 Memory Initialization File。这个英文名词,它的首字母,是MIF,所以呢,内存初始化文件,它的后缀名,就是【.mif】。
所以呢,接下来,我们需要在系统中,寻找一个以【.mif】为后缀的内存初始化文件,这个文件里面保存了我们所写入的指令。我们接着往下操作。
第二步,修改文件过滤器中的文件类型。
首先呢,请大家在打开文件对话框中找到【文件类型】字样,它的右边,是一个组合框。如下图所示。
本节的图2也有和图3类似的界面。只不过,在图2中,文件类型是【Memory Files】,而图3中的文件类型为【Design Files】。接下来,请大家点击图3中的【文件类型】右边的组合框中的下拉三角符号。
点击了下拉三角号以后,出现了一堆的文件类型列表,我们需要从文件类型列表中选择【Memory Files】选项,过程如下图所示。
请大家确保,自己选择后的结果里面,文件类型如图5中的红色框线所示。这一步好了以后,我们往下进行。
第三步,浏览到内存初始化文件,打开它。
关于这一步,首先呢,请大家在【Open File】对话框中,找到【cpu_me01】项目的总文件夹,然后呢,浏览到【\cpu_me01\ip_core\ram_disk_256x16】的位置,里面有一个名为【ram_disk.mif】的内存初始化文件,过程如下图所示。
点击选择图7的红色框线所示的文件,然后点击右下方的【打开】按钮,结果如下图所示。
经过这么几步的操作,我们已经是打开了内存初始化文件,接下来,我来讲解一下图9所示的,内存初始化文件的显示格式。
二. 内存初始化文件的显示格式——行列标签
在图9中,是一个显示的表格。在大家自己的软件界面的右边,还有一个垂直滚动条。当然了,在图8和图9的截图中,这个滚动条部分,我是没有截取的。在大家自己的软件中,是可以看到这个滚动条的。
这个表格,它有行和列。
请大家来看一看表格的标题行,如下图所示。
图10所示的红色框线的部分,就是标题行了。
在这个标题行中,首列是【Addr】字样,再往右是【+0】,【+1】,【+2】等等的字样,一直排到【+7】,最右边是【ASCII】字样。
在这个,这个【+xxx】中的x,我们可以将其看作是一个偏移地址。【+3】的话,偏移地址为3。【+6】的话,偏移地址为6。
我们再来看标题列,如下图所示。
在图11中,红色框线所示的列,就是标题栏。我的红色框线,仅仅是画出了标题列中的一部分。因为,我们所生成的虚拟RAM的尺寸是【256 x 16】的,也就是,有256个存储单元,每一个单元是16比特的。这样一来,列数太多了,所以,屏幕上显示不完,大家需要借助于软件界面的垂直滚动条,将界面往下拉,才可以看到最下面的一列的。
在这个标题列中,最上方,也是【Addr】字样,而再往下,是8的整数倍的各个整数了。从上往下,依次是【0】,【8】,【16】等等。这里的【0】,【8】,【16】等等的行标签,我们可以将它们看作是一个基地址。
然后呢,在图9所示的整个显示表格里面,我们可以点击选中一个单元格,如下图所示。
在这个图12里面,我点击的这个单元格,它是被蓝色高亮显示了。然后呢,我还用红色框线,标注了它所在的行和列。它的列标签,为【+3】。行标签,为【48】。那么,这个单元的地址,就是行标签所示的基地址48,加上列标签所示的偏移地址【+3】,为51。所以,我点击的这个单元格,它的内存地址,为51。
任何一个你所点击的单元格,都可以表示为【以8的整数倍数的整数所表示的基地址】再加上一个【大于等于0且小于等于7】的偏移地址的形式。然后呢,这个单元格,它的内存地址,就是基地址加偏移地址。
比如说,地址23,为基地址【16】 + 偏移地址【7】。地址99,为基地址【96】+ 偏移地址【3】。所以呢,地址23的行标签为【16】,列标签,为【+7】。地址99的行标签,为【96】,列标签,为【+3】。
三. 本系统所写入的指令
行列标签我讲完了,接下来,我来讲一讲本系统所写入的指令码,或者说是机器码。
我只往系统里写入了6条指令。这四条指令,依次位于0,1,2,3,四个内存地址之中。也就是,它们都位于行标签【0】所在的行,而列标签,分别为【+0】,【+1】,【+2】,【+3】,【+4】,【+5】。我们来看一看这6条指令。
如图13所示,它们分别是2007,3001,2010,1001,2030,3801。
这几条指令,不是十进制数,而是十六进制的整数。这是因为,你打开一个内存初始化文件,它默认显示的数,就是十六进制的。可以将其修改为十进制等等的其他格式的数,但是,在这里,我们先保持着十六进制的默认显示数制。至于如何修改显示数制,我们后面再讲。
接下来,我们来讲一讲,这几条指令的含义。
想要讲解这几条指令的格式,我们就得是结合着本系统的指令码的格式,还有本系统的指令编码表。
然后呢,之前,我在讲解译码模块的时候,谈到了本系统的指令码的格式。
在本系统里面,每一条指令,固定地,为16 bit。其中,高5位,也就是 位15到位11,为操作码。位10到位8,固定地,为保留字000,。低8位,也就是位7到位0,是操作数。
有了这样的信息,我们来逐个地分析本系统的6条指令码的内容。
第1条指令,2007(十六进制)
第一个,0x2007,它的低8位的值是7,高8位的值是0x20,我们将0x20展开为2进制,为0B 0010 0000。在这8个二进制位里面,低3位,对应着完整的一个指令码中的位10到位8,固定地,为000。这8个二进制位的高5位,对应着一个完整的指令码的位15到位11,为操作码。在这里呢,0x20的高5位的二进制码为0B 00100,这个0B 00100,它就和指令编码表中的格式4的一行对应上了。
格式4,它的功能是,将操作数n视为一个立即数,将其存入累加器da的低8位,并将其扩充为一个16 bit的整数。
这样一来,第一条指令 0x2007,它的含义是,将立即数7送入累加器da中。
第2条指令,3001(十六进制)
第2个,0x3001,它的低8位为1,高8位为0x30,转换为2进制码,为 0B 0011 0000,这8个二进制位,高5位为 0B 00110,低3位为000。这8位的低3位对应着完整的一条指令的位10到位8,是保留位,固定地,为 0B 000。而这8位中的高5位,为0B 00110,它对应着格式6。
格式6的功能,将累加器da中的内容,送入DRAM的地址r的位置。r的值,就是操作数所指定的值。
在这里,0x3001,它的意思是,将累加器da里面的数,送入DRAM地址1的位置。为啥地址为1?因为在0x3001中,低8位为1,而低8位是操作数,所以,结合着格式6的功能,我们说,0x3001,是将累加器da的内容送入DRAM地址1,而不是送入地址3。
第3条指令,2010(十六进制)
第3条指令,0x2010,它的低8位为0x10,高8位为0x20。我们将高8位转换为二进制,为0B 0010 0000,也就是,高8位里面的高5位,为0B 00100,它对应着完整的一个指令码的位15到位11,为操作码。这个0B 00100,对应着格式4。高8位中的低3位,对应着保留位,固定为0B 000。
格式4的功能,我们在第一条指令中讲过了,它是将操作数送入累加器da的低8位,并扩充为16 bit。
这样一来,0x2010,它的含义是,将立即数0x10,也就是十进制的16,送入累加器da的低8位,并扩充为16进制数。
所谓的,【将一个8位的操作数送入一个累加器的低8位,并扩充为16 bit】。它的意思是,将累加器的高位固定为0值,而将8位操作数送到累加器的低8位。
第4条指令,1001(十六进制)
第4条指令,为0x1001,它的低8位是1,高8位位为0x10。
我们将高8位转换为二进制数,为0B 0001 0000。在这8位中,低3位是保留位。高5位,对应着一条完整的指令码中的操作码部分,为0B 00010,它对应着格式2。
格式2的含义,将dram的r单元的数与累加器da相加,结果放在da中。在这里,这个DRAM的R单元,这个R,由操作数来指定。
这样的话,0x1001,它的意思是,将内存地址1中的数,和累加器中的数相加,结果存放在累加器da中。
在这里呢,我们来试着执行一下以上的指令。
第一条指令 0x2007,它的含义是,将立即数7送入累加器da中。第一条指令执行完以后,累加器da中存放的数,为整数7。
第二条指令0x3001,它的意思是,将累加器da里面的数,送入DRAM地址1的位置。第二条指令执行完了以后,累加器da中的数,也就是整数7,就被送到了内存地址1里面。此时,内存地址1中的数,就是整数7了。
第三条指令0x2010,它的含义是,将立即数0x10,也就是十进制的16,送入累加器da的低8位,并扩充为16进制数。第三条指令执行完了以后,累加器da中的内容,变为了整数0x10,或者说是十进制的16。
第四条指令,也就是本条指令0x1001,它的意思是,将内存地址1中的数,和累加器中的数相加,结果存放在累加器da中。在第四条指令开始执行之前,累加器中的数为十进制的16。内存地址1中的,为7,将16和7相加,结果为23,也就是十六进制的0x17。所以呢,本条指令执行完了以后,累加器da中的数,就变为0x17了。而内存地址1中,依旧是整数7。
第5条指令,2030(十六进制)
第5条指令,0x2030,它的低8位是0x30,也就是十进制的48。它的高8位为0x20。高8位为0x20的情况,我们在第3条指令和第1条指令中都已经分析过了,它是格式4。
格式4的功能,将操作数n视为一个立即数,将其存入累加器da的低8位,并将其扩充为一个16 bit的整数。
所以呢,0x2030,这条指令的含义,是将立即数0x30送入累加器的低8位,并扩充为一个16比特的整数。
本条指令执行以前,我们面临的环境,是第4条指令的执行结果。第4条指令的结果是,累加器da中的内容为0x17,内存地址1中的内容为7。
本条指令执行了以后,累加器的内容变为0x30,内存地址1中的内容,依旧是整数7。
第6条指令,3801(十六进制)
第6条指令,它的低8位为1,也就是,操作数为1。本条指令的高8位为0x38。
我们将高8位转换为二进制,为0B 0011 1000。在这8位里面,低3位是保留位,高5位对应着完整的一条指令中的位15到位11,为操作码,为0B 00111,它对应着格式7。
格式7的功能,dram的r单元与累加器da相减,结果放在累加器da。注意哈,在这里,其实是累加器中的数减去DRAM中的数,而不是内存中的数减去累加器中的数。这个,是我没有说明白。
本条指令执行以前,我们的执行环境为第5条指令的结果。
第5条指令的结果为,累加器的内容为0x30,内存地址1中的内容是整数7。
本条指令执行时,累加器中的内容0x30减去内存地址1中的整数7,也就是十进制的48减去7,结果为41。41的十六进制形式为0x29。
结束语
本节,算是讲完了。
有一段时间,我是没有去写作本专栏了。在搁置本专栏的期间里,我主要是在忙着学习MFC去了。
由于我所学习的MFC教材的页数比较多,并且,学习它所需要的思考力度比较大,所以,我没有脱开身来写作本专栏。当前,我已经在CSDN里面开设了MFC专栏。
MFC,就是微软基础类库,英文名称为 Microsoft Foundation Class Library。它是用微软的类库来编写图形界面程序的一种,面向对象的编程。难度比较高,学习它,耗费了我相当的精力,比较累吧。
大家若是有兴趣,也欢迎前往我的MFC专栏,去学习关于MFC的知识。
当前,预计,我应该会在一段时间以后,才来开始连续地讲解着MFC的知识。在连续地讲解着MFC的知识之前,我是打算着,先将本专栏的内容给讲完吧。本专栏讲完了以后,我打算再去讲解着MFC的知识。
本节里面,我是讲解了本系统中写入的6条指令码。这6条指令,是可以在仿真软件MultiSim中观察执行情况的。
本节,我就先不去领着大家一起观察仿真结果了,因为写文章写得有些累了。到了这里,如果你之前认真地学习了本专栏的内容,并且,你自己知道如何在Quartus II中切换顶层设计模块,也知道如何切换Simulation的Test Bench模块,那么,你可以自己去观测本节所讲的内容执行情况。
本节所述的6条指令的执行情况,我会在下一节中,来领着大家一起在仿真软件MultiSim中观测仿真执行情况。
祝大家学习愉快