摘 要:設計了一款面向嵌入式控制領域的16位堆棧處理器,該處理器包含兩個堆棧:執行數學表達式的數據堆棧和支持子程序調用的返回堆棧,其指令集含35條堆棧指令.詳細給出了該堆棧處理器的體系結構及設計方法;不僅采用簡單有效的指令編碼方式縮小了代碼體積,同時給出了單周期操作多個堆棧元素的解決方法.該處理器采用FPGA實現,在XC5VLX110T芯片上的運行時鐘頻率最高達到146.7MHz。最后給出了設計的軟件仿真與硬件綜合結果。
1 引言
Forth是由Charles H.Moore在1960年代發明的一種基于堆棧、可擴展、具有簡單哲學思想的計算機編程語言[1],特別適合于軟件代碼在千行數量級的中規模嵌入式系統中應用,并且已經被國外廣泛應用于天文、軍事、航空航天、工業自動化、圖形、儀器儀表等領域。
Forth語言既可以被看作匯編語言又可以被看作高級語言,它與傳統語言最大的區別在于它是基于堆棧的和可擴展性。Forth語言本質上定義了一種雙堆棧體系結構。這種體系結構的主要思想是基于兩個不同的堆棧,一個是用來執行數學表達式的數據堆棧,另一個是用來支持子程序調用的,即保存子程序返回地址的返回堆棧,指令的所有操作都是針對這兩個堆棧的一個或者幾個棧頂元素[2]。
Forth語言定義的這種雙堆棧體系結構清晰明了,復雜度低,并且其主要面向的是嵌入式控制領域。在這種背景下,結合FPGA的靈活性,本文設計并實現了一種基于FPGA 的16位堆棧處理器,相比基于RISC體系結構的嵌入式處理器,具有以下幾點優勢:
① 很大程度上避免了處理器進行上下文切換帶來的開銷,因為處理器的運行不依賴于大量的通用寄存器;
② 處理器的尋址方式非常簡單,幾乎所有指令都是0操作數指令.這樣不僅系統復雜度顯著降低,速度得到提升,代碼體積也大大減??;
③ 處理器在運行具有深度嵌套特征的程序時有更加明顯的優勢,因為具有專門的硬件堆棧來執行子程序調用與返回。
2 設計與實現
2.1 堆棧處理器體系結構概覽
處理器的結構如圖1所示,為了能夠清晰地展示其結構,部分信號并未顯示或直接連接.處理器包含的主要模塊見表1。
需要說明的是,表中的數據堆棧由棧頂寄存器T、次棧頂寄存器N1、第三棧頂寄存器N2以及深度為32的堆棧存儲器構成;返回堆棧由棧頂寄存器R以及深度為32的堆棧存儲器構成。
2.2 指令集
2.2.1 指令集設計
處理器實現了四種類型共35條指令,具體如下表2所示。
表中大部分指令遵從了Forth語言的原語命名規則與功能.比如,其中的‘>r’表示將數據堆棧棧頂寄存器T的內容彈出到返回堆棧棧頂寄存器R,‘r>’則表示相反的功能.另外,‘@’表示從存儲器讀數據到數據堆棧,而‘!’表示將數據堆棧的內容存儲到存儲器中.下面分別簡述各種類型指令的功能。
① 堆棧操作:主要是對數據堆棧或者返回堆棧的一個或者多個棧頂元素的操作.例如,swap指令交換T與N1的內容;one和zero指令分別將T的所有bit位置1和0;drop指令彈出T中內容并拋棄.所有的堆棧操作類型指令都在一個周期內完成。
② 數學運算:數學運算指令的操作數均為T和N1中內容.邏輯運算指令包括or、xor和and;移位指令包括asr、lsr和lsl;1plus和1min分別將數據堆棧棧頂元素加1和減1.所有數學運算類型指令都在一個周期內完成。
為了節省硬件資源,堆棧處理器沒有實現硬件支持的乘法器[3]和除法器[4],因此沒有直接的乘法和除法指令,取而代之的是mpp和shld指令.這兩條指令分別完成了乘法器和除法器在一個時鐘周期中完成的操作.mpp指令執行時需要同時操作寄存器T、N1和N2.N1與N2中分別存放初始的乘數與被乘數,T的初始值為0;最終乘積結果的高低16位分別存放于T與N1.mpp指令的執行分成兩步.第一步判斷N1最低位是否為1:如果為1,將N2與T中內容相加;否則不做操作.第二步將T做高位、N1做低位,對其內容進行邏輯右移.這樣連續執行完16條mpp指令后,T與N1中存放的分別為乘積的高低16位.這16條mpp指令再配合其他幾條指令便可以完成正常的乘法操作,完整的乘法操作耗費19個時鐘周期.除法的實現與乘法類似,完整的除法操作耗費21個時鐘周期.下面給出了乘法操作的程序示例,其詳細解釋將在3.1節的仿真分析中給出:
litc 0 ∥將0壓入堆棧
mpp ∥第1條mpp指令
…
mpp ∥第16條mpp指令
rot ∥乘法結束后,清理掉
drop ∥位于N2的被乘數
③ 訪存:堆棧處理器支持按字節和按字兩種訪存方式.指令集中的litc、c@和c!指令為字節訪存指令;而lit、@和!為按字訪存指令.這里簡要說明按字訪存指令的功能,按字節訪存與之類似.lit將一個字常量壓入數據堆棧;@將T的內容彈出送入地址寄存器A,按A中地址訪存取得數據后壓入數據堆棧;!指令將T中的內容彈出送入地址寄存器A,再將當前棧頂T的數據彈出存入A 中地址.除litc指令的其他指令需要兩個周期執行,litc指令在設計時被優化為不需要訪存,因此只需要一個周期執行。
④ 程序轉移:call為子程序調用指令,它將當前PC寄存器值壓入返回堆棧,同時將子程序地址送入PC寄存器;ret為子程序返回指令,它將返回堆棧的棧頂值彈出,送入PC寄存器;jmp為無條件跳轉指令;jz和jc為有條件跳轉指令,跳轉條件分別是數據堆棧棧頂T的內容是否為0和ALU進位標志位是否為0;drjne將返回堆棧棧頂R的內容減1,然后判斷R中內容是否為0,如果不為0則跳轉.除call與ret以外的其他指令都需要先訪存取得16位跳轉地址,然后壓入堆棧,因此需要兩個周期執行;ret與call指令只需要一個周期執行。
2.2.2 指令集編碼優化
存儲器是嵌入式控制系統中的一種稀缺資源,因此在設計堆棧處理器時,采用了一種簡單但是有效的編碼方式進行指令編碼,不僅大大減小了代碼體積,還在一定程度上可以降低系統的復雜度,提高系統效率。
在堆棧處理器上執行的程序一般都通過大量子程序調用進行模塊化設計以減小代碼尺寸,這樣才能充分利用硬件支持的返回堆棧.因此,在典型的堆棧處理器程序中25%的時間花費在子程序調用上[5],這就要求在實現堆棧處理器時必須高效地實現call指令.一方面,如果call指令占用的bit位較少,便能減小代碼體積;另外一方面,如果call指令的執行能在一個周期完成,將大大加快程序的執行速度.因此指令集采用了兩種不同的編碼方式實現。
對于除call以外的其他指令,每條指令占8位且最高位均為0.處理器每次訪存取出的16位數據中,高低8位各為一條指令.采用這種設計方案后,一方面,每次訪存可以取出兩條指令.這在處理器和存儲器之間形成了一個處理器速度兩倍于存儲器速度的緩沖,可以在一定程度上避免Cache的引入,降低嵌入式系統的復雜度;另一方面,每條指令占用8位而不是16位可以大大減小代碼體積。
all指令采用了另外一種實現方式,如果訪存取出的16位數據中最高位為1,那么這16位不再被解釋為兩條指令,最高位被解釋為call指令,剩下的15位經邏輯左移1位后作為子程序地址.相對于采用與其他類型指令相同的8位實現方式,這種實現方式使得call指令僅占1位,可以進一步減小代碼體積。
另外,call指令執行時的子程序地址不需要通過訪存取得,因此其執行將只耗費一個周期.當然,這樣的方案會讓call指令只能調用位于偶地址的子程序,但是好的設計來源于適當的折中,這種損失相對于獲得的效率和性能提升是值得的。
2.3 控制模塊
控制器本質上是如圖2所示的一個五狀態的有限狀態機。InstrF為其初始狀態,即取指令狀態。Slot0和Slot1為執行狀態,Slot0_M 和Slot1_M 為訪存狀態。Slot0與Slot0_M 對應于低8位指令地執行與訪存,Slot1與Slot1_M 對應于高8位指令地執行與訪存。
處理器在InstrF狀態訪存取出16位數據后進入Slot0狀態,先判斷數據的最高位是否為1:若為1,則最高位被解釋為call指令,低15位經邏輯左移1位后作為子程序地址,執行子程序調用后下一狀態進入InstrF;若為0,則執行低8位指令.如果低8位指令為單周期指令,下一狀態進入Slot1執行高8位指令;如果低8位指令為雙周期指令,下一狀態進入Slot0_M 進行訪存,訪存結束后下一狀態進入Slot1執行高8位指令。
處理器在進入Slot1狀態后,執行高8指令.如果高8位指令為單周期指令,下一狀態進入InstrF繼續取指令;如果高8位為雙周期指令,下一狀態進入Slot1_M 訪存,訪存結束后下一狀態進入InstrF繼續取指令。
在InstrF狀態可以響應外部中斷.如果此時檢測到中斷請求信號intreq為高電平,則給出中斷響應信號intack,并且將中斷向量intvec送入PC,下一狀態依然為InstrF狀態。
2.4 堆棧實現
本文設計的堆棧處理器包含兩個硬件支持的堆棧:數據堆棧和返回堆棧.由于所有指令都是對這兩個堆棧的操作,因此堆棧的實現方式對于堆棧處理器的效率和性能是至關重要的.堆棧的實現不僅要最大程度上避免溢出,還要保證在訪問時能夠非常高效。
2.4.1 堆棧溢出問題
關于硬件堆棧溢出問題存在多種解決方法,主要有:請求式單元素堆棧管理器、頁式堆棧管理以及
聯合Cache等.其實Charles H.Moore曾經做過統計,在典型的堆棧程序執行過程中,當堆棧深度超過32時,幾乎所有的程序都不會發生堆棧溢出問題。因此堆棧處理器WISC和EP32直接將堆棧深度設置為256,以求完全避免堆棧溢出問題。
相對于采用其他硬件方式來避免溢出問題,直接增加堆棧深度更加簡單有效,因此本文論述的處理器直接將堆棧存儲器深度設置為32(不包括棧頂寄存器在內).這對于一般的應用已經足夠,由于是采用FPGA實現,在需要的情況下可以很方便地將堆棧深度增加。
2.4.2 指令的單周期堆棧操作
指令集中的很多指令,如swap和rot指令,需要同時操作堆棧的兩個甚至三個棧頂元素.如果采用普通的堆棧存儲器,想同時訪問棧頂的多個元素,就需要訪問堆棧多次,這樣那些需要操作多個棧頂元素的指令在執行時需要耗費多個周期.這對于堆棧處理器將是極大的性能損失。
因此,為了保證所有指令的堆棧操作都能夠在單周期內完成,在堆棧設計上采用了圖1中所示的棧頂寄存器加堆棧存儲器的方式.對于數據堆棧,由于在最多的情況下(rot指令的執行)需要同時訪問棧頂的三個元素,因此設置了三個棧頂寄存器T、N1和N2.這樣數據堆棧的真正棧頂元素就是T,而不是堆棧存儲器的棧頂元素.返回堆棧的設計與此類似,設置了一個棧頂寄存器R。
3 仿真與綜合
本文設計采用Verilog進行RTL級描述,仿真軟件為ModelSimSE6.5C,綜合軟件為SynplifyPro9.6.1,目標芯片為Xilinx公司的Virtex-5 XC5VLX110T。
3.1 仿真分析
圖3中給出了2.2.1節中的乘法程序的仿真波形圖,圖中state與nextstate信號給出了有限狀態機的狀態轉移情況;instr_exec表示當前正在執行的指令;I與PC則分別為指令寄存器與程序計數器;T、N1和N2為數據堆棧的三個棧頂寄存器;result表示ALU的運算結果。
從圖中可以看出,在70~110ns,執行了兩條lit指令(指令編碼為40H),這兩條指令將乘數與被乘數003H與0011H壓入數據堆棧.在這之后開始執行2.2.1節中給出的乘法運算程序.在120~130ns,執行了一條litc(指令編碼為50H)指令,將00H擴展為0000H 壓入數據堆棧.此時,可以查看堆棧寄存器T、N1和N2里的內容分別為0000H、0011H和0003H.在140~370ns期間連續執行了16條mpp完成在mpp指令操作后的一些堆棧清理操作.最后,在第400ns時查看數據堆棧的棧頂寄存器T與N1,發現內容分別為0000H 與0033H,其中0000H 為乘法結果的高16位,而0033H 則為低16位,與預期結果一致。
3.2 綜合分析
使用Xilinx公司的XC5VLX110T作為目標芯片時,片上資源利用率不超過3%,堆棧處理器的最高頻率可以達到146.7MHz.表3中給出了本設計在不同芯片上與不同文獻中同類設計進行的主頻性能對比.從表中數據可以看出,本設計在主頻性能上與前三種同類設計相比有較明顯的提升.值得注意的是,與文獻[8]中的MSL16處理器相比較而言,本文處理器主頻略有下降,這是因為MSL16處理器在設計中應用了兩級流水線技術.本文則側重于運用較簡單的設計、較少的硬件資源以獲得較高的性能:一方面,流水線技術在堆棧處理器中的應用無疑會大幅增加堆棧處理器的復雜度,這與本文的設計初衷不相符;另一方面,從性能提升率上看,流水線技術的應用所帶來的頻率增加并非異常顯著。
4 結束語
本文以雙堆棧體系結構為基礎,設計了一種不同于RISC體系結構的嵌入式堆棧處理器.該處理器結構緊湊,系統復雜度低,代碼體積?。赬ilinx公司的XUPV5-LX110T開發板上實現后,其主頻達到了146.7MHz,片內資源使用率不超過3%.仿真與綜合結果證明了設計的正確性,與同類設計相比較,主頻性能有較為明顯的提升.堆棧處理器較高的主頻與較低的資源使用率為后期以該處理器為核心構建一個SoPC提供了基礎。
評論