DM814X系列SysLink異構核心通訊元件介紹
1:異構多核間通訊的基本原理
本文主要基於TMS320DM814x系列達芬奇異構多核處理器進行介紹。DM814x整合ARMcortex-A8核、c674xDSP核、高清視訊處理子系統(HDVPSS)以及高清視訊/影象協處理器(HD-VICP2)。HDVICP2由基於ARM968核心的Video M3核管理,可完成H.264、MPEG_4、MJPEG編解碼;HDVPSS由VPSS M3核管理,具有2路高清視訊捕獲通道及顯示通道。
異構多核處理器大多采用主從式的結構。主從式結構根據不同核的功能把處理器核分為主核和從核。主核的結構和功能一般較為複雜,負責全域性資源、任務的管理和排程並完成從核的引導載入。從核主要接受主核的管理,負責執行主核分配的任務,並具有本地任務排程與管理功能。在多核處理器中,根據不同核的結構,各個核可執行相同或不同的作業系統。在DM814x中ARM為主核、DSP和協處理器為從核,ARM 核執行開源的Liunx系統,也可以執行TI的實時作業系統SYS/BIOS,DSP核和M3核執行實時作業系統SYS/BIOS。
主從式的異構多核處理器核間的互聯結構如圖1所示。
圖1 主從式異構多核互聯結構
從圖1可知,為了實現異構多核之間的通訊,在晶片內設計了核間中斷控制器以及核間互聯的匯流排。核間中斷是多核問任務同步與通訊的橋樑,核間中斷暫存器各標誌位分配給晶片內不同的核,通過核間中斷向另一個核傳送中斷請求,執行相應的中斷服務程式或通過中斷暫存器傳遞地址,配合共享記憶體實現資料的傳遞與共享。各個核心對於外設的訪問則通過配置匯流排等實現。
綜上,實現各個處理器核有效的管理和通訊需要具備以下功能:
1)主處理器對從處理器進行管理;
2)內部處理器之間資訊的傳輸和交換。
前者可通過片上互聯實現,後者則由核間中斷和記憶體共享來實現。下面將以DM814x為例詳細說明上述功能的實現。
1.1 核間中斷
為了實現高效的片上核間通訊,DM814x系列達芬奇處理器片上整合硬體郵箱中斷(Mailbox Interrupts)和自旋鎖(Spinlocks)。DM814x有12個郵箱,每個郵箱有4箇中斷源以向4個核傳送中斷,並且提供4個訊息深度的FIFO,每個訊息32位寬。每個郵箱都可以由任意一個核讀寫,通過相應的暫存器設定中斷髮送者以及接收者,通過訊息暫存器傳遞訊息圳。ARM、DSP和2個M3媒體控制器之間通過系統級的郵箱進行通訊,每個HDVICP2有各自獨立的郵箱,可以向自身內部模組和其他核發送中斷。
1.2 共享記憶體
共享記憶體的實現首先需要系統對記憶體進行合理的對映與管理,每一個子系統都有自己的記憶體和記憶體對映暫存器,為了簡化軟體的開發,DM814x使用統一的對映,從而使得晶片資源具有一致性。
DM814x中執行Linux和SYS/BIOS兩套作業系統,它們分別採用makefile機制與XDC構建系統。Linux在執行時通過核心啟動引數來配置由核心管理的記憶體空間,SYS/BIOS在構建時採用XDC配置檔案進行資料區、程式碼區等記憶體區的分配。系統構建與執行時需要對各個核所使用的記憶體以及共享記憶體進行劃分。作為共享記憶體,可供不同處理器共享使用,為了實現共享資源的互斥訪問,晶片整合硬體自旋鎖,以解決多核間共享資源的訪問互斥問題。
2:異構多核間任務通訊的實現
異構多核處理器實現高效的核間通訊與協作,不僅需要片上硬體模組的支援,還需要在軟體中提供任務間通訊的機制。Linux和SYS/BIOS都提供了本作業系統上程序通訊的機制,在Linux中有管道、訊息佇列、訊號量、共享記憶體等任務間同步與互斥的方式,SYS/BIOS中有 訊號量,郵箱,事件等方式,而對於核間的通訊,TI提供了 一套異構多核間通訊元件SysLink,為使用者核間通訊提供了多種的實現方法下面將介紹SysLink特性和相關的API。
SysLink元件主要由以下元件構成:
系統管理(System Manager)
處理器管理(Processor Manager——PM)
核間通訊(Inter-Processor Communication——IPC)
其他模組(Utility Modules)
2.1、系統管理
系統管理模組(IPC module)為方便多核管理提供了簡單快捷的方法,此外也為系統資源(系統記憶體)的管理提供了介面。
IPC模組提供的功能包括:
SysLink系統初始化(syslink_setup()、syslink_destroy())併為其他SysLink元件分配記憶體,包括IPC模組和PM模組(MemoryOS_setup()、Ipc_setup(&config))
系統配置:任何系統級別的配置資訊是由系統管理;
2.2、處理器管理
ProcMgr模組為從處理器提供了以下services
1)boot-loading從處理器
2)讀寫從處理器的記憶體區
3)從處理器電源管理
因此該模組為以上services提供了以下介面:
Loader:處理器的Loader介面有多種實現實現方式,被寫入的檔案形式可能是如COFF、ELF、動態loader(不太清楚這是啥)或者自定義型別的檔案等等;
PowerManager:考慮到處理器管理模組的通用性並且希望電源管理模組可以自定義,在SysLink中電源管理是可嵌入處理器管理的獨立模組;
ProcessorManager:為處理器提供了載入、MMU管理(A8支援)、讀寫從處理器記憶體等介面。在SysLink系統中,為了方便管理,每個處理器都會被編碼(即Processor ID);如圖中所示,在該系統中使用了硬體抽象層來遮蔽底層硬體差異,這樣做的好處就是為不同的底層硬體提供了通用的軟體介面。SysLink中從處理器的Loader檔案理論上支援多種格式,在SysLink Release版本中主要支援COFF和ELF。在TI的編譯系統中,可以以可執行檔案的字尾名來區別COFF檔案和ELF檔案。當前的ELF Loader只支援順序載入,即只有當一個從處理器載入並啟動後才能去載入下一個從處理器,不支援並行載入。
圖2:Loader流程圖
2.3、處理器間通訊協議(Inter-Processor CommunicationProtocols)
SysLink下定義了以下幾種通訊機制:
Notify、MessageQ、ListMp、GateMp、HeapBufMp、HeapMemMp、FrameQ(通常用於raw 視訊資料)、RingIO(通常用於音訊資料)
這些通訊機制的介面都有一下幾個共同點:
所有IPC通訊機制的介面都由系統規範化的命名;在HLOS端,所有IPC 介面都有<Module>_setup() and<Module>_destroy()。API用於初始化或者銷燬相應的IPC Module;部分初始化還需要提供配置介面,<Module>_config();所有的例項化都需要使用<Module>_create()來建立,使用<Module>_delete()來刪除;在更深層次使用IPC時需要用API <Module>_open()來獲取handle,在結束使用IPC時需要用API <Module>_close()來回收handle;IPC的配置多數都是在SYS/BIOS下完成配置的,對於支援XDC配置的則可以使用靜態配置方法;每個IPC模組都支援Trace資訊用於除錯,而且支援不同的trace等級;部分IPCs提供了專門的APIs來用於提取分析資訊;
2.3.1、Notfiy
Notify元件將硬體中斷抽象成多組邏輯事件,是一種簡單快捷的傳送低於32bit資訊的通訊方式。Notify模組使用硬體中斷,因此不能被頻繁排程。事件是有優先順序的,EventId越小優先順序越高,事件0的優先順序最高,隨著EventId增大優先順序依次遞減;當多個事件被觸發,優先順序最高的會最先響應。如信令傳遞、buffer指標傳遞等。在信令傳遞時使用高優先順序的事件,如事件0;而在傳遞buffer指標是可以使用低優先順序的事件,如事件30等。由於其他模組使用了Notify機制,因此在SysLink中預留了部分事件號,這部分事件號使用者需要慎重選用(如果你沒有使用其他組建的話,可以考慮佔用這部分事件號),在註冊事件前可以使用Notify_eventAvailable()來檢查該事件是否可用,即該中斷號上的該事件號是否被註冊。
2.3.2、MessageQ
MessageQ,基於佇列的訊息傳遞,有以下特點:
實現了處理期間變長訊息的傳遞;訊息的傳遞都是通過操作訊息佇列來實現的;每個訊息佇列可以有多個寫者,但只能有一個讀者;每個任務(task)可以對多個訊息佇列進行讀寫;一個宿主在準備接收訊息時,必須先建立訊息佇列,而在傳送訊息前,需要開啟預定的接收訊息佇列;
2.3.3、ListMP
ListMP實現了多宿主雙向迴圈連結串列,即該雙向迴圈連結串列為多個處理器共同擁有,可以由多個處理器共同維護,共同使用。ListMP的實現區別於一般的雙向迴圈連結串列,因此它不僅具有雙向迴圈連結串列的特性外,還增添了其他的特性,比如以下幾點:
實現了簡單的多宿主協議,支援多個讀寫者(multi-reader、multi-write);
使用Gate作為內部保護機制,防止多個宿主處理器同時訪問該連結串列;
ListMP的實現並未加入通知機制,如果需要的話,可以在外部封裝是引入Notify機制來實現;使用ListMP機制來管理的buffers都需要從共享記憶體區分配,包括從堆記憶體分配的buffers以及動態分配的記憶體。
2.3.4、GateMp
GateMP是針對於多處理器共享資源的一種保護機制,就如其名字一樣,把共享資源比作房子,那麼GateMP就是這個房子的門。GateMP元件實現了開關門的機制,用於保護共享資源一次只被一個處理器讀寫。根據SOC硬體資源配置的不同,GateMP的實現有所不同。對於硬體支援Hardware Spinlock的可以基於H/W spinlock來實現GateHwSpinlock;而對於沒有該硬體資源的系統中,則使用軟體方法(Peterson演算法)來實現GatePeterson。
2.3.5、HeapMP
HeapMP主要包括HeapBufMP和HeapMemMP,用於共享記憶體區的堆記憶體配置和管理。
HeapMP具備以下幾個特徵:
支援多宿主,即無論是執行HLOS的主處理器還是執行SYS/BIOS的從處理器都可以配置和管理堆記憶體;可以將共享記憶體區配置成緩衝池(buffer pools);可以從共享記憶體區分配和釋放緩衝區;
HeapBufMP為使用者提供了固定大小的緩衝池管理介面;HepMultiBufMP為使用者提供了可配置大小的緩衝池管理介面。
2.3.6、FrameQ
FrameQ是專門為傳遞視訊幀而設計出來的元件。FrameQ的基本資料結構是可用於queue/dequeue資料的資料佇列,封裝了視訊幀快取指標、幀資料型別、幀寬、幀高、時間戳等資訊。
對於FrameQ模組具有以下特徵:支援多個讀者,但寫者唯一;可以分配和釋放Frames;可以對指向同一塊記憶體區多次分配和初始化成新的幀buffer;FrameQ允許有多個佇列,在多通道的運用中,視訊幀會根據通道號被分配到相應的幀佇列中;
2.3.7、RingIO
RingIO是基於資料流的環形緩衝buffer,而且針對於音視訊資料的特性做了優化。
RingIO支援一下特性:僅支援一個讀者和一個寫者;讀寫相對獨立,可以在不同的程序或者處理器中同時進行讀寫操作;
2.4、公共元件(基礎元件)
Utility Modules包括SharedRegion、List、Trace、MultiProc、NameServer等,這些模組是上層元件實現的基礎。
2.4.1、SharedRegion
SharedRegion有兩種配置方式,即靜態配置方法和動態配置方法。實際配置中需要指明共享記憶體區在各個處理器中對映的虛擬地址及堆疊設定等,SharedRegion模組由於其狀態都存在處理器本地的記憶體中,因此其本身並不會佔用共享記憶體區空間。所有的SharedRegion模組API都是用Gate用於程序互斥操作。
SharedRegion模組會為系統中每個處理器建立一個共享記憶體查詢表。在這個查詢表中包含了所有處理器與共享記憶體區的關係及相關設定。如果某塊共享記憶體區對於某處理器是不能訪問,那麼在表中會設定為空。
2.4.2、MultiProc
MultiProc模組用於多核處理器中唯一的標識處理器(多處理器ID管理,在 fwload的程式中,其中呼叫MultiProc_getId獲取ProcId用於ProcMgr_open的引數),在使用該模組前,需要在IPC環境中使用*.cfg指令碼來配置多處理器環境。
3:基於link的McFW的軟體框架介紹
基於sysLinkIPc底層通訊模組,TI設計了一個高清音視訊採集、編碼、傳輸的McFW軟體框架,這個框架將執行在各個核上的執行緒統一為一個Link結構。
3.1 Link介紹
每一個link就是一個具有一定功能的執行緒和相關資料結構的組合,每一個link都有一個唯一的ID,可以從這個ID中可以看出這個Link是執行在哪一個核上的,每個link可以有多個輸入佇列和輸出佇列以及Notify機制通知新資料是否準備好。
並且在Link建立的時候,要告訴這個Link的上一個link,和下一個要連線的link,從而把所有的link連線到一起,組成一個chain。
而Link中的各個執行緒則是通過MessageQ訊號量等執行緒通訊機制進行同步與互斥,對於視訊資料則是通過核間共享記憶體避免資料的轉移,從而可以實現高效的核間資料共享。
3.2 Link 的工作機制
在每個link中必須實現一些函式並在初始化時註冊這些函式指標給link管理的核心模組,用於幀資料的獲取、釋放、dump相關狀態等。
對於任一個link想從它的上游link獲取幀資料都需要呼叫link管理核心函式System_getLinksFullFrames(),該函式內部會發送訊息到對應的上游link,觸發該link向管理模組註冊的回撥函式System_LinkGetOutputFramesCb()將幀資料傳遞給該link;
同樣的,在當一個link想釋放處理完畢的幀buffer給上游link時需要呼叫link管理核心函式System_putLinksEmptyFrames(),該函式內部會發送訊息到對應的上游link,觸發它註冊的回撥函式System_LinkPutEmptyFramesCb()將幀buffer回收,用於後續的資料處理;
建立chain時,所有的下游link的link都會註冊一個System_GetLinkInfoCb()的回撥函式,在下游link的driver中會在建立driver時呼叫System_linkGetInfo()函式來獲取上游link的相關引數。
通過上述的方法,對於一個link來說就不需要關心和它互動的是哪一個link,所有的定址都通過linkID來自動查詢,並且同一個link實現可以和不同的link互動,而不需要改變函式的實現。