自己动手写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

      为了方便实现对数据存储器按字节寻址,在设计的时候使用48位存储器代替一个32位存储器,如图9-25所示,读操作时,从48位存储器中各读出一个字节,组合为一个32位的数据输出,写操作时,依据sel的值,修改其中特定存储器对应的字节即可。因此,地址addr的最低两位不需要使用,比如:读取地址n处的字,实际就是从48位存储器的地址n/4处各读取一个字节,组合起来就来地址n处的字。读者可以结合本节实现的数据存储器理解9.3.3节中MEM模块的输出。



9.4.2 修改最小SOPC

      添加数据存储器RAM后的SOPC如图9-26所示。读者可以与图4-9对比。



      此处需要修改openmips_min_sopc.v,在其中将OpenMIPSROMRAM按照图9-26所示连接起来,具体代码不在书中列出,读者可以参考本附带光盘Code\Chapter9_1目录下的同名文件。

9.5 测试程序

      下面的测试程序是用来验证前几节实现的加载存储指令(除llsc指令)是否正确,程序的注释给出了预期执行效果。源文件是本书附带光盘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处理器正确实现了加载存储指令。





0 0
原创粉丝点击