自己動手寫CPU之第九階段(6)——修改最小SOPC

將陸續上傳新書《自己動手寫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

      爲了方便實現對數據存儲器按字節尋址,在設計的時候使用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 = 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處理器正確實現了加載存儲指令。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章