作者 | 林Nova
出品 | 汽車電子與軟體
本文主要介紹Infineon AURIX™️2G TC3XX系列晶片内核結構及相關暫存器,並結合代碼示例講解晶片内核指令集系統。
#01 前言
1.1 内核
晶片内核(Core)是中央處理器(CPU)中的獨立處理單元,能夠執行指令、處理資料和控制操作。
英飛凌AURIX™️2G TC3XX系列晶片的内核架構是一種混合架構,同時結合了精簡指令集電腦(RISC)和複雜指令集電腦(CISC)的特徵,稱為TriCore™️内核架構(以下簡稱TriCore™️)。它是一款專門為即時性進行了優化的32位的多核同構嵌入式系統架構。之所以命名為TriCore™️,是因為其集成了RISC架構、DSP架構和即時系統的技術於一體,形成了兼具三方優勢的內核架構。
TriCore™ 架構採用了RISC的高性能load/store資料處理模式,同時具有DSP的資料處理能力。TriCore™ 是32位處理器架構,因此採用32位位址空間,支援可選的虛擬位址空間。
下面是TriCore™ 架構的特性:
- 32位架構
- 4GB的位址空間
- 同時支援16位元和32位元指令,減少代碼大小
- 大多數指令在一個週期內執行
- 分支指令(使用分支預測)
- 使用並行資料記憶體,實現低中斷延遲與快速自動上下文切換
- 專用介面特定于應用程式的輔助處理器,以允許添加定制指令
- 零開銷回路功能
- 雙/單時鐘週期,16x16位元乘法累加單元(可選飽和)
- 可選浮點單元(FPU)和記憶體管理單元(MMU)
- 廣泛的位處理能力
- 單指令多資料(SIMD)打包資料操作(2x16位或4x 8位運算元)
- 靈活的中斷優先順序方案
- 位元組和位元定址
- 資料記憶體和CPU暫存器的小端位元組排序
- 記憶體保護
- 調試支持
1.2 指令集系統
每款內核都有其匹配的內核指令集,內核架構也成為指令集架構,比如我們日常使用的電腦(Intel或AMD晶片),使用的就是X86指令集,內核就屬於X86架構。常見的還有ARM架構、DSP架構和RISC-V架構。當我們想要掌握某款晶片,基於該晶片進行系統搭建時,就必須要瞭解該晶片的內核。
指令集系統是一種用於電腦或其他數位文書處理器的體系結構,它規定了處理器能夠執行的指令集合,包括操作碼、運算元和指令格式等。不同的處理器架構(如x86、ARM、MIPS等)都有自己的指令集系統,這些系統決定了處理器如何執行各種操作,從簡單的加法和乘法到複雜的條件分支和記憶體訪問。指令集系統的設計直接影響了處理器的性能、功耗和軟體相容性。
嵌入式領域的晶片一般都是精簡指令集內核,比如RISC-V、ARM等, 精簡指令集(RISC,Reduced Instruction Set Computing)是一種電腦處理器架構設計理念,其核心思想是將處理器的指令集設計得相對較小和簡單,以提高執行效率。相對於複雜指令集電腦(CISC)架構而言,RISC架構強調用更少、更基本的指令來完成操作,並且這些指令的執行時間相對較短。
和ARM指令集一樣,TriCore™ 指令集屬於通用暫存器型結構中的暫存器-暫存器結構,即除了load和store以外,其餘指令的運算元都來自通用暫存器組。
本文介紹的TriCore™ 內核是AURIX™️ TC3XX系列中使用的TriCore™TC1.6.2版本的內核架構。由於篇幅原因,無法講解所有指令,但是通過本文的介紹,讀者能夠較好地掌握TriCore™ 指令系統的結構和使用,然後就可以自行利用內核手冊進行查閱和學習。
#02 内核介紹
接下來針對內核結構,首先介紹程式模型、通用暫存器和系統暫存器等。
2.1 程式模型(Programming Model)
該小節主要介紹內核的資料類型、資料格式、存儲模式以及位址空間等內容,因為電腦的本質其實是對資料的處理和呈現,因此要理解其內核,首先要理解其資料的處理模型。
2.1.1 資料及内存
首先我們來看TriCore™ 的資料模型,注意,這裡提到的資料模型,是指內核在處理資料時的視角,並非我們C語言中定義資料的視角。
TriCore™ 架構支持下面幾種資料類型:
- Boolean:布林型,FALSE表示0,TRUE表示1
- Bit String:布林型布林串,一段Bit段,用於移位操作
- Byte:位元組,一個位元組是8Bit的資料
- Signed Fraction:有符號的分數,TriCore™ 支援16位元、32位元、64位元的分數
- Address:位址表示一段32位元位址資料(TriCore™ 有專門的位址暫存器,下文會介紹)
- Signed and Unsigned Integers:又符合和無符號整型,表示32位元有、無符號資料
- IEEE-754 Single-Precision Floating-Point Number:浮點數
TriCore™ 是32位架構,因此大多數指令都是基於32位元資料進行處理的,與資料類型相對應,其指令支援的資料格式如下圖:
程式對資料的讀取、存儲操作是有對齊要求的,見下表:
同樣外設的位址訪問也是有對齊要求的:
TriCore™ 的資料存儲採用小端模式,低位元組存儲在低位址。
TriCore™ 是32位內核,因此位址最多支援0xFFFF FFFF,也就是4GB空間大小。因此位址空間的定義被分為16個段(Segments)[0 - F],位址最高位的4個Bit表示段號,每個段256MBytes大小。其中F段被用於外設暫存器位址段。
2.1.2 定址模式
我們知道,電腦在進行資料計算過程中,需要不斷進行資料的讀取和存儲,這中間有一個重要的流程就是定址。不同的內核支持不同的定址模式,TriCore™ 支持的定址模式如下表:
- Absolute:絕對位址定址模式,用於外設和全域變數的定址
- Base+Offset:基底位址加偏移定址方式,一般用於結構體成員定址和區域變數的棧定址
- Pre-Increment and Pre-Decrement Addressing:預加/預減定址,一般用於棧的操作
- Post-Increment and Post-Decrement Addressing:後加/後減定址,一般用於資料資料的處理指示
- Circular:迴圈定址,一般用於迴圈操作中
- Bit-reverse:位元翻轉定址,用於FFT演算法中
2.2 通用暫存器和系統暫存器(General Purpose and System Registers)
TriCore™ 的內核暫存器包括通用暫存器和系統暫存器,系統暫存器包括程式狀態暫存器等。其中系統暫存器的位址是16位內核位址,讀寫採用的是專用的指令,MTCR(Move To Core Register)和MFCR(Move From Core Register)。
2.2.1 通用暫存器
通用暫存器是電腦體系結構中的暫存器,其設計用途是存儲通用資料,而不是特定的資料類型或用途。通用暫存器可以存儲整數、浮點數、記憶體位址等各種資料類型,而不受限於特定的操作或指令。
在許多電腦架構中,通用暫存器用於執行各種操作,如算數運算、邏輯運算、資料移動等。程式師可以使用這些暫存器來存儲臨時變數、中間計算結果和其他通用資料。通用暫存器的優點在於其靈活性和通用性,允許程式師更自由地使用暫存器來執行各種任務,而不受到特定資料類型或操作的限制。這有助於提高程式的靈活性和性能。
相比於ARM內核,TriCore™ 有兩類通用暫存器,包括16個32位元資料暫存器(D[0]~D[15])和16個32位位址暫存器(A[0]~A[15])。
其按照功能有如下幾類:
- 通用暫存器:D[0]~D[14]、A[2]~A[7]、A[12]~A[14]為真正意義上的通用暫存器
- A[15]和D[15]為隱式暫存器(一般16位元指令使用,指令語法中不用明確指出,默認使用該暫存器),32位元指令下與通用暫存器功能相同,16位元指令下作為預設的暫存器使用
- A[0]、A[1]、A[8]、A[9]為全域暫存器,在函式呼叫、中斷、異常等場景都不會進行操作
- A[10]為棧指針暫存器
- A[11]為返回位址暫存器
資料和位址暫存器的分離促進了算術和記憶體操作並存執行的高效實現。同時,相鄰的兩個奇偶暫存器還可以成對進行訪問,比如通過E[0]訪問由D[0]和D[1]組成的64位元資料(D[1]存放高8位),通過P[0]訪問由A[0]和A[1]組成的64位位址。
眾所周知,在作業系統調度或中斷程式切換過程中,系統需要保存上下文,其中主要的內容是保存暫存器。TriCore™ 的通用暫存器分為高上下文和低上下文,高上下文包括A[10]~A[15]、D[8]~D[15],低上下文包括A[2]~A[7]、D[0]~D[7]以及A[11],其中僅有返回位址暫存器A[11]在兩部分中都存在。A[0]、A[1]、A[8]、A[9]作為全域位址暫存器,不存儲在上下文中。
2.2.2 程式狀態資訊暫存器(Program State Information Registers)
程式狀態資訊暫存器包含三個暫存器:程式計數器,也就是我們常說的PC指標;程式狀態字PSW;前一個上下文資訊PCXI。
PC暫存器為32位元程式計數器,反映了程式執行的位置,始終指向下一條即將執行的指令。
程式狀態字PSW顧名思義,是用來反映程式執行過程中的狀態資訊。其中:
USB:使用者狀態位元(User Status Bits),用來保存指令執行過程中的狀態標誌位元,如溢出資訊等,用於後續指令作條件判斷
- RES:預留
- PRS[2](Bit15)& PRS[1:0](Bit12,Bit13):保護暫存器集切換位(Protection Register Set bit),記憶體保護暫存器有多套配置,PRS用來選擇程式保護暫存器或資料保護暫存器的配置切換
- S:安全任務識別字(Safety Task Identifier),標注當前任務是否為安全任務
IO:I/O存取權限級別控制位(Access Privilege Level Control),TriCore™ 內核對於外設暫存器的訪問是由許可權級別的:
- 0:User-0 Mode,無權訪問外設,在此許可權下訪問會產生PSE和MPP異常,也無法進行開關中斷
- 1:User-1 Mode,常規存取權限,在此許可權下能夠訪問大部分外設,包括Port口的讀寫、計時器的讀取和大部分I/O狀態讀取,能夠開關中斷
- 2:Supervisor Mode,具有完全的外設存取控制許可權
3:Reserved Value
- IS:中斷棧控制位(Interrupt Stack Control),TriCore™️內核是有獨立的中斷棧暫存器ISP的,當進入中斷且IS位為0的時候,硬體會先將棧暫存器置為ISP的值,以使用中斷的特有棧(當然,在此之前硬體會自動保存SP指標,中斷返回後會自動載入回來,不用擔心被覆蓋),並將IS位置1 。因為是共用棧,如果發生中斷嵌套,則不作任何操作,繼續往下使用中斷棧
- GW:全域位址暫存器寫許可權位,決定是否可寫入全域位址暫存器,1表示有許可權;全域位址暫存器一般用來指向某個關鍵的位址區域或關鍵的資料結構體,比如OS的任務調度控制塊等;另外通常A[0]被用於短資料的讀取和寫入,A[1]也一般為編譯器操作預留,A[8]和A[9]未作為編譯器預留,一般用於指向系統的關鍵資料
- CDE:調用深度計數使能位(Call Depth Count Enable),TriCore™️內核是有調用深度檢查的,每次調用計數器加1,最大6位元數目器,也就是最多支持64層調用(每個Task或者中斷具有獨立的上下文,因此獨立計數),當超過調用上限,會產生異常;將CDE置0,可暫時關閉調用計數,但是在下次Call指令執行時會自動再次打開
CDC:調用深度計數器(Call Depth Counter),調用深度計數器,7個Bit位寬,溢出時產生異常,也可修改CDC降低調用深度上限,比如改為1111100,則4次調度就會產生異常
前一個上下文資訊PCXI用來進行程式的上下文載入,當程式需要返回時,或中斷結束調用時,都需要從這個地方向前連結,因此PCXI也稱為連結字(Link Word),它和CSA的配合使得TriCore™️能夠實現一套高效的硬體上下文保存機制,下面我們會介紹,這裡先看PCXI的內容:
- RES:預留
- PCPN:前一個CPU中斷優先順序(Previous CPU Priority Number),在之前關於中斷的文章中我們提到過,中斷的處理流程的一個步驟,是將ICR.CCPN,也就是當前的CPU中斷優先順序位,寫入到PCPN,也就是本暫存器位中,用於中斷環境的保存,中斷執行完之後再進行恢復
- PIE:先前中斷使能位(Previous Interrupt Enable),同PCPN,在中斷處理流程中將CPU中斷使能狀態位元ICR.IE,保存到PIE中,用於中斷環境保存,中斷執行完之後再進行恢復
- UL:高、低上下文標籤(Upper or Lower Context Tag),1表示高上下文,0表示低上下文
- PCXS,PCXO:CSA是存儲在記憶體中的,PCXS和PCXO用於指示存儲位址,物理位址有32位元,但是實際的記憶體空間沒有這麼大的,且CSA是64位元組對齊,位址末尾6個為0,所以只需要20位元就能指示上下文存儲位置,具體演算法是Addr=(PCXS<<12)|(PCXO<<6)
TriCore™️的上下文機制是利用CSA建立鏈表,PCXI指示鏈表的首位,以此形成函式呼叫棧關係。
2.2.3 棧管理暫存器
棧管理包括用戶棧和中斷棧,用到前文提到的棧指針暫存器A[10]、中斷指標暫存器ISP(Interrupt Stack Pointer),以及前文提到的PSW中的IS位,來進行棧的管理。當運行非中斷程式時,CPU正常根據棧指標暫存器使用普通程式棧,當從Task進入中斷時,硬體會自動切換到中斷棧。
棧指標暫存器通常是程式啟動時進行初始化,根據連結腳本分配的棧空間,設置棧起始位址。
中斷棧指標暫存器的使用,能夠防止中斷程式侵佔Task程式的棧。在程式進入中斷且PSW.IS==0時,系統會從ISP中載入中斷棧位址到棧指針中,以使用中斷的棧;如果是中斷的嵌套,PSW.IS==1,則不進行任何操作,繼續使用中斷棧。當中斷執行完畢後,PSW和棧指標都會從上下文中恢復,因此Task程式得以繼續使用原先的棧。
2.2.4 系統控制暫存器
系統控制暫存器SYSCON的主要功能是對所屬核進行控制(該寄存器有外設32位位址,可進行跨核訪問,比如主核通過控制從核的BHALT位設置啟動),主要包括:
- RES:預留
- BHALT:啟動凍結狀態控制位元(Boot halt status and release),當晶片啟動後,除了主核以外,該位元都會置1,代表該核處於凍結狀態,主核通過對該位寫0來啟動該核,啟動後寫1無效
- U1_IO:User-1 Peripheral access as supervisor,前面我們提到使用者模式包括User-0、User-1和Supervisor三種模式,該位元能夠允許User-1獲得對外設的全部訪問,不改變模式的情況下提高許可權
- U1_IED:User-1 Instruction execution disable,禁止或使能User-1模式下開關中斷
- ESDIS:Emulator Space Disable,模擬地址禁用
- TS:進入異常(Trap)後PSW.S的預設值
- IS:進入中斷後PSW.S的預設值
- TPROTEN:Temporal Protection Enable,1表示使能,0表示關閉
- PROTEN:記憶體保護使能,注意需要先設置好記憶體保護配置再使能
- FCDSF:空閒上下文清單耗盡粘滯標誌位元(Free Context List Depleted Sticky Flag),該位表徵是否發生過上下文列表耗盡,因為CSA的空間是RAM中設置的,如果任務或中斷過多,且都調用過深會導致CSA耗盡,從而該位置位,知道清除
2.2.5 Core_ID暫存器
Core_ID暫存器用來獲取當前核的ID,在多核系統中,共用代碼尤其是OS,獲取所在核是非常重要的。
中斷、記憶體保護、Trap相關的暫存器我們將會在模組單獨介紹時候進行說明,這裡就不展開介紹了。
#03 TriCore™️指令集結構
TriCore™是一種通用的、32位的微控制器-DSP,針對即時嵌入式系統進行了優化的多核同構架構。
TriCore™️指令集支援32位元指令和16位元指令,支援嵌入式常用的微控制器系列處理指示,也支援一些DSP資料處理指令。
3.1 指令語法
和ARM等指令集系統一樣,TriCore™️指令集的語法也是由指令和操作陣列成,運算元可以是立即數、暫存器或記憶體。一條指令包括基礎操作符、操作修飾符、運算元修飾符和操作陣列成。如下面這條指令,是將一個立即數寫入位址暫存器的高位:
- 基礎操作符:定義了該指令的基礎操作,比如mov就是賦值,add就是累加;
- 操作修飾符:定義該操作更準確的細節,比如movh中的h定義操作a15的高16位;
- 運算元修飾符:定義了運算元的資料類型,比如movh.a中的a定義了目標暫存器為位址暫存器;
- 運算元:需要操作的暫存器、立即數或記憶體。
3.1.1 基礎操作符
基礎操作符即該指令的操作定義,操作類型較多,下文會給出部分示例,此處就不一一列出了。
3.1.2 操作修飾符
TriCore™️指令集的操作修飾符見下表:
3.1.3 運算元修飾符
TriCore™️的運算元修飾符見下表:
有些16位元指令還會使用隱式暫存器,即該條指令不指定暫存器,而是固定使用約定好的暫存器,隱式暫存器包含下面幾種:
- D[15]:通用資料暫存器D[15]可以作為16位元指令的隱式資料暫存器使用;
- A[15]:通用位址暫存器A[15]可以作為16位元指令的隱式位址暫存器使用;
- A[10]:A[10]為棧指標暫存器,所以對於棧相關操作指令預設運算元為A[10];
- A[11]:A[11]為返回位址暫存器,即連結暫存器,所以跳轉指令預設使用A[11]作為指令預設的運算元。
3.2 程式狀態字標誌位元(使用者狀態位元)
之前的內核暫存器介紹中提到過,程式狀態字暫存器PSW中有5個使用者狀態位元,用於指令的狀態存儲。
這5個狀態位元會會影響指令的操作邏輯,同時它們也會因為指令的操作結果而發生變化。比如第一個Carry位,它表示計算進位,比如在使用加法指令ADDC(帶進位元的加)中結果溢出了,那C位就會置位;並且如果在執行前C位已經置位了,那ADDC的結果就會加1。
在手冊中每個指令後都會描述PSW的影響情況,可在指令詳細資訊中查看。
3.3 指令許可權
TriCore™️內核分為三個許可權等級,因此有些指令對許可權是有限制的。下表列出的指令表示只有該許可權等級下能夠使用。
#04 TriCore™ 指令介紹
本章節將分類介紹TriCore™ 指令集中的指令,同時包括部分操作修飾符和運算元修飾符的介紹。
4.1 如何查詢指令手冊
TriCore™ 指令數百條本文不一一解釋,這裡先介紹下如何查詢TriCore™ 指令手冊。
作為示例,我們來看ADD.A指令的文檔介紹,這裡各部分用序號標出,以做解釋。
- 指令助記符;
- 指令長名;
- 32bit格式指令的描述;
- 16bit格式指令的描述;
- 32bit格式指令使用語法,有時候運算元可以有多種類型,所以會有多種指令語法描述;
- 操作碼,也就是指令解碼最後的二進位數字格式;
- 指令的運行邏輯描述,這裡使用了仿C語言的RTL(Register Transfer Level)語言描述,在1.3章節中有語法介紹;
- 16bit格式指令語法;
- 16bit格式指令操作碼;
- 16bit格式指令邏輯描述;
- 對PSW中狀態位元的影響,如溢出等;
- 32bit格式指令示例;
- 16bit格式指令示例;
- 其他相關指令。
當我們遇到要查詢某條指令的時候,先根據其結構識別出它的指令助記符(並非所有指令-修飾符組合在內核手冊中都有示例),然後查到對應的指令,再根據其邏輯及修飾符的邏輯解釋其指令含義。
4.2 Load/Store指令
在Load-Store結構的內核中,Load(LD.X)和Store(ST.X)指令是唯一兩條能夠訪問記憶體的指令,它們是記憶體和暫存器間的橋樑,其餘的所有指令都是基於暫存器進行操作的。
Load/Store指令支援以下這些定址模式:
其中最主要的還是絕對位址定址和基址-偏移定址。
TriCore™ 指令集的Load/Store除了支援位元組(byte)、半字(Half Word)、字(Word)、雙字(Double Word)等基礎資料類型,還支持浮點型和位址型(位址暫存器的讀寫)。TriCore™ 中一個字Word,代表32位元組。
下面對Load/Store指令結合操作修飾符和運算元修飾符進行指令說明,這裡需要注意的是,TriCore™ 的通用暫存器是分為通用位址暫存器和通用資料暫存器兩類的,因此運算元修飾符會強調運算元類型。
Assembly language
LD.A A0, [A15] //將A15指向的記憶體中的4位元組資料,取到A0中,A表示運算元為位址暫存器
LD.BU D5, [A0] //將A0指向的記憶體中的一位元組無符號資料,取到D5中,B表示Byte,1位元組,U表示無符號
LD.D E0, [A0] //將A0指向的記憶體中的8位元組資料,取到E0中(D0+D1),D表示Double Word
LD.H D1, [A0] //將A0指向的記憶體中的2位元組資料,取到D1的低16位中,H表示Half Word
LD.W D2, [A2]0x44 //將A2+0x44指向的記憶體中的4位元組資料,取到D2中,W表示Word,0x44表示位址偏移
ST.A [A1], A0 //將A0的資料,存儲到A1指向的4位元組記憶體中,A表示運算元為位址暫存器
ST.B [A1], D0 //將D0的1位元組資料,存儲到A1指向的1位元組記憶體中,B表示長度為1位元組
ST.D [A1], E0 //將E0(D0+D1)中的8位元組資料,存儲到A1指向的8位元組記憶體中,D表示Double Word
ST.H [A0], D0 //將D0中的2位元組資料,存儲到A0指向的2位元組記憶體中,H表示Half Word
ST.W [A1], D0 //將D0中的4位元組資料,存儲到A1指向的4位元組記憶體中,W表示Word
4.3 算數運算指令
算數運算指令包括MOVE、加減乘除、絕對值、邏輯運算、移位元等,還包括計算非零bit位等。
TriCore™ 算術運算子有一種Saturation運算,操作修飾符為S,即飽和運算,意思是說當運算結果超出記憶體大小,結果是當前記憶體的最大值。比如uint8的最大值為255,用ADDS去計算200+200,得到的結果是255。同樣的如果減法運算值小於負的最大值,結果也是負的最大值。
4.3.1 MOV指令
MOV指令是將一個立即數或者暫存器的值,賦值給另一個暫存器的指令。
Assembly language
MOV D0, D1 //將D1中的值賦值給D0
MOV.A A0, D0 //將D0中的值賦值給位址暫存器A0,A表示目的運算元是位址暫存器
MOV.D D3, A4 //將位址暫存器A4的值賦值給資料暫存器D3,D表示目的運算元是資料暫存器
MOV.AA A3, A4 //將位址暫存器A4的值賦值給位址暫存器A3,AA表示源運算元和目的運算元都是位址暫存器
MOVH D3, 180 //將立即數180賦值給D3的高16位,並將D3的低16位清零,H表示高位
4.3.2 ADD/SUB指令
加法和減法指令類似,此處只列出加法的說明。
加法指令是ADD,普通加法運算中,溢出會自動去除高位,比如uint8的255+100,結果等於99。
而帶修飾符的加法運算,是會修改PSW的使用者狀態位元的,比如ADDC指令如果溢出會修改Carry位。
Assembly language
ADD D0, D1, D2 //計算D1+D2的值,結果存到D0中,溢出位捨棄,如255+1=0(uint8)
ADD.A A0, A1, A2 //計算位址暫存器A1+A2的值,結果存到A0中,A表示運算元為位址暫存器
ADDX D3, D1, D2 //計算D1+D2的值,結果存到D3中,如果溢出,PSW.C=1,否則PSW.C=0
ADDC D3, D1, D2 //計算D1+D2+PSW.C的值,結果存到D3中,若溢出,PSW.C=1,否則PSW.C=0
ADDI D3, D1, 858 //計算D1+858的值,結果存到D3中
ADDIH D3, D1, 0x8 //立即數移位加法,計算D1+0x8<<16的值,結果存到D3中,IH表示立即數左移16位
ADDS D3, D1, D2 //計算D1+D2的值,如果溢出則做飽和運算處理,結果存放到D3中
ADDS.U D3, D1, D2 //計算無符號D1+D2的值,如果溢出則做飽和運算處理,結果存放到D3中
4.3.3 MUL乘法指令
乘法指令為MUL,部分乘法指令同樣也會修改PSW的使用者狀態位。
Assembly language
MUL D3, D1, D2 //計算D1*D2的值,溢出位捨棄,結果存放到D3中
MUL E0, D1, D2 //計算D1*D2的值,結果存放到E0(D1+D2)中
MULS D2, D1, D2 //計算D1*D2的值,溢出則做飽和運算處理,結果存放到D3中
MUL.U E0, D1, D2 //計算無符號D1*D2的值,結果存放到E0(D1+D2)中
4.3.4 DIV除法指令
TriCore™ 指令集支持32位的有/無符號除法,硬件不支持雙精度64位觸發,對於雙精度64位的除法,則需要依賴於編譯器的軟件實現。另外由於除法指令計算時間相對較長,會消耗數個指令週期,因此會降低中斷的實時性響應,這種影響與商的有效位成正比。除法運算在計算時結果可以佔據一個暫存器對,商保存在第一個暫存器中,余數保存在第二個暫存器中。
有符號整數除法的計算相對於加法等有較多的判斷邏輯:
C
DIV E[c], D[a], D[b] //計算D[a]/D[b],商存放到E[c][31:0],餘數存放到E[c][63:32]中
dividend = D[a];
divisor = D[b];
if (divisor == 0) then {
if (dividend >= 0) then {
quotient = 0x7fffffff;
remainder = 0x00000000;
} else {
quotient = 0x80000000;
remainder = 0x00000000;
}
} else if ((divisor == 0xffffffff) AND (dividend == 0x80000000)) then {
quotient = 0x7fffffff;
remainder = 0x00000000;
} else {
remainder = dividend % divisor
quotient = (dividend - remainder)/divisor
}
E[c][31:0] = quotient;
E[c][63:32] = remainder;
無符號整數的計算也類似上面的形式:
C
DIV.U E[c], D[a], D[b] //計算無符號D[a]/D[b],商存放到E[c][31:0],餘數存放到E[c][63:32]中,U表示無符號
dividend = D[a];
divisor = D[b];
if (divisor == 0) then {
quotient = 0xffffffff;
remainder = 0x00000000;
} else {
remainder = dividend % divisor
quotient = (dividend - remainder)/divisor
}
E[c][31:0] = quotient;
E[c][63:32] = remainder;
浮點數的除法運算邏輯整體不複雜,根據IEEE754規範計算32比特浮點數除法,指令的後臺執行流程相對要多一些,這裡不詳細列出,感興趣的讀者可以查看內核手册。
Assembly language
DIV.F D3, D1, D2 //計算浮點數D1/D2的值,結果存放到D3中
4.3.5 ABS/ABSDIF絕對值指令
TriCore™ 指令集支持直接進行有符號數的絕對值運算,或者計算兩個數的差值的絕對值。
Assembly language
ABS D1, D2 //計算D2的絕對值,結果存放到D1中
ABSDIF D3, D1, D2 //計算D1-D2的值的絕對值,結果存放到D3中
4.3.6 MIN/MAX比較指令
TriCore™ 指令集支持直接對兩個數進行比較,取其較大值或較小值,輸入源可以是整形或者無符號整形。
Assembly language
MIN D3, D1, D2 //比較D1和D2的大小,將較小的存放到D3中
MIN.U D3, D1, 8 //比較D1和8的大小,將較小的存放到D3中,U表示無符號數
MAX D3, D1, D2 //比較D1和D2的大小,將較大的存放到D3中
MAX.U D3, D1, 8 //比較D1和8的大小,將較大的存放到D3中,U表示無符號數
4.3.7 CADD/CSUB/SEL條件指令
TriCore™ 對於整數運算提供了條件執行指令,即先對某個數進行判斷,來決定是否執行加減或選擇賦值來源。
Assembly language
CADD D4, D1, D2, D3 //如果D1非零,則D4=D2+D3,否則D4=D2
CADDN D4, D1, D2, 8 //如果D1為零,則D4=D2+8,否則D4=D2
CSUB D4, D1, D2, D3 //如果D1非零,則D4=D2-D3,否則D4=D2
SEL D4, D1, D2, D3 //如果D1非零,則D4=D2,否則D4=D3
SELN D4, D1, D2, 8 //如果D1為零,則D4=D2,否則D4=8
4.3.8 AND/OR/XOR邏輯指令
TriCore™ 支援 AND/OR/XOR 等按位邏輯指令,同時也支援結果取反的 NAND/NOR/XNOR 指令,以及源操作數取反的 ANDN、ORN 等。後者兩種容易混淆,需要注意區分,例如 NAND 和 ANDN。
Assembly language
AND D3, D1, D2 //計算 D1 與 D2 的按位與,結果存入 D3 中,也就是 D3 = D1&D2
OR D3, D1, 0xCC //計算 D1 與 0xCC 的按位或運算,結果存放到 D3 中,也就是 D3 = D1 | 0xCC
XOR D3, D1, D2 //計算 D1 與 D2 的按位異或,結果存放於 D3 中,也就是 D3 = D1^D2
NAND D3, D1, D2 //計算 D1 和 D2 的按位與運算,並對結果進行按位取反,將結果存放到 D3 中,即 D3 = ~(D1&D2)
ANDN D3, D1, D2 //計算 D2 進行按位取反,然後與 D1 進行按位取與,結果存放到 D3 中,也就是 D3 = D1 & ~D2
NOT D1 //計算 D1 進行按位取反,結果存放於 D1 中,即 D1 = ~D1
4.3.9 CLO/CLZ/CLS計位指令
TriCore™ 提供了位計數功能,能夠計算高位連續的 0、1 或符號位。
CLO(Count Leading Ones):計算從最高位開始的連續 1 的個數。例如,0xF0000000 的計算結果為 4,0xF8000000 的計算結果為 5。
CLZ(Count Leading Zeros):計算從最高位開始的連續 0 的個數。例如,0x0FFF0000 的計算結果為 4,0x07FF0000 的計算結果為 5。
CLS(Count Leading Signs):計算從次高位開始,與最高位相同的連續位數。例如,若第 31 位為 1,則從第 30 位開始計算連續 1 的個數。
Assembly language
CLO D2, D1 // 計算 D1 的 CLO 值,結果存放到 D2 中
CLZ D2, D1 // 計算 D1 的 CLZ 值,結果存放到 D2 中
CLS D2, D1 // 計算 D1 的 CLS 值,結果存放到 D2 中
4.3.10 SH/SHA/SHAS移位指令
TriCore™ 的移位指令支持有符號數移位,若為正數則左移,若為負數則右移。
Assembly language
SH D3, D1, D2 // 按照 D2 的值對 D1 進行移位,填充值始終為 0,結果存放到 D3 中
SHA D3, D1, D2 // 按照 D2 的值對 D1 進行移位,左移填充位為 0,右移填充位同符號位,結果存放到 D3 中
SHAS D3, D1, D2 // 與 SHA 計算相同,區別在於結果取飽和操作,S 代表 SATURATION
4.3.11 EXTR/INSERT位提取/插入指令
TriCore™ 支援對數據進行位元提取和位元插入功能。
位元提取是指從源操作數中提取特定的位元段,並將其賦值給目的操作數。操作數可以是整數或無符號整數。
Assembly language
EXTR D3, D1, D2, #4 //D2[4:0]為pos,4為width,然後按照下圖從D1中截取,賦值到D3中
Assembly language
EXTR.U D3, D1, D2, #4 //D2[4:0]為pos,4為width,然後依下圖從D1中截取,賦值到D3中,U表示無符號
Assembly language
DEXTR D3, D1, D2, 5 //將{D1,D2}左移5位,然後截取[63:32]位,賦值給D3
Assembly language
INSERT D3, D1, D2, D4, #8 //D4[4:0]為Pos,8為width,將D2依照下圖位置左移,然後覆寫D1該段,結果賦值為D3
4.4 Packed打包計算
TriCore™ 的算術運算子還有一種打包運算方法,對於一個32位元數據,可以依照位元組或半字分別計算,結果分別存放到指定位置。
Assembly language
ADD.B D3, D1, D2 //將D1與D2依照位元組依序相加,結果分位元組存放到D3中,任一位元組計算溢出,V和SV都會置位
ADD.H D3, D1, D2 //將D1與D2依半字依序相加,結果分半字存放到D3中,任一個半字計算溢出,V和SV都會置位
4.5 EQ/NE/LE/GE比較指令
TriCore™ 中比較指令的使用也是比較頻繁的,主要用於條件指令的操作修飾符,例如分支跳轉等。
- EQ (Equal)
- NE (Not Equal)
- LT (Less Than)
- GE (Greater than or Equal to)
另外對於大於和小於等於,TriCore™ 是沒有指令的,需要依賴上述指令隱式實作。
4.6 地址指令
TriCore™ 中對於位址的運算和操作和資料還是具有一定差異的,同時硬體上採用了通用位址暫存器等設計來實現加速。一般使用賦值載入以及LEA指令進行位址計算。
LEA(Load Effective Address)指令用於計算絕對位址,並保存到指定的位址暫存器。
Assembly language
MOVH.A A5, #0x7001 //將0x7001賦值給A5高16位,低16位置0
LEA A5, [A5]0x4864 //將A5中的值加上0x4864,然後賦值給A5,組成0x7001 4864位址值
4.7 跳躍指令
TriCore™ 中的分支指令大致分為以下三種:
- Jump:跳轉指令,該指令不做任何其他操作,僅修改PC指標到指定位址運行,一般用於常規指令運算或分支跳轉;
- Jump And Link:連結跳轉指令,該指令除了跳轉以外,也會將其下一指令的位址存到回傳位址暫存器中;
- Call And Return:呼叫與回傳指令,指令必須成對使用,呼叫指令會保存高上下文,回傳指令則會恢復高上下文。一般用於函數呼叫。
Assembly language
J foobar //跳到foobar
JI A5 //跳到A5指向地址
JL foobar //將下一指令位址寫入回傳位址暫存器中,並跳到foobar CALL foobar //儲存高上下文,將下一條指令位址寫入到回傳位址暫存器中,跳到foobar
JEQ D1, D2, foobar //如果D1==D2,跳到foobar
JGE D1, D2, foobar //如果D1>=D2,跳到foobar
4.8 上下文相關指令
TriCore™ 的上下文除了中斷、Trap以及CALL、RET指令能夠操作以外,還提供了專有的上下文操作指令。主要分為以下兩類:
- SVLCX/RSLCX:低上下文保存與復原指令,使用的記憶體是CSA區域,並同時會更新PCX以及FCX暫存器;
- STUCX/STLCX/LDUCX/LDLCX:上下文載入和儲存指令,使用的區域需要指令指定,且不會更新PCX和FCX;
這裡要注意區分,第二種使用方法雖然也能夠保存和載入通用暫存器,但是它是不使用CSA區域的,同時也不會更改PCX和FCX暫存器。
4.9 系統相關指令
TriCore™ 提供了核心系統指令,主要包括System Call、指令和程式屏障、開關中斷等功能。
4.9.1 SYSCAL系統調用指令
TriCore™ 的系統呼叫是透過Trap6來實現的,一般用來給OS做核心模式切換使用的。
Assembly language
SYSCALL 4 //執行System Call,4為系統呼叫的入參
4.9.2 DYSNC/ISYNC數據/指令屏障
這兩條指令的作用是作為資料/指令屏障而使用的。
DYSNC指令
在電腦科學中,資料屏障(Memory Barrier)是一種用於控制處理器和記憶體之間資料同步的機制。它確保在多核心或多執行緒系統中的記憶體存取順序得到正確的維護,避免了由於並發存取導致的資料不一致性和競態條件。 DYSNC作為一個屏障,將其前後的指令進行分隔,確保後面的指令一定在前面的所有記憶體存取完成後執行,包括Cache,防止因編譯器優化或指令流水、多執行緒等導致的資料存取次序錯誤。
ISYNC指令
指令屏障(Instruction Barrier)是另一個用來控制處理器執行指令順序的機制。它確保在處理器執行指令時,按照程式設計師的預期順序執行,避免指令重排或亂序執行導致的問題。另外如果指令修改了系統的狀態,例如記憶體保護設置,則指令能夠保證其後執行的所有程式都是按照新的記憶體保護設定執行的。
Assembly language
DYSNC //數據屏障
ISYNC //指令屏障
4.9.3 MFCR/MTCR核特殊功能暫存器指令
TriCore™ 的核特殊功能暫存器CSFR是每個核特有的,而且對於多核心其位址是不進行區分的,因此必須透過特殊的指令進行操作,哪個核執行該指令就作用於哪個核心。
MFCR(Move From Core Special Function Register)讀取指令
MFCR指令用於從CSFR讀取內容,例如我們最常用的讀取目前核的ID。
MTCR(Move To Core Special Function Register)操作指令
MTCR指令用於修改CSFR中的內容,後面必須接一條ISYNC用於指令屏障,確保操作完成。
Assembly language
MFCR D15, #0xFE1C //讀取CoreID,結果儲存到D15中
MTCR #0xFE04, D15 //修改PSW暫存器,將其值修改為D15
4.9.4 ENABLE/DISABLE開關中斷
TriCore™ 中每個核的中斷使能是相互獨立的,只需要使用ENABLE/DISABLE指令即可。
Assembly language
ENABLE //開斷
DISABLE //關中斷
4.9.5 TRAP指令
TriCore™ 提供了使用者軟體觸發Trap的方法,可以使用TRAPV或TRAPSV,當OverFlow或StickyOverflow位置位時觸發Trap。
Assembly language
TRAPV //Trap on overflow
TRAPSV //Trap on sticky overflow
4.10 16Bit指令
TriCore™ 的大部分指令在某些情況下是支援16位元格式的,甚至有些指令只有16位元格式,具體可以參考核心手冊,上面有每條指令的格式說明。
#05 使用示例
下面我們透過一段範例程式碼,透過對彙編進行說明,來幫助讀者了解TriCore™ 的指令集。下面的程式碼透過輾轉相除法求兩個數的最大公約數,資料來源和結果都用了全域變量,以使用Load/Store指令。
5.1 示例代码
C
//變數宣告
volatile int MyResult;
volatile int num1=15, num2=20; //volatile防止編譯器最佳化
// 函數宣告
int TestInstruction(void);
int gcd(int a, int b);
void echoInit(void){
TestInstruction();
}
int TestInstruction(void) {
// 計算並輸出最大公約數
MyResult = gcd(num1, num2);
printf("GCD of %d and %d is %d\n", num1, num2, MyResult);
return 0;
}
// 計算最大公約數的函數
int gcd(int a, int b) {
int temp;
// 輾轉相除法
while (b != 0) {
temp = b;
b = a % b;
a = temp;
}
return a;
}
5.2 TestInstruction調用
我們先看TestInstruction的呼叫這裡,使用的是Jump指令,而不是JL或Call。
這樣做的好處是,當該條代碼是調用方的最後一條指令,在其執行完畢之後已經沒有必要返回調用方了,而是直接返回調用方的上一層調用方,這樣的優化能夠節省系統時間,提高性能。
但是缺點是呼叫堆疊中就會少一層,圖中echoInit就沒有顯示,不便於調試。所以我們在偵錯過程中常會遇到呼叫堆疊的資訊缺失,或是使用Return偵錯操作的時候會直接回傳兩層的情況,都是這個原因。
5.3 TestInstruction函數
TestInstruction函數的C語言總共只有三行,我們一一展開描述。
第一行是以num1和num2為入參,計算最大公約數:
- 首先是兩個LD.W指令,因為是全域變量,所以要從記憶體中取出;
- 然後是CALL指令,呼叫gcd函數,TriCore™ 的入參是依序使用D4~D7資料暫存器,或A4~A7位址暫存器;
- 然後傳回值在D2暫存器中,使用ST.W將其存入MyResult全域變數中;
第二行是呼叫printf函數將其顯示出來:
- 首先是三條LD.W指令將全域變數讀出來(這裡因為我使用了volatile,所以沒有編譯器優化,每次都重新讀寫);
- 接著是三條ST.W指令將載入的三個變數壓入堆疊中,作為printf函數的第2、3、4個入參;
- 然後是MOVH.A和LEA指令將位址0x8001 421C寫入到A4暫存器作為第一個入參,這裡printf的第一個入參是字串,傳入的是字串的首位址,我們開啟記憶體使用ASCII格式可以看到這段字元;
- 然後進行printf函數呼叫。
最後一行是return 0:
- 使用MOV指令,將返回值寫入D2暫存器;
- 使用RET指令返回,前面提到,TestInstruction是透過Jump呼叫的,所以這裡會直接回到呼叫echoInit的那層;
5.4 gcd函數
gcd函數這裡是while循環,透過條件跳轉,來進行循環計算。
- 首先是一條JG指令(目前觀察其功能同J,但無論是Infineon手冊或Tasking手冊都沒有找到JG的相關描述),作為入口跳到0x80004078;
- 在0x80004078處通過條件指令JNE,當D5(形參b)不等於0時跳轉0x80004070進入循環;
- 然後在0x800040700開始,執行MOV、DIV、MOV進行演算法的數學計算,接著進入下一輪的循環判斷;
當迴圈結束後執行到0x8000407C,這裡將D4(形參a)的值寫入D2中,並呼叫RET函數回傳;
至此全部的函數呼叫就介紹完了,不同的編譯器或編譯最佳化等級、編譯選項等可能有不同的組譯結果,但是整體的內容是差不多的,希望讀者能夠透過這個範例,了解TriCore™ 指令集的架構。
#06 小結
本文介紹了英飛凌的AURIX™️ TC3XX核心TriCore™ 的核心架構,並對其指令集系統進行了講解,並透過程式碼範例進行講解,透過本文讀者能夠對TriCore™ 核心及指令集有一定的了解和掌握。
掃描QR碼,關注英飛凌汽車電子尋找更多應用程式或產品信息
評論