您的位置:首页 > 运维架构

自己动手写CPU之第九阶段(6)——修改最小SOPC

2014-11-17 23:57 260 查看
将陆续上传新书《自己动手写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(

input	wire		          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
end

endmodule

其中涉及到的相关宏定义在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 = 0x0000eeff
sb   $3,0x3($0)       # 向RAM地址0x3处存储0xff, [0x3] = 0xff

srl  $3,$3,8          # 逻辑右移8位, $3 = 0x000000ee
sb   $3,0x2($0)       # 向RAM地址0x2处存储0xee, [0x2] = 0xee

ori  $3,$0,0xccdd     # $3 = 0x0000ccdd
sb   $3,0x1($0)       # 向RAM地址0x1处存储0xdd, [0x1] = 0xdd

srl  $3,$3,8          # 逻辑右移8位, $3 = 0x000000cc
sb   $3,0x0($0)       # 向RAM地址0x0处存储0xcc, [0x0] = 0xcc

lb   $1,0x3($0)       # 加载0x3处的字节并作符号扩展, $1 = 0xffffffff
lbu  $1,0x2($0)       # 加载0x2处的字节并作无符号扩展, $1 = 0x000000ee

################       第二段:测试sh、lh、lhu指令     ##############

ori  $3,$0,0xaabb     # $3 = 0x0000aabb
sh   $3,0x4($0)       # 向RAM地址0x4处存储0xaabb,
# [0x4] = 0xaa, [0x5] = 0xbb

lhu  $1,0x4($0)       # 加载0x4处的半字并作无符号扩展, $1 = 0x0000aabb
lh   $1,0x4($0)       # 加载0x4处的半字并作符号扩展, $1 = 0xffffaabb

ori  $3,$0,0x8899     # $3 = 0x00008899
sh   $3,0x6($0)       # 向RAM地址0x6处存储0x8899,
# [0x6] = 0x88, [0x7] = 0x99

lh   $1,0x6($0)       # 加载0x6处的半字并作符号扩展, $1 = 0xffff8899
lhu  $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] = 0x99

ori  $3,$0,0x4455
sll  $3,$3,0x10
ori  $3,$3,0x6677     # $3 = 0x44556677
sw   $3,0x8($0)       # 向RAM地址0x8处存储0x44556677,
# [0x8] = 0x44, [0x9] = 0x55,
# [0xa] = 0x66, [0xb] = 0x77

lw   $1,0x8($0)       # 加载0x8处的字, $1 = 0x44556677

lwl  $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 _loop
nop

上面的测试代码可分为四段,分别测试了不同的加载、存储指令,在ModelSim中的仿真效果如图9-27所示,通过观察寄存器$1的变化,可知OpenMIPS处理器正确实现了加载存储指令。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐