Vivado實戰—單週期CPU指令分析

程式設計師傑森發表於2021-08-31

引言

  不知道你是否和我有過同樣的感受,《計算機組成原理》這門學科學起來如此的艱難:一節課下來,教室黑板上留下了滿滿的 “足跡”,看上去也挺簡單的,不就是 0 和 1 嘛。但這些看起來簡簡單單的 0 1 碼卻成為了我當時學習路上的絆腳石。原始碼、反碼、補碼等等等等,各種的碼制轉換令我一頭霧水,我曾一度懷疑這就是計算機乾的活兒嗎?

  隨著後面慢慢了解《計算機組成原理》後,我願稱計算機為世界上最麻煩的電子產品。也形象的將計算機描述為一套有電源、有身體、有框架,但就是沒有思想的空殼兒。這時候,我們的人類社會才萌生了許許多多“ 利用 ”它們的主人—程式“ 猿 ”。

  程式設計師就是要為計算機這個富有 ” 聰明 “潛質的夥伴編寫一套一套的 “程式” ,告訴它們應該去做什麼亦或是怎麼去做。正是由於它們能吃苦、不怕累的精神再加上完美程式的契合,我們的生活質量得以大幅度提升。
請新增圖片描述
  即使你不是計算機專業的學生,相信對於CPU你也有所瞭解。正所謂CPU(Central Processing Unit),也就是中央處理器。負責解釋計算機指令以及處理計算機軟體中的資料。中央處理器主要包含控制器、運算器兩部分,其中還包括cache以及實現它們之間資訊互換的資料、匯流排。
  可千萬別小看這個不起眼的 “小東西” ,它可是程式執行、機器正常運轉必不可少的元器件。作為電子計算機三大核心部件(CPU、記憶體、I/O裝置)之一的CPU,在計算機體系結構中承載著控制調配硬體資源、執行通用運算等重要職責。下面我們來了解較為簡單的單週期CPU功能指令。


上機實驗

分析實現單週期CPU的14條指令計算結果,比較理論與實踐結果的正確性。

Addi:

0x00000000	addi  $1,$0,8	000001	00000	00001	0000 0000 0000 1000	=	04010008  分析第一個週期,指令地址為041008,在程式測試段中,可以看到,本條指令為addi,那麼現在開始檢驗指令addi的實現過程:指令addi的功能是rt←rs + (sign-extend)immediate; immediate符號擴充套件再參加“加”運算。

  首先找到rs在本週期中存放的源運算元地址,rs=00;然後在Objects中新增immediate元件,找到立即數的值,immediate=8;最後進入ALU分析計算結果,在ALU的程式碼檔案中有兩個引腳,ALUSrcA和ALUSrcB,它們在第一個週期中的值分別是0和1,則A埠進行ReadData1,B埠進行extend,計算結果為result=8,指令執行正確,可以實現資料相加功能。
在這裡插入圖片描述

Ori:

0x00000004	ori  $2,$0,2	010000	00000	00010	0000 0000 0000 0010	=	40020002
  分析第二個週期,指令地址為40020002,在程式測試段中,可以看到,本條指令為ori,那麼現在開始檢驗指令ori的實現過程:指令ori的功能是rt←rs | (zero-extend)immediate; immediate符號擴充套件再參加“或”運算。暫存器源運算元地址:rs=0,immediate=2;
進行zero-extend後,值為2
在這裡插入圖片描述
進行“或”運算,ALU兩個引腳輸入埠值為
在這裡插入圖片描述
計算結果為
在這裡插入圖片描述指令計算結果無誤,指令執行正常。

Add:

0x00000008	add  $3,$2,$1	000000	00010	00001	0001 1000 0000 0000	=	00411800
  分析第三個週期,指令地址為0411800,在程式測試段中,可以看到,本條指令為add,那麼現在開始檢驗指令add的實現過程:指令add的功能是rd←rs + rt。源運算元地址:rs=2,rt=1;ALU兩個引腳值都是0,讀暫存器中的值,此時存入結果為03,程式執行正常,功能實現。
在這裡插入圖片描述

Sub:

在這裡插入圖片描述  分析第四個週期,指令地址為08622800,在程式測試段中,可以看到,本條指令為sub,那麼現在開始檢驗指令sub的實現過程:指令sub的功能是rd←rs - rt。源運算元地址:rs=3,rt=2;ALU兩個引腳值皆為0,程式讀取暫存器中的值,此時存入結果為5,程式執行正常,功能完好。
在這裡插入圖片描述

and:

在這裡插入圖片描述
  分析第五個週期,指令地址為44a22000,在程式測試段中,可以看到,本條指令為and,那麼現在開始檢驗指令and的實現過程:指令and的功能是rd←rs & rt;(邏輯與運算)。源運算元暫存器地址:rs=5,rt=2;ALU引腳值為0,讀取ALU計算結果為00000000,分別對源運算元5和2轉為2進製為:0101、0010按位與後值為0,結果吻合,該單元工作正常,功能實現。
在這裡插入圖片描述

or:

在這裡插入圖片描述
  分析第六個週期,指令地址為48824000,在程式測試段中,可以看到,本條指令為or,那麼現在開始檢驗指令or的實現過程:指令or的功能是rd←rs | rt;(邏輯或運算)。源運算元暫存器地址:rs=04,rt=02;ALU雙引腳值為0,讀取暫存器堆中的值,可以看到存放真值為0和2(取最後結果)。讀取值為2。

  分別對源運算元4和2轉為2進位制並按位或後值為2,與程式執行結果吻合,該單元工作正常,功能實現。
在這裡插入圖片描述

sll:

在這裡插入圖片描述

  分析第七個週期,指令地址為60084040,在程式測試段中,可以看到,本條指令為sll,那麼現在開始檢驗指令sll的實現過程:指令sll的功能是rd<-rt<<(zero-extend)sa,左移sa位 ,(zero-extend)sa。源運算元暫存器地址:rt=08 ,尋求真值,移位數sa=1,程式執行後 。在二進位制資料計算中,左移一位就相當於乘以2,故程式結果正確,該單元工作正常。

bne:

在這裡插入圖片描述

  分析第八個週期,指令地址為c501fffe,在程式測試段中,可以看到,本條指令為bne,那麼現在開始檢驗指令bne的實現過程:指令bne的功能是:if(rs!=rt) pc←pc + 4 + (sign-extend)immediate <<2 else pc ←pc + 4【與beq不同:不等時轉移,相等時順序執行。】在暫存器堆中找到源運算元地址,尋得真值,找尋計算結果,結果吻合,單元工作正常。
在這裡插入圖片描述

slti:

在這裡插入圖片描述
  分析第九個週期,指令地址為6c460008,在程式測試段中,可以看到,本條指令為slti,那麼現在開始檢驗指令slti的實現過程:指令slti的功能是:if (rs <(sign-extend)immediate) rt =1 else rt=0,源運算元地址找到後,對立即數進行sign-extend,然後判斷是否滿足if()中的給定條件,程式結果完整無誤,本單元工作正常。
在這裡插入圖片描述

beq:

在這裡插入圖片描述
  第14個週期中,指令地址為c0e1fffe,在程式測試段中,可以看到,本條指令為beq,那麼現在開始檢驗指令beq的實現過程:指令beq的功能是:if(rs=rt) pc←pc + 4 + (sign-extend)immediate <<2 else pc ←pc + 4,immediate是從PC+4地址開始和轉移到的指令之間指令條數。

  immediate符號擴充套件之後左移2位再相加。左移2位是由於跳轉到的指令地址肯定是4的倍數(每條指令佔4個位元組),最低兩位是“00”,因此將immediate放進指令碼中的時候,已經右移兩位,這與上文 “指令之間指令條數”相契合。運算結果符合事實,單元工作正常。
在這裡插入圖片描述

sw:

在這裡插入圖片描述
  第19個週期中,指令地址為98220004,在程式測試段中,可以看到,本條指令為sw,那麼現在開始檢驗指令sw的實現過程:指令sw的功能是:memory[rs+ (sign-extend)immediate]←rt;immediate符號擴充套件再相加。

  將rt暫存器的內容儲存到rs暫存器內容和立即數符號擴充套件後的數相加作為地址的記憶體單元中。同樣的依次找到源運算元在暫存器中的值,進入ALU運算後,檢驗結果,過程圖展示如下,結果無誤,單元功能實現完好。
在這裡插入圖片描述

lw:

在這裡插入圖片描述
  第20個週期中,指令地址為9c290004,在程式測試段中,可以看到,本條指令為lw,那麼現在開始檢驗指令lw的實現過程:指令lw的功能是:rt ← memory[rs + (sign-extend)immediate];immediate符號擴充套件再相加。

  讀取rs暫存器內容和立即數符號擴充套件後的數相加作為地址的記憶體單元中的數,然後儲存到rt暫存器中。同樣的依次找到源運算元在暫存器中的值,進入ALU運算後,檢驗結果,過程圖展示如下,結果無誤,單元功能實現完好。
在這裡插入圖片描述

j:

在這裡插入圖片描述

  第21個週期中,指令地址為e0000010,在程式測試段中,可以看到,本條指令為j,那麼現在開始檢驗指令j的實現過程:指令j的功能是:pc <-{(pc+4)[31..28],addr[27..2],2{0}},無條件跳轉。

  由於MIPS32的指令程式碼長度佔4個位元組,所以指令地址二進位制數最低2位均為0,將指令地址放進指令程式碼中時,可省掉!這樣,除了最高6位操作碼外,還有26位可用於存放地址,事實上,可存放28位地址了,剩下最高4位由pc+4最高4位拼接上。指令驗證過程如下,最後結論為:單元工作正常,功能實現完好。
在這裡插入圖片描述

halt:

在這裡插入圖片描述
  第22個週期中,指令地址為fc000000,在程式測試段中,可以看到,本條指令為Halt,那麼現在開始檢驗指令Halt的實現過程:指令Halt的功能是:停機;不改變PC的值,PC保持不變。從模擬後的波形圖中,可以清晰看到,波形圖在本指令後,電平恆定,不再發生變化,所以本指令功能實現,模組單元工作正常。

總結

  深刻了解了一個簡單單週期CPU的設計方法,無論是複雜亦或是簡單的系統,最好的辦法就是採用分層和模組化的設計方法。在眾多訊號狀態中,首先從最高層開始梳理邏輯,劃分模組,進而到每個模組的內部核心處繼續劃分,這樣就避免了在訊號條件變動很多的情況下不能夠清晰的理清的問題。

  硬體設施的不足讓人感覺整個課程在“雲端”,講授的內容不能很好的和現有知識的應用契合,雖然第一次接觸VerilogHDL硬體描述語言,但是這門語言的思想和我所學的C++語言使用了同樣的邏輯架構,採用了自頂向下、分而治之的思想逐步剖析,正所謂“大廈的建立絕非一朝一夕”,採用“分步”的設計思想完成最終單週期CPU的設計與實現。

  有些許遺憾的是未能在硬體上實現,如果能在開發板上燒寫程式,接觸到實物,應該能有更為深刻的理解,在模組呼叫使用埠繫結時,有一個小技巧:勿完全按照順序賦值;因為這樣的方法可以儘量減少程式出錯的概率,而對於我們後期檢查原始檔時也無需完全分辨每一個引數的值,減少工作量。

  維基百科、谷歌學術是非常不錯的平臺。

相關文章