• 沒有找到結果。

實作CPU與系統整合

N/A
N/A
Protected

Academic year: 2021

Share "實作CPU與系統整合"

Copied!
79
0
0

加載中.... (立即查看全文)

全文

(1)

作 C P U

系 統 整 合

指導老師

: 王益文

員: 林易瑤 蔡紫蕾 黃巧琪 蔡幸娟

民國

91 年 11 月 1 日

(2)

目錄

摘要

……….

5

導論

……….

5

MIPS CPU

―――――

5

1. MIPS R4000 2. OpenMips v.s. R4000

PLI (

programmable Lamguage Interface

)

―――――

8

動機

………...

9

整體架構

……….… 10

使用平台

………. 11

成員與工作分配

………. 12

軟體設計

……….. 12

. Assembler 的設計背景 ――――― 12 . Assembler 的規則 ――――― 13 . 擴充功能 ―――――- 14 . OM_SDK 的誕生 ――――― 15 . OM_SDK 的基本功能 ――――― 16 1. 控制命令列 2. 訊息系統視窗 3. 暫存器監看視窗 4. 硬體模擬系統之輸入輸出介面 5. memory (instruction & data) 監看視窗 6. 其他控制指令 六. 提供的服務 ――――― 18 1. Memory management 2. Echo server 3. Register monitor 4. Degugger

(3)

5. Memory 6. 建立反組譯過後組合語言的資料庫 7. 建立指令表功能

硬體設計

……….. 22

. MIPS CPU指令集 ――――― 22 1. ALU 指令 2. Memory 指令 3. 跳躍條件指令 二. 實作MIPS CPU流程圖――――― 29 . FPGA基本元件及功能 ――――― 29 1. Block 2. Embedded Block 3. Component 4. 產生 VHDL code 5. 利用 ModelSim 做模擬 四. OpenMips模擬系統架構 ――――― 30 1.CPU 架構 2. OpenMips_testbench 3. SimSystem 4. memory interface 5. CPUcore 五. 驗證與除錯 ――――― 45 1. ALU Function 驗證 1-1. 使用 ModelSim 產生 WaveForm 1-2. 亂數比對(與 C code 的結果做比對) 2. CPU core 的驗證 六. 問題與討論 ――――― 49 1. Brebch 指令的判斷條件 2. Nonrestoring Divider 實作的困難

3. Restorin Diveder 對商數與餘數的 correct 依據

結論

……….. 51

使用手冊

……….. 52

(4)

附錄

……… 59

附錄一 CPUcore 之架構圖 ――――― 59 附錄二 FD_Register 之架構圖 ――――― 60 附錄三 enCtrlSignal 之架構圖 ――――― 61 附錄四 regFileUnit 之架構圖 ――――― 62 附錄五 forwardUnit 之架構圖 ――――― 63 附錄六 DE_Register 之架構圖 ――――― 64 附錄七 ALUUnit 中 shiftUnit 之架構圖 ――――― 66

附錄八 ALUUnit 中 multiplier 的 dataUnit 之架構圖 ――――― 67

附錄九 ALUUnit 中 dividerCrt 的 dividerData 之架構圖 ――――― 68

附錄十 ALUUnit 中 mipsALU 之架構圖 ――――― 69

附錄十一 EM_register 之構造圖 ――――― 70

附錄十二 MemoryUnit 之構造圖 ――――― 72

附錄十三 MW_Register 之構造圖 ――――― 73

附錄十四 OpenMips_testbench 的驗證的軟體畫面 ――――― 75

(5)

這個專題的目的就是設計並實作一個MIPS CPU,並且設計一個簡單的 Assembler 將 .ASM 檔案轉換成機器語言,傳給利用 FPGA 軟體設計出來的模擬 CPU 架構,進行指令的處理。

MIPS CPU

1. MIPS R4000

MIPS R4000 是一顆可為 32 bits 或 64 bits 的 CPU,但一般是 64 bits 的 CPU;當運算有所需要時,就可以變成一顆 32 bits 的 CPU。在 MIPS R4000 這顆 CPU 中,所有指令的長度都是 32 bits。R4000 的 64-bit 架構還提供了以 下列出的功能:

• 64-bit on-chip floating-point unit • 64-bit integer arithmetic logic unit • 64-bit integer registers

• 64-bit virtual address space • 64-bit system bus

(6)

圖一:R4000 Processor Internal Block Diagram

MIPS R4000 是利用 superpipeline 的架構,這個架構是藉著八個 stage 讓 指令可以平行運算,這八個 stage 我列在下面:

•IF - Instruction Fetch, First Half •IS - Instruction Fetch, Second Half •RF - Register Fetch

•EX - Execution

•DF - Data Fetch, First Half •DS - Data Fetch, Second Half •TC - Tag Check

•WB - Write Back

在一般的狀況下,一個 cycle 可以做兩道指令。R4000 處理器的內部 pipeline 執行運算的速率是 master clock 的兩倍,這個處理器可以藉由 pipeline cache access、更短的暫存器讀取時間、實行 virtual-indexed primary cache 和允

(7)

許在 functional unit 中的潛在物花費大於一個 pipeline 的 clock cycle 來產生大 量的輸出,它也利用一個 on-chip TLB 來提供迅速的 virtual-to-physical 的位址 轉換。

R4000 的系統介面包含了以下:

• 一個 64-bit multiplexed address 和 data bus • 8 個 bit 的檢查位元

• 一個 9-bit 的 parity-protected command bus • 8 個 handshake 訊號 這顆 CPU 提供了以下的暫存器以供執行指令時使用: • 32 個一般目的暫存器 • 一個 Program Counter(PC)的暫存器 • 兩個暫存器(HI 和 LO)儲存乘法和除法的結果 • 浮點運算的暫存器

CPU 的暫存器可以為 32 bits 或 64 bits 的寬度,其型態取決於 R4000 的 處理器運算模式。 另外在指令格式的部分,該 CPU 提供下列三項指令格式: • immediate(I-type) • jump(J-type) • register(R-type)

(8)

圖二:三種指令型態的格式 2. OpenMips vs. R4000

我們所做的 OpenMips 和 R4000 最大的不同在於我們只做 32 bits 和原來 R4000 既有 64 bits 也有 32 bits 的型態不同,也從 R4000 的八個 stage 換成 R2000 的五個 stage,不過我們仍然沿用 R4000 的指令格式,所以 OpenMips 是藉於 R2000 和 R4000 之間的 CPU。 在整個 CPU 的架構中,我們仍然是沿用 R4000 的指令格式,可是 OpenMips 不作浮點數的運算。

PLI

再將硬體與軟體作結合時必須要透過某一個介面來將硬體的描述語言(HDL) 與系統的軟體程式語言(如 C , JAVA….),為了要了解 Verilog 運作時內部資料表 示的方法,於是程式介面(Programming Language Interface 簡稱 PLI )就派上用 場,。不但提供了一系列的程序可以存取到內部的資料,也可以寫到內部的資料, 甚至連模擬時的相關環境參數值都存取的到,使用者自訂的程序與函式就可以依 賴這一個介面來達成,使用標準的Verilog 程序與函式寫好程式,並輸入測試值, 同時,自訂系統任務與函式可以一起加入設計與測試模擬中,當模擬器把外部資 料都轉成內部格式後,就開始模擬運算,輸出結果。以下用大略的圖示來表示我 們的架構

(9)

FPGA 傳遞資料 PLI 內部資料表示 存取內部資料結構 PLI 提 供 的 函 式 庫

Serverplatform

Verilog 程式編輯 ModelSim WaveForm

(10)

進入大學之後的第一年,因為學的都是基礎的課程,主要目的是在建構我們 對 Computer 的基本概念,因此對每一門科目都只是略有涉獵,並沒有對哪一科 有特別的看法,直到上了大二時,修了一門叫“電子電路學”的課程,才發覺對 硬體方面有著濃厚的興趣。再加上接下來又修了一連串有關硬體的學科,像“數 位電路導論”、“數位電路設計”、“計算機結構學”等,而且表現都不錯,更 增加了自己的自信心,因此到了大三時便決定要做與硬體相關的專題研究。 再加上我們修王益文老師不少學分的課,並且王老師一向在這方面有豐富的 知識,因此決定找王老師做為我們的專題指導老師。正好當時有一位學長在做有 關 CPU 的研究,所以我們便在學長的帶領之下開始了我們專題的研究。

整 體 架 構

以下簡單介紹我們專題實驗的主要概念以及架構。基本上我們的目標很簡 單,只是想要試試看能不能寫一個 C 的程式,直接與 我們所時作的 CPU 做溝 通,測試結果是否正確。因此,我們必須要知道在 C 程式與 CPU 之間要如何 做聯繫。在請教過老師及學長之後,我們大略了解到我們要朝哪個方向開始發 展,以下的架構圖便是我們發展專題的基礎。 首先,我們利用 WIN-32 SDK 來 發展我們所需的軟體應用程式(WIN API),同時也利用 FPGA 設計硬體架構,但 是我們的軟體與硬體間並沒有溝通的橋樑,也可以說我們只是兩個獨立的部分, 所以我們要想辦法讓兩者之間能夠互通訊息。在這個部分我們是利用 DLL(動態 連結檔)聯繫軟體及硬體部分。而 DLL 與軟體的溝通我們是利用 Socket 來達 成;DLL 與硬體的溝通則是透過 PLI ,讓 PLI 與 PFGA 做溝通。在圖中的 Simulator 指的是 ModelSim,其為 FPGA 的模擬工具,因此我們透過 ModelSim

(11)

便可知道我們的硬體運作是否正確。

有了基本的架構圖之後,我們開始思考該如何來達成我們的目標――用我們 自己設計的 CPU 執行一個簡單的 C 程式。

使 用 平 台

我們專題所使用的 Tools 為 FPGA ,ModleSim and Leonardo。使用這些工 具的目的是為了要設計硬體 CPU 的部分,並且驗證所設計的 CPU 是否正 確 ,結果為我們所預期的。還有 WIN-32 SDK,為發展軟體應用程式部分的主 要工具。 我們利用 FPGA 來建構出 CPU 整個的架構圖,以簡單明瞭的圖形介面表 示出複雜的線路圖,使整個畫面看起來美觀易懂,並且也讓使用者可以以較容易 地方式找出發生問題的地方,因此容易 maintain ,而不必再寫像 VHDL 的程 式一般,要很仔細地注意每個 component 與其所對應的訊號線,然後出問題時 要花費許多時間去尋找出問題的地方。更重要的一點是,使用 FPGA 這套軟體 可以自動產生 VHDL 檔,是一套相當方便的工具,可以免除我們為了撰寫 VHDL code 時的麻煩,直接以圖形的方式將我們要設計的電路表示出來,由軟體本身 產生出code 更是大大的節省了我們設計的時間。

而 ModelSim 則主要用來做 CPU 的 Simulation。透過 ModelSim 我們可以

(12)

測試自己所設計出來的 CPU 是否正確,與我們所希望的結果相符合。經由 PLI (Programmable Language Interface),ModelSim 也可以使硬體部分與軟體部分 (自己撰寫的程式)做溝通,並且可以以 waveform 的方式顯示出每個指令的動 作,也可以明顯的看出每一個program counter 所做的動作;我們也藉助副檔名 為 .s 和 .asm 的檔案,一一對照其暫存器或記憶體的內容,以便確認我們所做 出的結果是正確的。 至於 Leonardo 則是在作 Synthesize 所用到的工具,簡單來說,我們可以 利用 Leonardo 知道我們所設計的 CPU 是否有可用的價值,亦即它執行的速度 是否夠快。如果不夠快,也可以知道到底是哪個部分拖慢了整體的執行速度。但 缺點是每 Synthesize 一次需要花費很多地時間,因此在這部分我們花費了不少 時間。 而 WIN-32 SDK 則是我們用來發展應用程式的工具,我們藉由此工具發展 Assembler、Server 、與 DLL 溝通等,並產生出一簡單的操作介面。

成 員 與 工 作 分 配

基本上來說,我們的實驗專題需要的人力還不算少,並不是一、兩個人便可 以處理的,因此我們的組員有四人,大概分為處理軟體的部分以及硬體設計的部 分。軟體的部分基本上是在處理程式與硬體間溝通的問題,包括寫一個

Assembler 、提供 Echo server 的服務、建立一個操作介面等等;而硬體設計部 分則是利用FPGA 設計整個 CPU 的硬體架構,由基本的 ALU 開始設計起,並 經由觀察 ModelSim 的 Waveform 驗證此設計是否正確。

(13)

體 設 計

一.

Assembler

的設計背景

就如同在“整體架構”部分所提到的一樣,為了要讓 C 程式能夠載入到 CPU ,我們必須要設計一個程式來完成這個任務。而這個任務的首要工作便是 將屬於高階語言的 C 轉成硬體可以看懂的低階語言,因此我們便需要一簡便的 assembler 來幫助我們達成這個目標。 為了能讓 C 順利地被硬體讀入,我們先利用 gcc compiler 將 .c 的檔案轉 成 .s 檔案,再與一些相關的函式以 –ld 參數連結成 .o 檔案,之後再反組譯 成 .asm 的檔案。然後將 .asm 檔案當作我們自己寫的 assembler 的輸入來源 , 之後將被 assembler 處理過後所產生出來的資料(為機械碼)載入到 memory 中,並且經由上述流程傳送給硬體做處理,最後產生出我們所要的結果。 至於為何要自己寫 assembler 呢?主要是要避免掉一些不必要的麻煩,例 如:每個 assembler 組譯出來的檔案格式可能會不同;提供的指令可能也會不 同,那對我們硬體那方面來說會很麻煩,因此可能還得將組譯過後的資料再重新 整理過,那倒不如一開始我們自己寫 assembler ,不僅可以清楚了解到它在做什 麼動作、有什麼功能,還可以隨自己的需要設計,可朔性比較大,也比較安全, 不會有太大的意外產生,使我們疲於尋找出問題所在。

二.

Assembler

的規則

因為我們以前並不曾寫過這一類的程式,所以我們並沒有一個確切的方向知 道該如何撰寫assembler 。因此我們先參考其他 assembler 程式所使用的格式, 看它們是如何區分data,address,text 等等的區段;有哪些指令或是如何配置記

(14)

憶體位址空間等。大概有了這些概念之後才開始設計我們自己的assembler。在 設計之初光是載入 .asm 檔時,該如何將檔案區分成我們需要的格式,就讓我們 傷了一陣子的腦筋。我們不斷反覆地看這個檔案的排法有何脈絡可尋,要以哪些 字元當作區分的準則?在抓取某一欄位時是否抓取到不該抓的字元?這些都要 非常注意。最後總算讓我們看出了一些端倪,以下我們以Finite state machine 來 表示此規則:

~ASCII ASCII

~ASCII iStart=0 && ‘:’ 0~9 || a~f ~ASCII(type=ID_SECTION_ADDR) ~ASCII (type=ID_SECTION_STRING) ASCII ASCII 0x0a || 0x0d || 0 (type = ID_SECTION_LINE_ERR) ASCII S0 S2 S1 S4 S3

(15)

三.擴充功能

我們除了設計了Assembler 之外,我們還加了一些有趣的功能,基本上是以 一個視窗介面做為設計發展的藍圖(如下圖一),我們希望能夠提供參數列、監 看記憶體內容、監看暫存器內容、硬體模擬系統之輸入/輸出以及顯示訊息的功 能。有了這些功能我們可以透過 PLI 監看硬體使用記憶體及暫存器的狀況;也 可以在命令列直接下達指令,並將每一指令所產生的訊息送到顯示訊息的視窗, 讓使用者知道是否指令下達錯誤;最重要的一點便是我們可以藉由觀察記憶體及 暫存器的內容知道硬體的設計是否有問題,比較容易 debug 。 圖一:視窗操作介面 視窗操作介面介紹 視窗一: console 區即系統訊息視窗,使用者鍵入的指令及電腦執行後 的訊息都會顯示在此。 視窗二: register 區即暫存器監看視窗,暫存器的內容會顯示在此。

(16)

視窗三: I/O device 區即硬體模擬系統之輸入輸出介面,I/O 的狀態會顯 示在此。

視窗四: memory 區即 memory (instruction & data ) 監看視窗,記憶體的 內容會顯示在此。 視窗五: command 區即控制命令列,使用者將指令鍵入於此。

四.

OM_SDK

的誕生

我們是用WIN-32 SDK 的方法來撰寫視窗操作介面,我們自己稱這個介面為 OM_SDK。其中還用到了 TCP/IP 的概念,藉由這個概念我們建立了一個可以和 硬體溝通的 Server,所以在執行本應用程式時,要先將設計好的介面即 Server 開啟,輸入指令後,便可以開始跟硬體部分的 FPGA Adv. 作溝通。我們設計後 的視窗操作介面如圖一。

五.

OM_SDK

的基本功能

功能1:控制命令列: 這個部分主要是在讓使用者輸入所需的指令功能,所有的指令格式及功 能詳列於表一。 指令名稱 指令功能 clear –console 清除console 區的內容

save –console file_name 將console 區的內容存至名為 filename 的 檔案

open -instTable table_name 開啟此檔名的表格(副檔名為.csv) 此處的table_name 為我們的指令集 view -instTable number 查看在指令集中的第 number 筆的指令

及其所有資料;若number 大於指令集的 所有個數,則會顯示出所有指令集的資料

(17)

find -inst instruction_name 在我們 open 的 table 中尋找所鍵入指令 名稱的指令 test -regfile 測試暫存器之檔案 source file_name 以輸入之檔名為來源檔,內容可以為多個 指令的集合,此處所指之指令為命令列所 使用之指令

Setenv asmArraySize number 動態配置一個記憶體空間,存放.asm 檔的 資料

Setenv enReg 開啟register 的監控視窗 Setenv disReg 關閉register 的監控視窗 Setenv enInst 開啟cpu 指令的監控視窗 Setenv disInst 關閉cpu 指令的監控視窗

open -asmELF file_name 開啟名稱為file_name 的.asm 檔,將它載 入我們所動態配置的記憶體空間,並將它 轉成能讓硬體看得懂的機械語言

view -asmELF number 查看asmELF table 的第 number 筆資料 view –regfile 查看目前暫存器內容

view –memory number 從 number 的位址開始查看目前記憶體內 容

save -asmELF file_name 將asmELF table 存到名為 file_name 的檔 案

socket -time number 設定socket time 的時間長短 Help 快速查詢所有指令 表 一 (由於某些指令需先鍵入其他指令方可使用,詳情請參考使用手冊) 我們利用 cmdDecode 函式處理輸入控制命令列的指令及其參數,並檢 驗指令是否錯誤,再依不同的情況將適當的錯誤訊息顯示於系統訊息視 窗。之所以會有這個視窗的目的在於可以讓使用者操作輕鬆。 功能2:系統訊息視窗 這個視窗主要是根據命令列所輸入的指令來產生出對映的訊息。我

(18)

們是利用 cmdDecode 函式所傳回的值經由 processCommand 函 式判斷接下來所要做的動作,是顯示出一個訊息?抑或是再呼叫函 式做動作。主要目的在於可以清楚讓使用者知道 OM_SDK 到底 做了什麼動作,與我們所希望的是否相符。 功能3:暫存器監看視窗 顧名思義,這個視窗主要是用來顯示在硬體中的暫存器內容。我們 利用 TCP/IP 的概念,建立了一個可以和硬體做溝通的 Server , 利用這個 Server 我們得以順利地將硬體中的暫存器內容傳回給 OM_SDK ,讓使用者清楚地知道暫存器的變化,進而驗證硬體的 設計是否有錯誤。 功能4:硬體模擬系統之輸入輸出介面 此視窗的主要功能在於將 CPU 執行程式的結果寫入到 IO console,或是在 IO console 寫入,讀出到 CPU。基本上我們是採 用 memory mapping 的方式,讓 CPU 對 memory 寫入。然後去 呼叫 handleBusIO 把字元寫入/讀出到 IO console。

功能5:memory (instruction & data ) 監看視窗

顧名思義,這是將記憶體內容顯示於此。我們同樣也是用 TCP/IP 的概念,建立了一個 memory Server ,透過這個 Server 所提供的 功能,可以傳送 memory data 、memory address、instruction address、instructions 給硬體,硬體依據所獲得的指令執行,若有 對 memory 做存取動作時,也會透過此 Server 修改 memory 中 的資料。主要目的在於能讓使用者清楚 memory 的變化,藉此驗 證自己的硬體設計是否有錯。

(19)

功能 6:其他控制指令(save console, setenv, and so on ) 除了上面五種視窗介面的功能外,最主要的就是控制指令了,而這 些指令都已詳載於表一了。至於為何要有這些指令?當然就是為了 要能夠順利地控制軟體,讓軟體將適當的資料傳送給硬體。

六.提供的服務

我們的軟體依據我們的目的而提供的服務功能有:memory management、副 檔名為 .asm 的 loader、echo server、register monitor、debugger 、memory 、建 立反組譯後組合語言的資料庫、建立指令表功能的服務等。

1. Memory management :

功能1 :將讀入的反組譯資料(由 cross-compiler tools 產生的.txt 檔)寫入 memory 中,當做是硬體要執行的程式。

功能 2 : 透過硬體的控制訊號來存取 memory,並提供 read/write 一個 word data 或 read/write a byte data.或 read/write 一個 halfword data 或 read/write angle data 。 功能 3: 提供 reset memory 亦即將 memory data 清為零,主要目

的是避免參考到錯誤的資料。因為如果當一個新檔案載 入進來時,若原來舊的位址有資料,可是新檔沒有,若 新檔參考到這個位址時應是錯的,但舊檔資料仍在,那 就會獲得一個錯誤的結果,導致嚴重的接下來一連串的 錯誤。

功能4: 提供 32 bits address line 亦即 4G bytes 的定址空間給硬 體部分做存取資料的動作。

(20)

讓使用者可以清楚知道是哪個位址有問題。

這項服務目的是為了方便管理記憶體空間,若是直接用硬體部分來處理, 會顯的更為困難,因此我們選擇用軟體來管理。

2. Echo server :

client 端送一字元至 server 端,sever 端再送一相同字元給 client 端。

這項服務目的是為了讓軟體跟硬體可以溝通而建立,server 端會不斷的向 client 端要一個字元。

3. Register monitor :

只要client 端的值有變動,server 端便會去讀 client 端的值。 這部分的方法是利用當硬體部分HDL 的 signal 改變時,會透 過verilog 的 function 傳給 PLI 的 function,然後 PLI 的 function 再透過TCP/IP 把 32 bits 的 register 內容傳給 server。

這項服務目的是為了可以方便監視暫存器的內容,且其顯示出的內容會 不斷的改變。 4. Debugger : 只要在clk 為 0 時,client 端傳資料給 server 這個服務會利用亂數產生正確的指令集。 5. Memory :

功能 1 : read instructions 。 client 端送 instructions 的 address 給 server 端,server 端把該位址內容(指令 之32 位元數值)傳回給 client 端。

(21)

功能2: client 端送 data address 。欲寫入的 data byteEn(寫 入 byte 個數)與寫入訊號(Wr)給 server 端.,若 Wr 為1,以 byteEn 來決定寫入 data 的 byte 個數 (0~4),且送出其 data 值給 client ; 若 Wr 為 0, server 端送出該位址內容(32 bit 的數值)給 client 端。

這項服務目的是為了可以提供硬體執行指令時,有足夠的記憶體空間來 執行指令,其記憶體空間為4Gbyte(32 bits address line)。

6. 建立反組譯後組合語言的資料庫:

功能1:讀取反組譯過後的組合語言檔案 (每個指令的 address,每個指令的 binary code,每個指令的註 解) ,即 .asm 檔。 功能 2:找出每個指令在指令表中所對映的 index。我們利 用二分搜尋法,將指令表分成兩個部分搜尋,找到 屬於那一部分後,就在那一部分再做一次二分搜 尋,如此下去直到找到指令為止。 功能 3:偵察指令位址的順序,亦即指令由小到大讀取。在 這個讀入的檔案格式中,有個欄位記錄了該指令存 入到 memory 的位址,因此我們可以藉由這個欄 位觀看每個指令的位址是否有錯。 功能 4:偵測所讀取的指令是否存在於指令表中。我們在一 開始 open 指令表時,就已經把所有指令存在一個 陣列中,因此當我們讀取這個檔案時,會與此陣列 比對其指令欄位,看看是否為一已定義的指令。 功能 5:辨別每個位址的 section。

(22)

這個服務目的是為了反組譯後,確保每個指令為我們所支援的指令,如 此才能正確的load 到硬體的部分去執行。(其函式名稱為 source 檔中的 transformELFasm)。

7. 建立指令表功能:

讀取execel 的.csv 檔(instruction table),而這個指令表是我 們參考MIPS R4000 的指令表建立而成的,不過並不包括 double word 以及 exception 的指令。

這項服務目的是為了可以方便去做指令的比對,可以直接找出該指令的 op code 及其格式,指令又可分為 I-type、R-type、J-type,我們不作 double word 和exception 的指令。

硬 體 設 計

. MIPS CPU

指令集

1. ALU 指令集

(23)

AND T : GPR[rd] ← GPR[rs] and GPR[rt]

ANDI T : GPR[rt] ← 016 || ( immediate and GPR[rs] 0 ... 15 )

OR T : GPR[rd] ← GPR[rs] or GPR[rt]

ORI T : GPR[rt] ← GPR[rs]31...16|| ( immediate and GPR[rs]15...0)

NOR T : GPR[rd] ← GPR[rs] nor GPR[rt] XOR T : GPR[rd] ← GPR[rs] xor GPR[rt]

XORI T : GPR[rt] ← GPR[rs] xor ( 016 || immediate ) SLL T : GPR[rd] ← GPR[rt]31 sa ...0 || 0sa SLLV T : s ← GPR[rs]4...0 GPR[rd] ← GPR[rt](31s)...0 || 0s SRL T : GPR[rd] ← 0sa || GPR[rt] sa ... 31 SRLV T : s ← GPR[rs]4...0 GPR[rd] ← 0s || GPR[rt] s ... 31 SRA T : GPR[rd] ← ( GPR[rt]31 )sa || GPR[rt] sa ... 31 ADD T : GPR[rd] ← GPR[rs] + GPR[rt] ADDI T : GPR[rt] ← GPR[rs] + (immediate15) 16 //immediate 0 ... 15 ADDIU T : GPR[rt] ← GPR[rs] + (immediate15) 16 || immediate 0 ... 15 ADDU T : GPR[rd] ← GPR[rs] + GPR[rt] SUB T : GPR[rd] ← GPR[rs] - GPR[rt] SUBU T : GPR[rd] ← GPR[rs] - GPR[rt] MULT T-2 : LO ← undefined HI ← undefined T-1 : LO ← undefined HI ← undefined T : t ← GPR[rs] * GPR[rt] LO ← t31...0 HI ← t63...32

(24)

MULTU T-2 : LO ← undefined HI ← undefined T-1 : LO ← undefined HI ← undefined T : t ← ( 0 || GPR[rs] ) * ( 0 || GPR[rt] ) LO ← t31...0 HI ← t63...32 DIV T-2 : LO ← undefined HI ← undefined T-1 : LO ← undefined HI ← undefined T : LO ← GPR[rs] div GPR[rt] HI ← GPR[rs] mod GPR[rt] DIVU T-2 : LO ← undefined HI ← undefined T-1 : LO ← undefined HI ← undefined T : LO ← ( 0 || GPR[rs] ) div ( 0 || GPR[rt] ) HI ← ( 0 || GPR[rs] ) mod ( 0 || GPR[rt] ) MTHI T-2 : HI ← undefined T-1 : HI ← undefined T : HI ← GPR[rs] MTLO T-2 : LO ← undefined T-1 : LO ← undefined T : LO ← GPR[rs] MFHI T : GPR[rd] ← HI MFLO T : GPR[rd] ← LO SLT T : if GPR[rs] < GPR[rt] then GPR[rd] ← 031 || 1 else GPR[rd] ← 032 endif SLTI T : if GPR[rs] < ( immediate15)16 || immediate 0 ... 15 then GPR[rd] ← 031 || 1 else GPR[rd] ← 032 endif

(25)

SLTIU T : if ( 0 || GPR[rs] ) < ( immediate15)16 || immediate 0 ... 15 then GPR[rd] ← 031 || 1 else GPR[rd] ← 032 endif SLTU T : if ( 0 || GPR[rs] ) < 0 || GPR[rt] then GPR[rd] ← 031 || 1 else GPR[rd] ← 032 endif 2. Memory 指令集 Instruction Operation LB address ← ( offset 15) 16 || offset 0 ... 15 ) + GPR[base] mem ← LoadMemory

GPR[rt] ← ( mem7+8*byte)24 || mem

byte byte...8* * 8 7+ LBU

address ← ( offset15)16 || offset 0 ... 15 ) + GPR[base] mem ← LoadMemory GPR[rt] ← 024 || mem byte byte...8* * 8 7+ LH

address ← ( offset15)16 || offset 0 ...

15 ) + GPR[base]

mem ← LoadMemory

GPR[rt] ← ( mem15+8*byte)16 || mem

byte byte...8* * 8 15+ LHU

address ← ( offset15)16 || offset 0 ... 15 ) + GPR[base] mem ← LoadMemory GPR[rt] ← 016 || mem byte byte...8* * 8 15+

(26)

LW

address ← ( offset15)16 || offset 0 ...

15 ) + GPR[base]

mem ← LoadMemory GPR[rt] ← mem31+8*byte...8*byte

LWU

address ← ( offset15)48 || offset 0 ... 15 ) + GPR[base] mem ← LoadMemory GPR[rt] ← 032 || mem byte byte...8* * 8 31+ SB

address ← ( offset15)16 || offset 0 ...

15 ) + GPR[base]

data ← GPR[rt]638*byte...0 || 08*byte

StoreMemory

SH

address ← ( offset15)16 || offset 0 ...

15 ) + GPR[base]

data ← GPR[rt]638*byte...0 || 08*byte

StoreMemory

SW

address ← ( offset15)16 || offset 0 ...

15 ) + GPR[base]

data ← GPR[rt]63−8*byte || 08*byte

StoreMemory

以下為LWL、LWR、SWL 及 SWR 指令的實際操作過程圖

LWL

(27)

memory 中以 word 為單位,而 LWL 指令對 16 bits offset 作 signed-extend 後,再 與base 相加,得到一個 virtual address,指向 word 中一特定的 byte,將此 byte 為起始位置(即圖中 address 0 的 1),之後的內容(即圖中 address 0 的 1、2、3), 由register 高位元起把值 load 進(1、2、3 內的內容會取代 register 中 A、B、C 的 內容),剩下在 register 中低位元的 byte 內容不會被改變(即圖中 register 的 D 位置 內容)。

LWR

memory 中以 word 為單位,而 LWR 指令對 16 bits offset 作 signed-extend 後,再 與base 相加,得到一個 virtual address,指向 word 中一特定的 byte,將此 byte 為起始位置(即圖中 address 4 的 4),之前的內容(即圖中 address 4 的 4),由 register 低位元起把值load 進(4 內的內容會取代 register 中 D 的內容),剩下在 register 中 高位元的byte 內容不會被改變(即圖中 register 的 A、B、C 位置內容)。

SWL

memory 中以 word 為單位,而 SWL 指令對 16 bits offset 作 signed-extend 後,再 與base 相加,得到一個 virtual address,指向 word 中一特定的 byte,將此 byte 為起始位置(即圖中 address 0 的 1),之後的內容(即圖中 address 0 的 1、2、3),

(28)

被register 從高位元起把值 store 進 memory(1、2、3 內的內容會被 register 中 A、 B、C 的內容所取代),而此 byte 之前的內容不會被改變(即圖中 memory 中 address 0 的 0 位置內容)。

SWR

memory 中以 word 為單位,而 SWR 指令對 16 bits offset 作 signed-extend 後,再 與base 相加,得到一個 virtual address,指向 word 中一特定的 byte,將此 byte 為起始位置(即上圖中 address 0 的 1),之前的內容(即上圖中 address 0 的 0),被 register 從低位元起把值 store 進 memory(0 內的內容會被 register 中 D 的內容所取 代),而此 byte 之前的內容不會被改變(即圖中 memory 中 address 0 的 A、B、C 位置內容)。

3. 跳躍條件指令集

Instruction Operation

BEQ T : target ← (offset

15) 14 || offset || 02 condition ← ( GPR[rs] = GPR[rt] ) T+1 : if condition then PC ← PC + target endif BGEZ

T : target ← (offset15) 14 || offset || 02

condition ← ( GPR[rs]31=0 ) T+1 : if condition then

PC ← PC + target Endif

BGTZ

T : target ← (offset15) 14 || offset || 02

condition ← ( GPR[rs]31=0 ) and (GPR[rs]≠032)

T+1 : if condition then

PC ← PC + target Endif

BLEZ

(29)

condition ← ( GPR[rs]31=1 ) or (GPR[rs] = 032)

T+1 : if condition then

PC ← PC + target Endif

BLTZ

T : target ← (offset15) 14 || offset || 02

condition ← ( GPR[rs]31=1 ) T+1 : if condition then

PC ← PC + target endif

BNE

T : target ← (offset15) 14 || offset || 02

condition ← ( GPR[rs]≠GPR[rt] ) T+1 : if condition then PC ← PC + target endif J T : temp ← target T+1 : PC ← PC31...28 || temp || 02

.

實作

MIPS CPU

流程圖

Write Function spec. Write Technical spec.

(30)

Develop Area/timing/power constraints Write RTL Run lint Develop testbench Measure Verification coverage

. FPGA

基本元件及功能

1. Block

內容可以由 block、embedded block、component、truth table、flow chart、 state diagram……等所組成,為一階層式的構造,即每一層下可能還會有另一層。

2. Embedded Block

要對 input、output 訊號的關聯作描述,但 input 訊號並不需要有許多過程才 能得到optput 訊號,此時,即可使用 embedded block,作單純描述的動作。例如: input 訊號名稱為 a 和 b,output 訊號名稱為 c,c 的內容為 a 的內容放高位元、b 的內容為低位元,即c <= a & b,此時就只要利用此 block,就可以得到我們想要 的結果。

3. Component

和 block 極為類似,為一不同點即,component 可 re-usable,而 block 不行, 所以像logic gate 這類使用頻繁的元件都是用 component 所畫成,並可把它改變 成所想要的形狀。

4. 產生 VHDL Code

Synthesize Simulate

Write creation guide Pass – ready for integration

(31)

當架構圖畫完之後,只要利用工具列上的 HDL → Generate → Hierarchy Through Components 即可自動產生相對應於架構圖的 VHDL Code,再利用工具 列上的HDL → Compile → Hierarchy Through Components 去檢查 VHDL Code 是否有語法錯誤,例如:訊號跟訊號間bit 個數是否吻合。

5. 利用 ModelSim 做模擬

當上述產生 VHDL Code 內的兩項動作皆完成,且並沒有發現任何錯誤 時,即可利用工具列上的Simulation → Start Simulator 去開啟 ModelSim 對整個 架構圖作Debug,至於 Debug 實際過程,則會在 Chapter 5 的第二點詳細說明, 在此不再贅述。

. OpenMips

模擬系統架構

1. 整體架構

整個 OpenMips 最外層,也就是我們所模擬的 MIPS CPU,

OpenMips_testbench,是由一個 component(simSystem)與 eb1(enbedded block)所組 成。SimSystem 包含了 CPUcore 與 memory interface;CPUcore 是 OpenMips CPU 的核心,為pipeline 架構,處理所有的指令;memory interface 是模擬 CPU 中的 記憶體,並透過一個memory function 提供對 memory 所做的存取動作。

2. openMIPS-testbench

(32)

eb1 的內容用 verilog( 硬體描述語言 )寫成,描述並輸出 clk,reset, disMem 這 3 個 input 給 simSystem。這三個 inputs 的特性如下:

clk (clock) => 每 5 個 ns 振盪一次,振盪 block , register and WbPC

的strobe(探針) 與 debug function (check code)。

disMem(disable memory) => 當 disMem 為 0 時,會有 cache miss

的情況發生,反之,當其為1 時,就沒有 cache miss 的情況,有這 個訊號,可以就這兩種情況,分別去做debug,直接經由 embedded block 來控制 disMem 的值。 reset => 在正邊緣觸發,所以當 reset 為 1 時所有訊號的值將會歸 0,為 0 時無變化。 3. SimSystem 的架構(硬體模擬系統)

(33)

元件1. CPUcore: OpenMips CPU 的核心,提供 instAddr,dataAddr,dataout,

pMemwr,pMemByteEn,pMemSel 這些 input 給 memInterface;另外 EPC,copAddr,wbData 和 copInst 給 coProcessor。

以下針對每個訊號加以說明:

instAddr(instruction address) => 在 momory 中所要讀取的指令

的位址。

dataAddr(data address) => 在 memory 中所要讀取的資料的位 址。

dataOut => 對 memory 作輸入的資料。

pMemWr(write memory) => 為 1 時對 memory 作寫入的動作。

pMemByteEn(memory byte enable) => 對 memory 作存取的資

料量 (byte,half word,word)。

pMemSel(memory selection) => 選擇對 memory 的動作(load,

store)。

(34)

些input 給 CPUcore。 其訊號分別為:

dataIn => 由 momory 輸出資料給 core。

OpCode => 輸出 instruction 內容。

pInstDone (instruction done) => 為 1 時表示指令正在執行。 pMemDone(memory done) => 控制 memory 是否能做存取的動

作。

4. memory interface

元件1. memoryShell : 提供一個 combinational 的 2 ports memory

function ,將程式資料由軟體輸入,並把輸出值傳到元件 2 memory delay 中。

元件2. memDelay: 當 disMem = ‘0’時,提供了一個不規則的記憶體取存

取速度( 亦即不規則的 instDone 與 memDone ),若 instDone 或 memDone 為”1”,則 instruction 或 data 將會被傳給 CPU

(35)

cycle ,CPU 才會動作 )。 DisMem = ‘1’時,則是提供理想中的 memory 存取速度,沒有 memory miss 的現象會發生。

5. CPUcore

架構: 5 個 stage 的 pipeline ,分別為 fetch , decode , execution , memory , wirteback 。

組成的block 共有 10 個:

fetchUnit,FD_Register,ControlUnit,regFileUnit,forwardUnit, DE_Register, ALUUnit,EM_Register,MemoryUnit,MW_Register (請參閱附錄一)

元件1. fetchUnit : 捉取指令,增加 program counter ( PC );有三種增加 PC

值的方式:一般指令時NPC = PC + 4;jump 和 branch 指令時 NPC = PC + immediate 值。作 store 時 NPC = PC +0。程式一開始,PC 初始值為 initPc,也就是程式的起始位置,可利用 embedded block 去改變初始

(36)

值;程式執行期間PC 值則會選擇 pc 這一條訊號。 .

元件 2. FD_Register: 儲存 fetch stage 之 output,裡面是由多個 and gate 所 組成,當pipeEn 這個訊號為’1’時,fetch stage 的所有 output 就會 通過,傳到下一個stage。若 pipeEn 為’0’則 data 便會被 hold 在這 個Register 中。(請參閱附錄二)

元件3.ControlUnit: 對於截取到的指令做解碼的動作並控制目前 cpuCore

中每一個state 的狀態。內有五個 block,分別為 combStartUnit, combController.,cuBrhUnit,seqController 以及 enCtrlSignal;

1. combStartUnit

為一個truth table 判斷指令中的 opCode 與 Function 給定乘法與除法 start 的訊號;

(37)

號:

combImmSel(immediate selection) => 選擇 decoder stage 的 rt 值來源― fetch stage(R tyoe 指令)

從immMSB(LUI 所需要的 immediate 值) 從immValue(I type 指令 sign immediste) 從dirValue(I type 指令的無號 immediate 值)。

combSelRdAddr(select Rd address) => 決定要寫入指令中 rt 或rd 位置的暫存器。

combDeWbEn(decode write back enable) => 決定是否將最後 結果寫回register。

combDeFunc(decode function) => 控制 ALU 的運算。 combDeMemWr(deceode memory write) => 決定是否寫入

memory 的訊號。

combDeMemFun(decode memory function) => 對 memory unit 的動作,例如 store、load……等。

combDeMemSel(dememory select) => 決定是否由 memory 輸 出data

selBrhFun(brench function) => 判斷是否為 branch 的指令。 brhOpEn(brench Opcode Enable) => 檢查 branch 是否有

taken。

3. cuBrhUnit

同樣也是一個 truth table,由 brhFun 決定以下輸出

訊號:

combPcInc(Pc Increse) =>決定是否增加 PC 值 combEnRs(Enable Rs) =>決定是否 stall 住 Rs combEnRt(Enable Rt) => 決定是否 stall 住 Rt

(38)

指令去做跳躍的動作

combPcEn(Pc Enable) =>決定是否讓目前的 PC 值傳遞下去 combPcWr(PC Write) =>決定是否對 PC 做增加的動作 combDeBrhSel(branch select) =>用來表示各種 bramch

function

* combJmpImmSel(jump immediate ) => 選擇所下一到指令

的位置( Npc , brench address or jump address )

* combEnNpc(Enable Next pc ) =>決定是否繼續執行下一到

指令

4.

seqController

=> 為一個 finate state machine,分別用各個 stage

來給定現在CPU 的狀態。其中 mdone 的意義是為了要等待乘 除法,memory miss 與 branch ,jump 的處理時間,當 mdone = 1 時,表示一切準備就緒,開始抓取指令,然後再經過 mdone = 0,進入 waitmem 等待 memory 處理完成,再進入 genOpcode 進行下個stage 的動作。

(39)

利用 finate state machine 裡的狀態來控制以下五個訊號:

seqCtrlEn (control enable)=>決定是否要讓所有 control Unit 的訊號

通過。

seqPcInstSel (PC instruction select) => 選擇目前 PC 的狀態。 seqPcSel (Pc select) =>選擇 PC 的增加方式。

pipeStartEn (pipelome start enable) =>決定是否繼續執行 pipeline

中的stage。

ftEn(fetch enable) =>決定是否再抓取下一個指令。

5. enCtrlSignal

將剛剛四個block 的訊號組合起來,輸出 Control

Unit,根據訊號的內容來控制整個 core 的行為。(請參閱附錄三)

元件4. regFileUnit: 儲存了 32 個 general registers 的內容並提供其輸入/輸出。

依據指令的需求,利用control unit 所送 出的控制訊號,決定是否輸出 Rs、Rt 位址的 registers 內容或者是否要寫入 Rd 位址的 register。 (請參閱附錄四)

元件5. forwardUnit: 提供解決 data hazard 的 forward 功能,(把 execution

stage 或 memory stage 的結果 forward 給 decode stage),並將 memory stall 的控制訊號給 controlUnit.。(請參閱附錄五)

data hazard 的發生:當 memory rd address 或 excution rd address 不為零

且指令一定會對register 做寫入動作的條件下,memory stage 或是 execution stage 的 rd address 與 decode stage 的 rs , rt address 其中之一相 同時,就會發生 hazard 現象。因此將 excution 或 memory stage 的結果 forword 給下面的指令。

(40)

時,無法用forword 解決,便產生 stall 訊號,將 pipeline 停住,塞一個 nop 的 指令。

元件6. DE_Register : 儲存 decode stage 的 output 訊號,並在下一個 clock

cycle 給 execution stage。(請參閱附錄六) .

元件7. ALUUnit: 提供算術邏輯運算以及跳躍指令的判別運算。

在算數邏輯運算的部分有logic_shift,add_sub,multiplier,divider 四個 主要的component:.

1. logic_shift 分為 logicUnit 和 shiftUnit:

(41)

shiftUnit 利用多工器作左移或右移的動作,再根據指令決定補 0 或 sign

extend。(請參閱附錄七)

2. add_sub 利用 ripple_carry adder 作成,並由 equal component 做 flag checking 的動作,sum 是否為 0,即 zero flag 為 1;若為 sign 的運算, 則檢查是否有overflow; unsign,則檢查是否有 carryout。

(42)

此為flagChk 內的結構圖:

(43)

controlUnit: 是一個 finatestate machine,送出兩個控制訊號 ld,sh;ld

控制何時讀入x 的初始值,ld&sh 做成一個兩個 bit 的訊號,控制要 輸出的結果,01=>選取初始值 yResult (補了 n 個’0’後的);10=>選取 sumResult(算完乘法的最後結果)。當狀態為 idle 時僅表示把值 hold 住,實際上並無做任何動作,為load 時,把乘數和被乘數載入 register,在 act 時,則進行真正的運算

DataUnit; 裡用了兩個暫存器分別包在一個加法器的前後,由於使用

booth 演算法來做乘法,所以在加法器前要加一個 booth 演算法的 block。(請參閱附錄八)

Divider 內有兩個 block 分別為 dividerCtrl 和 dividerData;

1. DividerCtrl: 為一個 finate state machine,由 7 個 state 分別表示

stateOp 的訊號,用來控制目前 dividerData 的狀態;

Idle => 僅表示把值 hold 住,實際上並無做任何動作 Load => 把除數和被除數載入 register;

(44)

CorrectD、correctS => 由於我們使用 restoring 的演算法,

因此需要這兩個state,將除數與被除數取絕對值,當作 unsign 的除法;

act state => 就真正進入除法的運算過程;

correctR, correctQ =>由於前面的 state 取了絕對值,所以要

根據原本除數與被除數的正負號,去更正餘數及商的正 負。

2. DividerData: 中有 divSignalUnit 與 crtUnit 兩個 block。

DivSignalUnit => 是一個 turth table,利用上述 7 個 states 來控

制stateOp 以及所要做的動作。

CrtUnit => 則是用來判斷 correctR 及 correctQ 時所需做的更

正。一開始的regMUX 是用來 hold 住除數的值或是預算過 程中的結果。中間利用一個加法器與兩多工器來做除法所需 要的運算,最後再利用equal component 以及 AND 邏輯閘去 判斷餘數是否為零。(請參閱附錄十)

(45)

整合mipsALU

在 mipsALU 中把 logic_shift、add_sub、Mutiplier、divider,各自包成 component,並利用 func 這個訊號,去決定此時會使用到哪一個

component。由於乘法與除法的 component 都是做 sign 的運算,但是指令 中有分sign 與 unsign,因此需要分別補零或做 signextend 再送入 component 中。此外,Multiplier 的積會分別送到 hi 與 low register,divoder 的商和 餘數也分別送入low 與 high register,因此另設兩個 register 來存放結果。

在Jump and Branch 指令的特別運算,以邏輯閘分別表示不同的 brench 指令,並在 brhDecoder block 內建一個 truth table 來判斷是否 brench taken。(請參閱附錄十)

(此圖為 pipeline 中的 ALU stage)

元件8. EM_Register : 儲存 execution stage 的 output 訊號並在下一個

(46)

元件9. MemoryUint:包含 memByteUnit 和 memSelUnit 兩個 block,皆為 truth

table,提供 memory function 處理(byte,half word ,word 與 angle…)以及 把其相關之內部訊號送給 cupCore 的 input 與 output。

1. MemByteUnit =>在這個 Unit 中,由 bytefunc ( memfunc 和

mem address 組成的訊號 )來判別 memory function,輸出 byteSel0~3 選定data 內容;當我們在做 load 的指令時,是將 memory 中的 data 存入register 內;sotre 的指令則是由 register 的內容入 memory 中。 * 因為 LWL,LWR,SWL,SWR 這些指令都是以四個 byte 為單位, 所以我們把一個byte 視為一個單位,再用四個 MUX5s 去選出一個 新的資料內容,並利用memByteUnit 做控制,其中 output 訊號 lsMemByteEn 決定對 memory 做存取的 byte 個數。

2. memSelUnit =>由 memFunc 與 lsMemByteEn 這兩個訊號來

送出對memory 的控制訊號,如輸入或輸出的位置,byte 個數及排 列方式。(請參閱附錄十二)

元件 10. MW_Register: 儲存 memory stage 的 output 訊號並在下一個 clock cycle 給 regFileUnit 。(請參閱附錄十三)

.

驗證與除錯

1. ALU Function 驗證

使用 ModelSim 產生 WaveForm

在FPGA 畫完 logic 的架構後,我們利用 ModelSim 做模擬,驗證 一下是否能得到我們要的結果。將modelsim 的指令以及我們所要 測試的數值結合成 .txt 檔,如下:

add wave rsValue add wave rtValue

(47)

add wave logicFun add wave logicResult restart -f force rsValue 2#0001001110111100 force rtValue 2#0001000100111100 force logicFun 2#01 run 10 add wave =>是將所要檢查的訊號加入波形圖中 restart –f =>清空波形圖內的資料 fource =>是給定所要輸入的數值 run 10 =>執行時間為 10ns 進入modelsim 模式 source 這個 .txt 檔,便會自動產生一個波形圖, 接著只要檢查 logicResult 的訊號值是否為真正的答案,即可知道 此架構正確與否 。其他component 也是依照此種方法來做驗證的動作,但以上這些 動作,所做的檢查只能根據抹些特定的boundary 值做驗證,非包 含所有狀況。因此需要用一個C 的程式還產生大量的數值來做比 對。 亂數比對(與 C code 的結果做比對) 首先,根據logic_shift 的 function 寫一個 C 語言的程式,用亂數 產生各種logic,shift 的情況及數據,將其印在 .txt 黨 。 在將 logic_shift 這個 component 與 eb 結合成一個 testbench,其中 eb 的內容是用verilog 語言所寫成,主要的作用是由 modleSim 讀入 C 的 .txt 檔,作為 testbench 的輸入,經過電路圖的運算得到輸 出,並與C 的 .txt 檔的結果做比較,如果符合,就表示我們的電 路圖是正確的。以下是Verilog 的部分內容:

(48)

initial begin …… $strobe(); $readmemb("logic_shiftTest.txt",testPattern); for(i=0;i<3100;i=i+1) begin #10 testVector=testPattern[i]; ifLogic=testVector[62]; rsValue=testVector[61:44]; rtValue=testVector[43:26]; saValue=testVector[25:21]; shLogicFun=testVector[20:19]; shVariable=testVector[18]; result=testVector[17:0]; #10 $display("i:%d",i);

if(testVector[62]==1 && logicResult!=result) begin

$display("Logic Error=>rsValue:%b rtValue: %b ……);

$stop(); end

if(testVector[62]==0 && shiftResult!=result) begin

$display("Shift Error=>rsValue:%b rtValue:%b ……); $stop(); end end #100 $stop(); end 指令說明: $stobe => 輸出每一條訊號線的值

$readmemb => 把 txt 檔的內容放到 testPattern(為一 array)的變數 內

$display => 將其誇號內的訊息印出

※if(testVector[62]==1 && logicResult!=result) begin

$display("Logic Error=>rsValue:%b rtValue:%b ……); $stop();※

(49)

這段程式是在比對由.txt 檔所得到的 result 與電路圖產生出的 logicResult 是否相等,若不相同,則印出訊息並中斷比對的動作。 將.txt 檔放在 work 目錄下,並開啟 modelsim,執行 run –all 的指 令,modelSim 便會將整個.txt 檔讀入電路裡,計算結果進行比對。

2. OpenMips_testbench 的驗證

當我們完成OpenMips_testbench 的架構之後,要將我們的軟體架構 serverplatfrom 與硬體的電路圖做連結,驗證 CPU 是否正確運作。首先 要先利用gcc 將 C 的程式 compile 成組合語言(.C -> .S -> .O -> .ASM), 再將.asm 檔 source 進入 severplatform 轉換成機器語言(01010101……)。 再透過一個.DLL 檔與 PIL 程式跟 modelSim 做連結,將機器語言送入 OpenMips_testbench 進行處理。在這之前先寫一個 CPULoad.txt 的程式, 作用主要是將Socket 打開透過 PLI 程式跟 serverplatform 建立連結的路 徑;另外再寫一個CPUView.txt 的檔案將我們所要檢查的訊號加入 waveform。準備就緒後,給定執行的時間,由 modelSim 啟動整個系統; 當系統跑完了以後,會產生一個Waveform 的圖示,顯示出所有我們給 定的所有訊號,包含輸入與輸出;同時在serverplatform 中的 memory 與 register 的視窗會顯示出現在的 register 內容及 memory 的內容。

開啟serverplatform 開啟 modelSim source 再將 CPUView.txt 產生.S 檔 將.s 檔轉為機器語言 CPULoad.txt 與 source 進 modelSim

serverplatform 做溝通 , run 整個連結

由於我們為了要檢查每個指令是否運作正常,以及指令執行時的狀況, 例如:branch 指令發生時 taken 或 not taken 時下一個指令的位置有何 不同,或是產生stall 的情況。因此我們自己編輯 .S 檔去驗證

OpenMips_testbench 是否能符合我們的要求。 (所使用的軟體畫面請參閱附錄十四)

(50)

問題 與 討論

一.

Brebch

指令的判斷條件

BEQ => zero

BGEZ => not Rs(MSB) | zero BGTZ => not zero & not Rs(MSB) BLEZ => Rs(MSB) | zero

BLTZ => Rs(MSB) & not zero BNE => not zero

二.

Nonrestoring Divider

實作的困難

我們一開始作除法器所使用的演算法為 nonrestoring 演算法,以下為其內容 nonrestoring divider:有兩個 block,分別為 divCtrl 與 divData。

divCtrl:利用 finatestate machine ,一樣也是送出兩個控制訊號,ld 和 sh

控制divData 裡的資料。

ld 是兩個 bit 的訊號,控制所要選取的 data 為 sResult(做過 signextend

後的被除數初始值)、actResult(除法運算過程的結果)與 ctrlResult(要做 correct 的結果),其中 ld(0)在決定現在除法是在 action 或是 correct 的動 作。

(51)

divData :內有三個 block,分別為 divOpt , correctUnit 和 actionUnit

correctUnit 中,因為計算出來的餘數必須和除數同號,因此利用 [d(n) xor Q(n-1) xor d(n)] and {R(n-1) xor [d(n) xor Q(n-1)]}這個布林 函式去判斷,如何做correct ,利用 dff 去 hold correct R 的訊號 ,繼 續correct Q。

actionUnit 只單純判斷 s & d 的 MSB 為同號或異號,同號相減異號相加。 divOpt 由於 divCtr 中 finatestate machine 的定義,因此所做的動作只是

由 [ld(0) and not ld(1)]產生一個 ldD 的訊號。再除法一開始讓被除數 的值穿過register,之後將被除數保留在 register 內

divData 最初利用 ldD 所控制的 register 來判斷是否該讓被除數的值穿過 或是hold 在 register 內,接著再利用 ld(0)透過 MUX2s 去選擇目前所需使 用的數值由correctuUnit 或 actionUnit 而來,最後在使用加作器作真正的 運算。

實作完成後,在 debuge 時,發現當餘數為零時,經過 correct 的動作後,反 而使餘數和商數產生錯誤現象,因此需要在增加component 去額外判斷餘數 為零的情況,但是此動作反而會造成運算時間的增加以及面積的增大。在仔

(52)

細考慮之下,決定改用restoring 演算法代替。(請參閱附錄十五)

三.

Restorin Diveder

對商數與餘數的

correct

依據

假設運算式為S/D=Q…R 舉一個例子來說明 S D Q R ( 1 為負,0 為正) 19 / 3 = 6 …… 1 0 0 0 0 (-19)/ 3 = -6……. –1 1 0 1 1 19 /(-3) = -6 …… 1 0 1 1 0 (-19)/(-3) = 6…….-1 1 1 0 1 將右圖視為一個truth table 即可得到 Q = D xor S ,. R = S 也就可以依據上面兩個條件式解決此問題。

這是個很有挑戰性的專題,不僅僅著重在單一的軟體或硬體方面,而是著重在 軟體和硬體間的溝通協調,我們利用 TCP/IP 的觀念做為兩者之間的溝通的橋 樑。經由實作這個專題,整合了我們大學三年所學的基本概念,包括計算機概論, 組合語言,數位系統,網路程式規劃,演算法,計算機結構學。在將理論轉為實 作的過程中,讓我們對 CPU 有更進一步的認知,並且更加了解設計 CPU 的複 雜度與要面面俱到的困難。同時在過程中學習到如何利用各種方法來解決不同的 問題。

(53)

使

用 手 冊

目的:本手冊之目的為協助使用者方便始用本應用程式,且將本應用程式的功能 指令一一簡述,讓使用者更能得心應手。 操作介面介紹: 視窗一:console 區,使用者鍵入的指令及電腦執行後的訊息都會顯示在此。 視窗二:register 區,暫存器的內容會顯示在此。

視窗三:I/O device 區,I/O 的狀態會顯示在此。 視窗四:memory 區,記憶體的內容會顯示在此。

視窗五:command 區,使用者將指令鍵入於此,其指令整理如下:

指令名稱 指令功能 clear –console 清除console 區的內容

save –console file_name 將console 區的內容存至名為 filename 的 檔案

(54)

此處的table_name 為我們的指令集 view -instTable number 查看在指令集中的第 number 筆的指令

及其所有資料;若number 大於指令集的 所有個數,則會顯示出所有指令集的資料 find -inst instruction_name 在我們 open 的 table 中尋找所鍵入指令

名稱的指令

test -regfile 測試暫存器之檔案

source file_name 以輸入之檔名為來源檔,內容可以為多個 指令的集合,此處所指之指令為命令列所 使用之指令

Setenv asmArraySize number 動態配置一個記憶體空間,存放.asm 檔的 資料

Setenv enReg 開啟register 的監控視窗 Setenv disReg 關閉register 的監控視窗 Setenv enInst 開啟cpu 指令的監控視窗 Setenv disInst 關閉cpu 指令的監控視窗

open -asmELF file_name 開啟名稱為file_name 的.asm 檔,將它載 入我們所動態配置的記憶體空間

view -asmELF number 查看asmELF table 的第 number 筆資料 view –regfile 查看目前暫存器內容

view –memory number 從number 的位址開始查看目前記憶體內 容

save -asmELF file_name 將asmELF table 存到名為 file_name 的檔 案

socket -time number 設定socket time 的時間長短 help 快速查詢所有指令

Note 1:

查看我們自己定義的instructions Table 之內容時,需先執行 open -instTable table_name 此一指令,如此才可查詢正確的 instructions table 內的指令。 例如:

open -instTable inir.csv 將inir.csv 的 table 開啟載入 Reading file ok (121 instructions read) !!

view -instTable 66 查詢第 66 個指令(若數字大於等於 121,則會顯示出所有指令的內容)

(55)

find 也是同樣的方式,然後再鍵入要查詢的指令名稱即可。 例如:

open -instTable inir.csv 將inir.csv 的 table 開啟載入 view -inst add 查詢add 指令

Note 2:

查詢.asm 檔案的內容時則必須先開啟.csv 的 table 以作指令比較,然後再 設定asmArraySize 的大小後,才開啟要載入的.asm 檔案。

例如:

open –instTable inir.csv 在開啟.asm 的檔案之前,須先開 啟.csv 檔以作比較指令之 table setenv asmArraySize 11000 設定 asmArraySize 的大小

open -asmELF testAsm1.asm 將 testAsm1.asm 的檔案開啟載入 Note 3:

source file_name 其功能像 script,在一個 .txt 檔中,將多道指令先建立於 此檔案中存起來後,只要在command 區打上 source file_name,OpenMips 就會去執行裡面鍵入的每一道指令,方便測試或重複使用,使用者也可以 自行建立此類型檔案。

例如:

testAsm1Src.txt 的檔案內容: open -instTable inir.csv

view -instTable 200

setenv asmArraySize 11000 open -asmELF testAsm1.asm save -asmELF testAsm1.txt

使用者只要自建一個內容是我們所提供的指令綜合的檔案,就可以利用 source 的功能來節省時間。

以下為示範操作:(以 testAsm1Src.txt 為示範檔)

step 1. 先開啟 OpenMips 的操作視窗介面

step 2. 在 command 區鍵入 testAsm1Src.txt

第一道指令 可以和第二 道指令交換

(56)

step 3. 開啟 FPGA Adv.

step 4. 打開 openMIPSv1 的 library name,選擇 openMIPS_testbench

step 5. 在開啟出現的視窗選擇 HDL->Generate->Hierarchy Through Components 按下,若在Log Window 中出現 Generation completed successfully 的訊息, 即表示HDL 檔被成功的產生了

step 6. 再回到原來 openMIPSv1 testbench 的視窗,選擇 HDL->Compile->Hierarchy Through Components,則會出現同上面的 Log 視窗,不過顯示不同的訊 息,其成功訊息為:

Data preparation step completed, check transcript...

表示上述步驟中所產生的 HDL 檔並沒有錯誤,倘若真的發生錯誤,也可 以利用HDL->View Generatd HDL 打開 HDL 檔去檢查到底是哪部分出錯。

step 7. 再一次回到原來 openMIPSv1 testbench 的視窗,選擇 Simulation->Start Simulator,則會出現一個對話視窗,請直接按 OK,不需要選擇任何東西: step 8. 在出現的新視窗 ModelSim 的視窗中鍵入以下兩道指令: 1.source cpuload.txt 2.source cpuview.txt 然後會出現wave 視窗: source testAsm1Src.txt

(57)

step 9. 在 ModelSim 中再鍵入 run 15000,這指令是要跑 15000 ns,會出現 下圖的波形圖:

(58)

而且openMIPS 的視窗也會顯示出狀態如下圖:

如果要重新再run 一次,在 ModelSim 的視窗內鑑入 restart –f,則波形視 窗的內容就會被清除。

!!!Note!!!

在執行ModelSim 的部分時,絕不可將 openMIPS 的 server 視窗關掉,

(59)

考 文 獻

1. Computer Organization & Design

作者:David A. Patterson and John L. Hennessy 2. MIPS R4000 Microprocessor User’s Manual

作者:Joe Heinrich

3. MIPS RISC ARCHITECTURE

作者:GERRY KANE AND JOE HEINRICH 4. Verilog 硬體描述語言(Verilog HDL) 作者:SAMIR PALNITKAR 黃英叡 黃稚存 張銓淵 江文啟編譯 5. Windows 程式開發設計指南 第五版 作者:Charles Petzold 余孟學編譯

6. Internetworking with TCP/IP 第 2 版

(60)

(61)

附錄二:圖一為FD_Register 之架構圖,圖二則為其中一訊號通過 register component 的情況。

圖一

(62)

附錄三:圖一為enCtrlSignal 之架構圖,圖二則為其中一訊號通過邏輯閘 AND 的情況。

圖一

(63)
(64)
(65)

附錄六:圖一為DE_Register 之架構圖,圖二則為多 bit 之訊號的 component 通 過register 的情況,圖三則為單一 bit 訊號的 component 通過 register 的; 情況。

(66)

圖二

(67)
(68)
(69)
(70)
(71)

附錄十一:圖一為EM_register 之構造圖 ,圖二則為多 Bit 訊號通過 register component 的情況,圖三則為單一 bit 訊號通過 register component

圖一

(72)

圖三

(73)
(74)

附錄十三:圖一為MW_Register 之構造圖,圖二則為多 bit 訊號通過 regiter component 情況,圖三則為單一 bit 訊號通過 regiter component 情況

(75)

圖二

(76)

附錄十四:圖一的memory 視窗是將 CPU 讀取 memory 指令的內容,圖二的 memory 視窗則是顯示 memory 資料區的內容,圖三 WaveForm。

圖一

(77)
(78)

附錄十五:圖一為nonrestoring divider 中 divData 之架構圖,圖二為 divOpt 的內 容,圖三為 action Unit 的內容,圖四是 correct 的內容

(79)

圖二

圖三

參考文獻

相關文件

則察看自己的 cache 是否有紀錄,若否才前往 root(.)3. DNS 主機會先將該查詢記錄記憶在自己的

mov ax,var1 ;將其中一個記憶體內容先複製到暫存器 xchg ax,var2 ;分別執行記憶體與暫存器內容的交換動作 xchg ax,var1 ;完成交換。 Swap var1

工程數目 Número de projectos de construção Number of construction projects 工程數目 Número de projectos de construção Number of construction projects 公共工程 Projectos

• Description “pauses” story time while using plot time; there can be a nearly complete distinction between the forms of time.. • Ellipsis skips forward in story time while

Joint “ “AMiBA AMiBA + Subaru + Subaru ” ” data, probing the gas/DM distribution data, probing the gas/DM distribution out to ~80% of the cluster. out to ~80% of the cluster

•  Flux ratios and gravitational imaging can probe the subhalo mass function down to 1e7 solar masses. and thus help rule out (or

MOV reg,data reg ← data 轉移立即資料(data)到暫存器 reg 內 MOV dreg,sreg dreg ← sreg 轉移暫存器 sreg 的內容到暫存器 dreg MOV segreg,reg segreg ← reg

直線方程式從 11 年級挪到了 10 年級,看似恢復了 95 暫綱的安排,但是本條目的設計 內涵還是比較接近