指令的編碼與譯碼原理

       指令集是處理器體系架構的重要組成部分。指令集有兩個發展方面,包括以X86爲代表的CISC(複雜指令集)和以ARM、MIPS爲代表的RISC(精簡指令集)。CISC的目標是儘可能將常用的功能用最少甚至一條指令來實現,因此該指令對應的執行電路往往是複雜的,其側重的是硬件功能的實現;RISC則相反,其是將複雜的執行電路進行分解,即用盡可能簡單的多指令去描述該功能,以軟件來降低硬件的複雜度,因此RISC對編譯器的要求比較高。CISC的指令長度是可變的,執行週期也不固定,而RISC則是定長的、往往都是單週期執行。寄存器多也是RISC的特點。

        本文的重點不在於詳細比較RISC和CISC,而是介紹CPU指令的編碼和譯碼。我們都知道CPU的流水線執行過程有取指、譯碼、執行、訪存和回寫(參看博文:CPU指令的流水線執行),但很多人對於這些步驟的理解都僅僅在概念層面上的,有必要對其進行詳細闡述,理解CPU指令的設計和實現。

        我們都知道C語言是對現實問題的解決方法和過程的高度抽象,其語法主要包括數值、邏輯運算和分支控制轉移。數值運算就是加減乘除(比較也是減法),邏輯運算就是與、或、非、異或等,分支控制轉移包括if/else、for、while等語法。彙編語言是在機器層面上對C語言的理解和建構,其指令同樣也包括數值、邏輯運算、分支控制轉移,但是一行C語言可能需要多條彙編指令才能實現。同時由於指令在內存上,而CPU訪問寄存器要比訪問內存要快得多,所以CPU的運算一般都在寄存器中進行,因此彙編語言一般都需要增加內存和寄存器直接的數據加載/存儲指令。CPU只認識二進制輸入,所以可以把彙編語言當次硬件層面上的僞代碼,其便於開發人員熟記,同樣是低級語言。

指令的編碼就是實現彙編語言到二進制機器碼的過程,其是彙編器實現的(編譯器是將C語言轉爲彙編語言)。         現在假設某種簡單的CPU只支持4種功能:包括

1)加法 ADD Rd,Rs,Rn , 結果是Rd=Rs+Rn

2)減法 SUB Rd,Rs,Rn , 結果是Rd=Rs-Rn

3)數據傳送 MOV Rd,Rs,結果是Rd=Rs

        4)數據加載 LDR Rd,[Rs] ,結構是將內存中以Rs寄存器的值爲地址取值賦給Rd

         如何進行編碼呢?

1)首先將指令分爲操作碼+操作數 兩個部分,操作碼即代表指令功能,如ADD、SUB等,其在CPU中就代表某種具體的電路,如ADD就代表加法電路,SUB代表減法電路;操作數即是代表功能的輸入和輸出,對應電路的輸入和輸出。

2)現在共有4種功能,那至少需要2個比特來進行編碼,如00代表ADD,01代表SUB,10代表MOV,11代表LDR;

        3)操作數的編碼,假設寄存器共有8個,Rd,Rs,Rn都是其中的一個,即d,s,n的範圍是0到7,那至少需要3個比特來編碼,如000代表R0,001代表R1,以此類推,111代表R7.

        那要實現以上四種功能指令,總共需要2+3+3+3=11個比特進行編碼。如SUB R6,R1,R2,即R1減去R2的值賦給R7,那其編碼就是01 110 001 010.

        理解完指令的編碼,那指令的譯碼應該是比較好理解的。取指就是根據當前程序計數寄存器PC(假設硬件電路規定R7就是寄存器PC)的值從內存中取出機器碼指令,該機器碼的值是01 110 001 010。接下來的過程就是譯碼,即根據最先的兩個比特01送入譯碼器,選擇爲減法電路;110即譯碼選擇爲R6,001譯碼選擇R1,010譯碼選擇R2. 再接着的就是指令的執行了,執行就是減法單元電路對兩個輸入(R1,R2)進行運算,將結果賦給R6.

取指和譯碼都是CPU的控制單元(CU)完成的,執行是ALU(邏輯運算單元)完成,可以將ALU看出是很多種電路的集合。CPU指令的設計和指令編碼息息相關。

        ARM和MIPS CPU都是32位字長,因此指令編碼是32比特,能夠支持和表達更多的功能(操作碼)和寄存器(操作數)。如ARM體系是r0到r15,因此需要4個比特來表示,當然ARM的寄存器還有組的概念,即CPU在不同的工作模式時看到的寄存器可能是不同的,如r13,r14等。這些譯碼時不僅需要指令操作數作爲輸入,還需要當前狀態寄存器的值作爲輸入。

        16位指令集是因爲什麼產生的呢?是因爲同樣的一段C語言代碼,用16位指令進行編碼比32位編碼能夠節省30%的代碼量,代碼量越少,那佔用的內存就越少,自然成本越低。在MCU領域,一般都是成本敏感的,所以16爲指令在MCU領域有非常廣泛的使用。

         ARM和MIPS的指令設計是以32位爲基礎進行設計,其執行也是32作爲輸入的,那如何在32位指令集中實現16位呢?我們都知道二八原理,即20%的指令的使用率會達到80%,所以我們對這20%的指令(其是32位指令的一個子集)進行編碼,自然可以用較少的比特數來進行編碼,而在16位指令使用時,我們可以強制要求其只使用一部分寄存器,那自然可以以更少的比特數來進行寄存器的編碼。可以將16位指令集看成是32位指令集的一個子集,CPU在譯碼階段先將16爲指令轉化爲對應的32位指令,再進行譯碼、執行。

          ARM的16位指令集爲thumb指令集,MIPS的16爲指令集爲MIPS16指令集。至於32位指令集和16位指令集之間的無縫切換,請參看另一篇博文:32位和16位指令集模式自動切換機制

        







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