我们会继续上传新书《自己写CPU》(未公布),今天是12片,四篇

书名又之前的《自己动手写处理器》改为《自己动手写CPU》

4.3 验证OpenMIPS实现效果

4.3.1指令存储器ROM的实现

本节将验证我们的OpenMIPS是否实现正确,包括:流水线是否正确、ori指令是否实现正确。在验证之前,须要首先实现指令存储器,以便OpenMIPS从中读取指令。

指令存储器模块是仅仅读的。其接口如图4-7所看到的,还是採用左边是输入接口,右边是输出接口的方式绘制。这样便于理解。

接口含义如表4-12所看到的。

指令存储器ROM模块在文件inst_rom.v中实现,代码例如以下,能够在本书附带光盘的Code\Chapter4\文件夹下找到源文件。

module inst_rom(

	input wire			ce,
input wire[`InstAddrBus] addr,
output reg[`InstBus] inst );
// 定义一个数组,大小是InstMemNum,元素宽度是InstBus
reg[`InstBus] inst_mem[0:`InstMemNum-1]; // 使用文件inst_rom.data初始化指令存储器
initial $readmemh ( "inst_rom.data", inst_mem ); // 当复位信号无效时,根据输入的地址。给出指令存储器ROM中相应的元素
always @ (*) begin
if (ce == `ChipDisable) begin
inst <= `ZeroWord;
end else begin
inst <= inst_mem[addr[`InstMemNumLog2+1:2]];
end
end endmodule

代码非常好理解,有下面几点说明。

(1)在初始化指令存储器时,使用了initial过程语句。

initial过程语句仅仅运行一次,通经常使用于仿真模块中对激励向量的描写叙述,或用于给变量赋初值,是面向模拟仿真的过程语句。通常不能被综合工具支持。所以假设要将本章实现的OpenMIPS处理器使用综合工具进行综合,那么须要改动这里初始化指令存储器的方法。

(2)在初始化指令存储器时,使用了系统函数$readmemh,表示从inst_rom.data文件里读取数据以初始化inst_mem,而inst_mem正是之前定义的数组。inst_rom.data是一个文本文件。里面存储的是指令。其每行存储一条32位宽度的指令(使用十六进制表示),系统函数$readmemh会将inst_rom.data中的数据依次填写到inst_mem数组中。

(3)OpenMIPS是依照字节寻址的。而此处定义的指令存储器的每一个地址是一个32bit的字,所以要将OpenMIPS给出的指令地址除以4再使用。比方:要读取地址0xC处的指令,那么实际就是相应ROM的inst_mem[3],如图4-8所看到的。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGVpc2hhbmd3ZW4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

除以4也就是将指令地址右移2位。所以在读取的时候给出的地址是addr[`InstMemNumLog2+1:2]。当中InstMemNumLog2是指令存储器的实际地址宽度,比方:假设inst_mem有1024个元素,那么InstMemNum等于1024。InstMemNumLog2等于10。表示实际地址宽度为10。

4.3.2 最小SOPC的实现

为了验证。须要建立一个SOPC,当中仅包括OpenMIPS、指令存储器ROM,所以是一个最小SOPC。OpenMIPS从指令存储器中读取指令,指令进入OpenMIPS開始运行。

最小SOPC的结构如图4-9所看到的。

最小SOPC相应的模块是openmips_min_sopc。位于文件openmips_min_sopc.v中,读者能够在本书附带光盘的Code\Chapter4\文件夹下找到该文件。主要内容例如以下。

在当中例化了处理器OpenMIPS、指令存储器ROM,并将两者依照图4-9的方式连接。

module openmips_min_sopc(

	input	wire		clk,
input wire rst ); // 连接指令存储器
wire[`InstAddrBus] inst_addr;
wire[`InstBus] inst;
wire rom_ce; // 例化处理器OpenMIPS
openmips openmips0(
.clk(clk), .rst(rst),
.rom_addr_o(inst_addr), .rom_data_i(inst),
.rom_ce(rom_ce)
); // 例化指令存储器ROM
inst_rom inst_rom0(
.ce(rom_ce),
.addr(inst_addr), .inst(inst)
); endmodule

4.3.3 编写測试程序

我们须要写一段測试程序,并将其存储到指令存储器ROM,这样当上一节建立的最小SOPC開始执行的时候,就会从ROM中取出我们的程序,送入OpenMIPS处理器执行。因为眼下的OpenMIPS仅仅实现了一条ori指令。所以測试程序非常easy,例如以下,相应本书附带光盘Code\Chapter4\TestAsm文件夹下的inst_rom.S文件。

  ori $1,$0,0x1100        # $1 = $0 | 0x1100 = 0x1100
ori $2,$0,0x0020 # $2 = $0 | 0x0020 = 0x0020
ori $3,$0,0xff00 # $3 = $0 | 0xff00 = 0xff00
ori $4,$0,0xffff # $4 = $0 | 0xffff = 0xffff

共同拥有4条指令。都是ori指令。

第1条指令将0x1100进行零扩展后与寄存器$0进行逻辑“或”运算。结果保存在寄存器$1中。

第2条指令将0x0020进行零扩展后与寄存器$0进行逻辑“或”运算,结果保存在寄存器$2中。

第3条指令将0xff00进行零扩展后与寄存器$0进行逻辑“或”运算,结果保存在寄存器$3中。

第4条指令将0xffff进行零扩展后与寄存器$0进行逻辑“或”运算,结果保存在寄存器$4中。

指令的凝视说明了指令的运行结果。接下来,依照正常的顺序应该是使用编译器编译我们的測试程序。但因为GCC编译器的安装、使用、Makefile文件的制作等内容还须要不少篇幅解说,而想必各位读者和笔者一样。急切地想知道OpenMIPS是否实现正确,所以本节採用手工编译的方式编译測试程序,4.4节将专题介绍GCC编译器的使用。

手工编译仅仅需依照指令内容填充进图4-1所看到的的ori指令格式中,就可以得到相应的二进制字,比方:对于指令ori $1,$0,0x1100。相应的二进制字如图4-10所看到的。

转化为十六进制即0x34011100,其余3条指令依照相同的方式能够得到相应的二进制字,依照$readmemh函数的要求。一行放一条指令。得到測试程序相应的isnt_rom.data文件例如以下,可在本书附带光盘的Code\Chapter4\TestAsm文件夹下找到同名文件。

34011100
34020020
3403ff00
3404ffff

4.3.4 建立Test Bench文件

本小节将建立Test Bench文件,当中给出最小SOPC执行所需的时钟信号、复位信号。代码例如以下,相应本书附带光盘Code\Chapter4\文件夹下的openmips_min_sopc_tb.v文件。

// 时间单位是1ns,精度是1ps
`timescale 1ns/1ps module openmips_min_sopc_tb(); reg CLOCK_50;
reg rst; // 每隔10ns。CLOCK_50信号翻转一次。所以一个周期是20ns。相应50MHz
initial begin
CLOCK_50 = 1'b0;
forever #10 CLOCK_50 = ~CLOCK_50;
end // 最初时刻。复位信号有效,在第195ns,复位信号无效,最小SOPC開始执行
// 执行1000ns后,暂停仿真
initial begin
rst = `RstEnable;
#195 rst= `RstDisable;
#1000 $stop;
end // 例化最小SOPC
openmips_min_sopc openmips_min_sopc0(
.clk(CLOCK_50),
.rst(rst)
); endmodule

4.3.5使用ModelSim检验OpenMIPS实现效果

万事俱备,仅仅欠东风了,本节是验证前的最后一步——建立ModelSimproject,进行仿真。參考第2章的介绍,新建一个ModelSimproject,project名能够为openmips_min_sopc。将上文创建的OpenMIPS全部源文件、Test Bench文件、指令存储器的源文件等(也就是本书附带光盘Code\Chapter4文件夹下全部.v文件)加入到project中。然后编译。

注意:还须要将上一小克制作的inst_rom.data文件拷贝到project文件夹下。

编译通过后。将workspace切换到Library选项卡,打开work这个library,选中openmips_min_sopc_tb,右键点击。选择Simulate,如图4-11所看到的。

在出现的波形显示界面中,加入要观察的信号,就可以開始仿真。此处我们选择寄存器$1-$4作为观察对象,如图4-12所看到的。通过观察寄存器$1-$4的终于值,可知OpenMIPS正确运行了測试程序,也就是正确实现了ori指令。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGVpc2hhbmd3ZW4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

加入很多其他要观察的信号,能够了解流水线运行情况。如图4-13所看到的。

为了使流水线情况显示的更加直观。此处以第一条指令在流水线中的运行过程为例。而且图中去掉了其他指令运行时引起的信号变化。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGVpc2hhbmd3ZW4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

(1)在复位结束后的第一个时钟周期上升沿,rom_ce_o变为ChipEnable。表示指令存储器使能。開始取指。进入取指阶段,从指令存储器中取出第一条指令0x34011100。赋给IF/ID模块的输入portif_inst。下一个时钟周期,第一条指令进入译码阶段。

(2)观察译码阶段。

  • 此时译码阶段的指令id_inst正是第一条指令0x34011100
  • 指令地址id_pc是0x00000000
  • 在ID模块对指令进行译码。得到指令运算类型alusel_o是3'b001。查询defines.h文件里的宏定义可知。相应宏EXE_RES_LOGIC,表示是逻辑运算
  • 得到运算子类型aluop_o是8'b00100101,查询defines.h文件里的宏定义可知,相应宏EXE_OR_OP。表示逻辑“或”运算
  • 译码得到參与运算的源操作数1是0x00000000。正是$0寄存器的值
  • 译码得到參与运算的源操作数2是0x00001100。正是指令中马上数零扩展后的值
  • 译码得到wreg_o的值为1,表示要写目的寄存器
  • 译码得到要写入的目的寄存器wd_o是5'b00001。正是$1寄存器

(3)观察运行阶段。

  • 进行指定的运算。得到wdata_o为0x00001100,就是要写到目的寄存器的数据
  • 传递译码阶段wreg_o的值,为1,表示要写目的寄存器
  • 传递译码阶段wd_o的值。为5'b00001。表示要写入的目的寄存器是$1寄存器

(4)观察訪存阶段

  • 传递运行阶段wdata_o的值,为0x00001100,表示要写到目的寄存器的数据
  • 传递运行阶段wreg_o的值。为1,表示要写目的寄存器
  • 传递运行阶段wd_o的值。为5'b00001,表示要写入的目的寄存器是$1寄存器

(5)观察回写阶段

  • 得到訪存阶段wdata_o的值,为0x00001100,表示要写到目的寄存器的数据
  • 得到訪存阶段wreg_o的值,为1。表示要写目的寄存器
  • 得到訪存阶段wd_o的值,为5'b00001。表示要写入的目的寄存器是$1寄存器

在回写阶段的最后,将依照要求写目的寄存器$1,使得$1的值为0x00001100。

通过上面的观察。可知原始的OpenMIPS五级流水线实现正确。接下来。我们就能够以此为基础,不断充实,加入实现很多其它的MIPS指令,只是,在此之前,我们要先学习使用GNU工具链。本节的样例仅仅有4条指令,能够手工编译,以后会遇到比較复杂。拥有较多指令的程序。届时,手工编译就显得效率低下了,所以要使用GNU工具链。

待续!

版权声明:本文博客原创文章。博客,未经同意,不得转载。

自己写CPU第四阶段(2)——验证该第一指令ori实现效果的更多相关文章

  1. 自己动手写CPU之第九阶段(8)——MIPS32中的LL、SC指令说明

    将陆续上传新书<自己动手写CPU>,今天是第47篇. 9.7 ll.sc指令实现思路 9.7.1 实现思路 这2条指令都涉及到訪问链接状态位LLbit,能够将LLbit当做寄存器处理,ll ...

  2. 自己动手写处理器之第四阶段(1)——第一条指令ori的实现

    将陆续上传本人写的新书<自己动手写处理器>(尚未出版),今天是第11篇,我尽量每周四篇 第4章 第一条指令ori的实现 前面几章介绍了非常多预备知识,也描绘了即将要实现的OpenMIPS处 ...

  3. 自己动手写CPU之第九阶段(4)——载入存储指令实现思路

    将陆续上传新书<自己动手写CPU>,今天是第40篇,我尽量每周四篇,可是近期已经非常久没有实现这个目标了,一直都有事,不好意思哈. 开展晒书评送书活动,在q=%E4%BA%9A%E9%A9 ...

  4. 自己动手写CPU之第九阶段(2)——载入存储指令说明2(lwl、lwr)

    将陆续上传新书<自己动手写CPU>.今天是第38篇,我尽量每周四篇,可是近期已经非常久没有实现这个目标了.一直都有事,不好意思哈. 开展晒书评送书活动,在q=%E4%BA%9A%E9%A9 ...

  5. 自己动手写CPU之第九阶段(7)——MIPS32中的LL、SC指令说明

    将陆续上传新书<自己动手写CPU>,今天是第46篇. 在MIPS32指令集中有两条特殊的存储载入指令:链接载入指令LL.条件存储指令SC,本次将介绍这两条指令.在兴许将实现这两条指令. 9 ...

  6. 自己动手写CPU之第四阶段(3)——MIPS编译环境的建立

    将陆续上传本人写的新书<自己动手写CPU>(尚未出版).今天是第13篇.我尽量每周四篇 4.4 MIPS编译环境的建立 OpenMIPS处理器在设计的时候就计划与MIPS32指令集架构兼容 ...

  7. 自己动手写CPU之第五阶段(1)——流水线数据相关问题

    将陆续上传本人写的新书<自己动手写CPU>(尚未出版),今天是第15篇,我尽量每周四篇 上一章建立了原始的OpenMIPS五级流水线结构,可是仅仅实现了一条ori指令,从本章開始,将逐步完 ...

  8. 自己动手写CPU之第七阶段(7)——乘累加指令的实现

    将陆续上传本人写的新书<自己动手写CPU>.今天是第30篇.我尽量每周四篇 亚马逊的销售地址例如以下.欢迎大家围观呵! http://www.amazon.cn/dp/b00mqkrlg8 ...

  9. 自己动手写CPU之第六阶段(2)——移动操作指令实现思路

    将陆续上传本人写的新书<自己动手写CPU>(尚未出版),今天是第21篇,我尽量每周四篇 6.2 移动操作指令实现思路 6.2.1 实现思路 这6条移动操作指令能够分为两类:一类是不涉及特殊 ...

随机推荐

  1. Unity3D游戏开发从零单排(四) - 制作一个iOS游戏

    提要 此篇是一个国外教程的翻译,尽管有点老,可是适合新手入门. 自己去写代码.debug,布置场景,能够收获到非常多.游戏邦上已经有前面两部分的译文,这里翻译的是游戏的最后一个部分. 欢迎回来 在第一 ...

  2. JVM学习03_new对象的内存图讲解,以及引出static方法(转)

    目录 -=-讲解对象创建过程中,-=-堆内存和栈内存的情况 -=-构造函数对类对象的成员变量的初始化过程 -=-构造函数出栈 -=-类的方法在不访问类对象的成员变量时造成的内存资源浪费怎么解决? -= ...

  3. 利用python 提取log 文件里的关键句子,并进行统计分析

    利用python开发了一个提取sim.log 中的各个关键步骤中的时间并进行统计的程序: #!/usr/bin/python2.6 import re,datetime file_name='/hom ...

  4. Oracle大数据量查询实际分析

    Oracle数据库: 刚做一张5000万条数据的数据抽取,当前表同时还在继续insert操作,每分钟几百条数据. 该表按照时间,以月份为单位做的表分区,没有任何索引,当前共有14个字段,平均每个字段3 ...

  5. 【JAVA学习】“-Xmx1024m -Xms1024m -Xmn512m -Xss256k”——Java执行參数(转)

    年轻代 年老代概念 http://jefferent.iteye.com/blog/1123677 JVM的堆的内存, 是通过以下面两个參数控制的  -Xms 最小堆的大小, 也就是当你的虚拟机启动后 ...

  6. 用C++语言开发Android程序 配置开发环境

    转自:http://www.cnblogs.com/yaotong/p/3622430.html 用C++语言开发Android程序 配置开发环境   如果你是一个C++语言的死忠,你喜欢C++语言到 ...

  7. HDU2586 How far away ?(LCA模板题)

    题目链接:传送门 题意: 给定一棵树,求两个点之间的距离. 分析: LCA 的模板题目 ans = dis[u]+dis[v] - 2*dis[lca(u,v)]; 在线算法:详细解说 传送门 代码例 ...

  8. 2010多校第一题 hdu3440House Man 差分约束系统

    给我们n座房子,房子的高度各不相同, 从最低的房子开始, 每次跳到更高的房子, 跳n-1次最能跳到最高的房子了,但是每次跳跃的距离不能超过d 将这些房子在一维的方向上重新摆放(但是保持输入时的相对位置 ...

  9. windows phone (14) 简单了解Ellipse元素和Rectangle元素

    原文:windows phone (14) 简单了解Ellipse元素和Rectangle元素  System.Windows.Shapes命名空间中包含了显示矢量图形的元素分别为ellipse和re ...

  10. hdu2242(树形dp+tarjan+缩点)

    hdu2242 http://acm.hdu.edu.cn/showproblem.php?pid=2242 给定n,m表示n个点,m条边 每个点有个权值 问我们删除两某条边(割边)后将图分为两个部分 ...