自己动手写CPU之第九阶段(6)——修改最小SOPC
来源:互联网 发布:街篮手游官网巨人网络 编辑:程序博客网 时间:2024/06/05 06:41
将陆续上传新书《自己动手写CPU》,今天是第45篇。
这几天事情多,好久没更新了
前几篇实现了加载存储指令,今天将修改最小SOPC,用以测试加载存储指令是否实现正确。闲话少说,进入正题。
9.4 修改最小SOPC
为了验证上一节添加的加载存储指令是否实现正确,需要修改在第4章中设计的最小SOPC,为其添加数据存储器RAM。
9.4.1 添加数据存储器RAM
数据存储器RAM的接口如图9-24所示,还是采用左边是输入接口,右边是输出接口的方式绘制,这样便于理解。接口含义如表9-7所示。
数据存储器RAM模块的代码如下,源文件是本书附带光盘Code\Chapter9_1目录下的data_ram.v文件。
module data_ram( inputwire clk, input wire ce, input wire we, input wire[`DataAddrBus] addr, input wire[3:0] sel, input wire[`DataBus] data_i, output reg[`DataBus] data_o ); // 定义四个字节数组 reg[`ByteWidth] data_mem0[0:`DataMemNum-1]; reg[`ByteWidth] data_mem1[0:`DataMemNum-1]; reg[`ByteWidth] data_mem2[0:`DataMemNum-1]; reg[`ByteWidth] data_mem3[0:`DataMemNum-1]; // 写操作 always @ (posedge clk) begin if (ce == `ChipDisable) begin //data_o <= ZeroWord; end else if(we == `WriteEnable) begin if (sel[3] == 1'b1) begin data_mem3[addr[`DataMemNumLog2+1:2]] <= data_i[31:24]; end if (sel[2] == 1'b1) begin data_mem2[addr[`DataMemNumLog2+1:2]] <= data_i[23:16]; end if (sel[1] == 1'b1) begin data_mem1[addr[`DataMemNumLog2+1:2]] <= data_i[15:8]; end if (sel[0] == 1'b1) begin data_mem0[addr[`DataMemNumLog2+1:2]] <= data_i[7:0]; end end end // 读操作 always @ (*) begin if (ce == `ChipDisable) begin data_o <= `ZeroWord; end else if(we == `WriteDisable) begin data_o <= {data_mem3[addr[`DataMemNumLog2+1:2]], data_mem2[addr[`DataMemNumLog2+1:2]], data_mem1[addr[`DataMemNumLog2+1:2]], data_mem0[addr[`DataMemNumLog2+1:2]]}; end else begin data_o <= `ZeroWord; end endendmodule其中涉及到的相关宏定义在defines.v中定义,如下:`define DataAddrBus 31:0 //地址总线宽度`define DataBus 31:0 //数据总线宽度`define DataMemNum 131071 //RAM的大小,单位是字,此处是128K word `define DataMemNumLog2 17 //实际使用到的地址宽度`define ByteWidth 7:0 //一个字节的宽度,是8bit
为了方便实现对数据存储器按字节寻址,在设计的时候使用4个8位存储器代替一个32位存储器,如图9-25所示,读操作时,从4个8位存储器中各读出一个字节,组合为一个32位的数据输出,写操作时,依据sel的值,修改其中特定存储器对应的字节即可。因此,地址addr的最低两位不需要使用,比如:读取地址n处的字,实际就是从4个8位存储器的地址n/4处各读取一个字节,组合起来就来地址n处的字。读者可以结合本节实现的数据存储器理解9.3.3节中MEM模块的输出。
9.4.2 修改最小SOPC
添加数据存储器RAM后的SOPC如图9-26所示。读者可以与图4-9对比。
此处需要修改openmips_min_sopc.v,在其中将OpenMIPS、ROM、RAM按照图9-26所示连接起来,具体代码不在书中列出,读者可以参考本附带光盘Code\Chapter9_1目录下的同名文件。
9.5 测试程序
下面的测试程序是用来验证前几节实现的加载存储指令(除ll、sc指令)是否正确,程序的注释给出了预期执行效果。源文件是本书附带光盘Code\Chapter9_1\AsmTest目录下的inst_rom.S文件。
.org 0x0.set noat.set noreorder.set nomacro.global _start_start:############## 第一段:测试sb、lb、lbu指令 ################ori $3,$0,0xeeff # $3 = 0x0000eeffsb $3,0x3($0) # 向RAM地址0x3处存储0xff, [0x3] = 0xffsrl $3,$3,8 # 逻辑右移8位, $3 = 0x000000eesb $3,0x2($0) # 向RAM地址0x2处存储0xee, [0x2] = 0xeeori $3,$0,0xccdd # $3 = 0x0000ccddsb $3,0x1($0) # 向RAM地址0x1处存储0xdd, [0x1] = 0xddsrl $3,$3,8 # 逻辑右移8位, $3 = 0x000000ccsb $3,0x0($0) # 向RAM地址0x0处存储0xcc, [0x0] = 0xcclb $1,0x3($0) # 加载0x3处的字节并作符号扩展, $1 = 0xfffffffflbu $1,0x2($0) # 加载0x2处的字节并作无符号扩展, $1 = 0x000000ee################ 第二段:测试sh、lh、lhu指令 ##############ori $3,$0,0xaabb # $3 = 0x0000aabbsh $3,0x4($0) # 向RAM地址0x4处存储0xaabb, # [0x4] = 0xaa, [0x5] = 0xbblhu $1,0x4($0) # 加载0x4处的半字并作无符号扩展, $1 = 0x0000aabblh $1,0x4($0) # 加载0x4处的半字并作符号扩展, $1 = 0xffffaabbori $3,$0,0x8899 # $3 = 0x00008899sh $3,0x6($0) # 向RAM地址0x6处存储0x8899, # [0x6] = 0x88, [0x7] = 0x99lh $1,0x6($0) # 加载0x6处的半字并作符号扩展, $1 = 0xffff8899lhu $1,0x6($0) # 加载0x6处的半字并作无符号扩展, $1 = 0x00008899################ 第三段:测试sw、lw、lwl、lwr指令 ############### 经过上面指令的执行,此时RAM的内容如下# [0x0] = 0xcc, [0x1] = 0xdd# [0x2] = 0xee, [0x3] = 0xff# [0x4] = 0xaa, [0x5] = 0xbb# [0x6] = 0x88, [0x7] = 0x99ori $3,$0,0x4455sll $3,$3,0x10ori $3,$3,0x6677 # $3 = 0x44556677sw $3,0x8($0) # 向RAM地址0x8处存储0x44556677, # [0x8] = 0x44, [0x9] = 0x55, # [0xa] = 0x66, [0xb] = 0x77lw $1,0x8($0) # 加载0x8处的字, $1 = 0x44556677lwl $1,0x5($0) # 非对齐加载指令lwl,执行后使得$1 = 0xbb889977, # 读者可以结合图9-8理解lwr $1,0x8($0) # 非对齐加载指令lwr,执行后使得$1 = 0xbb889944, # 读者可以结合图9-10理解nop################ 第四段:测试swl、swr指令 ################swr $1,0x2($0) # 非对齐存储指令swr,执行效果如下 # [0x0] = 0x88, [0x1] = 0x99, # [0x2] = 0x44, [0x3] = 0xff # 读者可以结合图9-16理解swl $1,0x7($0) # 非对齐存储指令swl,执行效果如下 # [0x4] = 0xaa, [0x5] = 0xbb, # [0x6] = 0x88, [0x7] = 0xbb # 读者可以结合图9-14理解lw $1,0x0($0) # 加载RAM地址0x0处的字, $1 = 0x889944ff, # 验证swr指令的执行效果lw $1,0x4($0) # 加载RAM地址0x4处的字, $1 = 0xaabb8844, # 验证swl指令的执行效果_loop:j _loopnop
上面的测试代码可分为四段,分别测试了不同的加载、存储指令,在ModelSim中的仿真效果如图9-27所示,通过观察寄存器$1的变化,可知OpenMIPS处理器正确实现了加载存储指令。
- 自己动手写CPU之第九阶段(6)——修改最小SOPC
- 自己动手写CPU之第九阶段(5)——实现加载存储指令1(修改译码阶段)
- 自己动手写CPU之第九阶段(5)——实现加载存储指令2(修改执行阶段)
- 自己动手写CPU之第九阶段(5)——实现加载存储指令3(修改访存阶段)
- 自己动手写CPU之第九阶段(5)——实现加载存储指令4(修改OpenMIPS顶层模块)
- 自己动手写CPU之第九阶段(9)——修改OpenMIPS以实现ll、sc指令
- 自己动手写CPU之第九阶段(1)——加载存储指令说明1
- 自己动手写CPU之第九阶段(4)——加载存储指令实现思路
- 自己动手写CPU之第九阶段(7)——MIPS32中的LL、SC指令说明
- 自己动手写CPU之第九阶段(8)——MIPS32中的LL、SC指令说明
- 自己动手写CPU之第九阶段(2)——加载存储指令说明2(lwl、lwr)
- 自己动手写CPU之第九阶段(3)——加载存储指令说明2(swl、swr)
- 自己动手写CPU之第七阶段(6)——乘累加指令实现思路
- 自己动手写CPU之第四阶段(3)——MIPS编译环境的建立
- 自己动手写CPU之第四阶段(4)——Makefile文件建立
- 自己动手写CPU之第五阶段(1)——流水线数据相关问题
- 自己动手写CPU之第六阶段(1)——移动操作指令说明
- 自己动手写CPU之第六阶段(2)——移动操作指令实现思路
- Vs2012在Linux开发中的应用(4):公共属性的定义
- Ubuntu下图形界面与控制台终端之间切换的快捷键
- js跨域访问的几种方式
- Oracle 零基础学习第一天——简介和基本命令学习
- 使用Hibernate的7个步骤
- 自己动手写CPU之第九阶段(6)——修改最小SOPC
- Hibernate Oracle sequence的使用技巧
- MFC doc类,view类,frame类之间互相调用
- 树结构练习——判断给定森林中有多少棵树 (并查集)
- 触控科技与ARM合作:实现高质量娱乐体验
- 线段树的区间修改
- POJ 2488-A Knight's Journey(DFS)
- arm linux 内核生成过程
- Hibernate主键生成策略