Davinci的異構多核間通訊基礎元件SysLink 2.0
=======================================================================================================
部分模組由於沒有用過,也沒看到SysLink包中提供例子,所以只是簡略介紹。
注:這部分涉及到cfg指令碼(XDC)配置,用得比較少,涉及到很多TI及其他公司封裝的java類庫,後續等俺學習了再詳細說明。
=======================================================================================================
緒論:Davinci中的多核系統一般由GPP+DSP構成,也就是所謂的異構多核(同構是指內部核的結構是相同的,而異構是指內部的核結構是不同的),為了為異構多核處理器間提供高效的異構多核協作,需要建立異構多核間的通訊機制。在TI提供的異構多核間通訊元件SysLink中,核間通訊機制為使用者提供了多種實現方法。下面的內容將介紹SysLink架構、特性和相關的API。
關鍵詞
縮寫詞 | 解釋 |
HLOS | Higher Level Operating System |
RTOS | Real Time Operating System |
CCS | Code Composer Studio |
IPC | Inter-Processor Communication |
GPP | General Purpose Processor e.g.ARM |
DSP | Digital Signal Processor e.g. C64X |
CGTools | Code Gen Tools e.g.Compiler Linker Archiver |
SysLink工具包為異構多核之間的通訊提供基礎開發介面,使得多核系統之間更方便的交換資訊。在多核異構系統中,每個核心執行的作業系統和所處地位各不相同。所執行的OS可以是HLOS,如Linux、WinCE等,也可以是RTOS如SYS/BIOS或者QNX。異構多核系統中主處理器(Host Processor)肩負著控制從處理器(Slave Processor)的責任。
廣義上的SysLink包含了執行在HLOS上的SysLink和執行在RTOS上的IPC。其基本架構如下圖所示:
SysLink工具包主要針對於嵌入式作業系統中的應用,主要由以下元件構成:
- 系統管理(System Manager)
- 處理器管理(Processor Manager——PM)
- 核間通訊(Inter-Processor Communication——IPC)
- 其他模組(Utility Modules)
1、系統管理
系統管理模組(IPC module)為方便多核管理提供了簡單快捷的方法,此外也為系統資源(e.g.系統記憶體)的管理提供了介面。
IPC 模組提供的功能包括:
- SysLink系統初始化(syslink_setup()、syslink_destroy())併為其他SysLink元件分配記憶體,包括IPC模組和PM模組(MemoryOS_setup()、Ipc_setup(&config))
- 系統配置:任何系統級別的配置資訊是由系統管理;
2、處理器管理
ProcMgr模組為從處理器提供了以下services:具體例子可以參見DVRRDK_xx.xx.xx.xx\dvr_rdk\mcfw\src_linux\utils\fw_load中firmware load的例子。
- boot-loading從處理器
- 讀寫從處理器的記憶體區
- 從處理器電源管理
- Loader:處理器的Loader介面有多種實現實現方式,被寫入的檔案形式可能是如COFF、ELF、動態loader(不太清楚這是啥)或者自定義型別的檔案等等;
- Power Manager:考慮到處理器管理模組的通用性並且希望電源管理模組可以自定義,在SysLink中電源管理是可嵌入處理器管理的獨立模組;
- Processor Manager:為處理器提供了載入、MMU管理(A8支援)、讀寫從處理器記憶體等介面。
Processor Manager系統框架圖如下
在SysLink系統中,為了方便管理,每個處理器都會被編碼(即Processor ID);如圖中所示,在該系統中使用了硬體抽象層來遮蔽底層硬體差異,這樣做的好處就是為不同的底層硬體提供了通用的軟體介面。
注:
- SysLink中從處理器的Loader檔案理論上支援多種格式,在SysLink Release版本中主要支援COFF和ELF。在TI的編譯系統中,可以以可執行檔案的字尾名來區別COFF檔案和ELF檔案,字尾名中帶有‘e’的是ELF(如:xxx.xe64P),不帶‘e’的是COFF檔案(如:xxx.x64P)。
- 當前的ELF Loader只支援順序載入,即只有當一個從處理器載入並啟動後才能去載入下一個從處理器,不支援並行載入。
3、處理器間通訊協議(Inter-Processor Communication Protocols)
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來用於提取分析資訊;
3.1、Notify
Notify元件將硬體中斷抽象成多組邏輯事件,是一種簡單快捷的傳送低於32bit資訊的通訊方式。
Notify元件提供了以下介面:
- 初始化並配置Notify元件;Notify_attach();
- 註冊/登出事件;Notify_registerEvent()/Notify_unregisterEvent()/Notify_registerEventSingle()/Notify_unregisterEventSingle()
- 傳送帶引數的事件給某處理器;Notify_sendEvent()
- 通過回撥函式接收事件;Notify_FnNotifyCbck()
- 使能/禁用事件;Notify_diableEvent()/Notify_enableEvent()
- 其他邏輯介面;Notify_eventAvailable()/Notify_intLineRegistered()/Notify_numIntLines()/Notify_restore()
- 同一個中斷號可以註冊多個事件,同一個事件可以有多個回撥函式或者多個宿主(可以是處理器、執行緒或者任務),事件被觸發後所有宿主都會被喚醒;
- 一個事件可以接收多個宿主傳送來的通知(notification),事件所攜帶的引數最大支援32bit;
- 事件是有優先順序的,EventId越小優先順序越高,事件0的優先順序最高,隨著EventId增大優先順序依次遞減;當多個事件被觸發,優先順序最高的會最先響應;
- Notify模組使用硬體中斷,因此不能被頻繁排程。
Notify元件常用於傳遞附帶訊息少於32bit的場景,如信令傳遞、buffer指標傳遞等。在信令傳遞時使用高優先順序的事件,如事件0;而在傳遞buffer指標是可以使用低優先順序的事件,如事件30等。
在Notify_sentEvent() API中帶有引數waitClear,該引數為可選引數,如果waitClear為TRUE,這就意味著多宿主事件無法及時響應,必須等待前一宿主事件結束後才能響應下一宿主;如果waitClear為FALSE,最好不要為事件附帶引數,否則多宿主事件可能會由於訊息被覆蓋而出現丟訊息的現象。該API最好不要在中斷服務程式(ISR)中呼叫(特別是waitClear = TRUE時),否則會導致中斷排程出現異常(表現之一:高優先順序的中斷響應會延遲);此外該API不能再使用GateMP模組鎖保護的程式段中呼叫,否則可能會導致作業系統死鎖。
由於其他模組使用了Notify機制,因此在SysLink中預留了部分事件號,這部分事件號使用者需要慎重選用(如果你沒有使用其他組建的話,可以考慮佔用這部分事件號),在註冊事件前可以使用Notify_eventAvailable()來檢查該事件是否可用,即該中斷號上的該事件號是否被註冊。
Module | Event Ids |
FrameQBufMgr | 0 |
FrameQ | 1 |
MessageQ(TransportShm) | 2 |
RingIO | 3 |
NameServerRemoteNotify | 4 |
3.2、MessageQ
MessageQ,顧名思義,基於佇列的訊息傳遞,可不是MaggieQ噢,哈哈。
MessageQ有以下特點:
- 實現了處理期間變長訊息的傳遞;
- 訊息的傳遞都是通過操作訊息佇列來實現的;
- 每個訊息佇列可以有多個寫者,但只能有一個讀者;每個任務(task)可以對多個訊息佇列進行讀寫;
- 一個宿主在準備接收訊息時,必須先建立訊息佇列,而在傳送訊息前,需要開啟預定的接收訊息佇列;
MessageQ元件常用在滿足以下條件的場景中:
- 在訊息傳遞中有多個寫者,但僅有一個讀者;
- 所需要傳遞的訊息超過32bit,且長度可變;讀寫者的緩衝區大小相同;
- 處理期間需要頻繁傳遞訊息,在這種情況下,訊息被依次放入佇列,能保證不會丟訊息;
- 訊息佇列為空時,呼叫MessageQ_get()獲取訊息時會被阻塞,直到訊息佇列被寫入訊息;
- 支援處理器間移動訊息佇列,在這種情況下,呼叫MessageQ_open()來定位佇列位置,而訊息傳遞部分程式碼不需要改動;
MessageQ元件提供了以下幾個API:
- 訊息佇列初始化:MessageQ_Params_init()
- 訊息佇列建立/銷燬:MessageQ_create()/MessageQ_delete(),create建立訊息佇列,並分配相應儲存空間
- 訊息佇列開啟/關閉:MessageQ_open()/MessageQ_close(),open時會返回遠端處理器上的QueID的地址。
- 為訊息佇列分配堆記憶體:MessageQ_alloc()/MessageQ_free()
- 為訊息佇列註冊/登出堆記憶體:MessageQ_registerHeap()/MessageQ_unregisterHeap()
- 向訊息佇列中放入/獲取訊息:MessageQ_put()/MessageQ_get()
- 其他邏輯API:
- 獲取訊息佇列ID:MessageQ_getQueueId()
- 獲取訊息佇列中訊息數:MessageQ_count()
- 在訊息佇列中嵌入訊息:MessageQ_setReplyQueue()
- 為訊息佇列解阻塞:MessageQ_unblock()
- 為除錯訊息佇列加入Trace:MessageQ_setMsgTrace()
3.3、ListMP
ListMP實現了多宿主雙向迴圈連結串列,即該雙向迴圈連結串列為多個處理器共同擁有,可以由多個處理器共同維護,共同使用。
ListMP的實現區別於一般的雙向迴圈連結串列,因此它不僅具有雙向迴圈連結串列的特性外,還增添了其他的特性,比如以下幾點:
- 實現了簡單的多宿主協議,支援多個讀寫者(multi-reader、multi-writee);
- 使用Gate作為內部保護機制,防止多個宿主處理器同時訪問該連結串列;
ListMP元件常用於滿足一下條件的場景中:
- 需要被多個宿主訪問並且需要頻繁傳遞訊息或者資料;
- 可用於無規則的訊息傳遞,基於連結串列實現,因此讀者可以遍歷所有物件,並選出需要的物件進行處理;如果硬體支援快速佇列,則無法完成佇列遍歷操作(WHY);
- 可以自定義訊息優先順序,同樣是基於連結串列實現,讀者可以隨意的選擇在連結串列頭部還是連結串列的尾部來插入訊息或者實現連結串列物件的位置調整,進而實現訊息的優先順序選擇;如果硬體支援快速佇列,則無法完成佇列遍歷操作(WHY);
- 無內建通知機制,可以靈活的外部通知機制來實現。譬如根據實際情況,選用Notify來實現,亦或是使用選用MessageQ則可以使用最少的中斷資源實現效能優良的通知機制,缺點是需要額外的程式碼實現通知機制。
- ListMP引數初始化:ListMP_Params_init()
- ListMP建立/銷燬:ListMP_create()/ListMP_delete()
- ListMP開啟/關閉:ListMP_open()/ListMP_close()
- ListMP相關連結串列操作:
- 判斷連結串列空:ListMP_empty()
- 獲取保護鎖:ListMP_getGate()
- 獲取連結串列頭/表尾:ListMP_getHead()/ListMP_getTail()
- 連結串列插入操作:ListMP_insert()
- 獲取連結串列上游元素/下游元素:ListMP_next()/ListMP_prev()
- 插入元素到連結串列頭/尾:ListMP_putHead()/ListMP_putTail()
- 刪除元素:ListMP_remove()
3.4、GateMP
GateMP是針對於多處理器共享資源的一種保護機制,就如其名字一樣,把共享資源比作房子,那麼GateMP就是這個房子的門。GateMP元件實現了開關門的機制,用於保護共享資源一次只被一個處理器讀寫。根據SOC硬體資源配置的不同,GateMP的實現有所不同。對於硬體支援Hardware Spinlock的可以基於H/W spinlock來實現GateHwSpinlock;而對於沒有該硬體資源的系統中,則使用軟體方法(Peterson演算法)來實現GatePeterson。
GateMP元件框架如下:
GateMP元件對使用者提供了以下API介面:
- GateMP初始化:GateMP_Params_init();
- GateMP建立/刪除:GateMP_create()/GateMP_delete();
- GateMP開啟/關閉:GateMP_open()/GateMP_close();
- 進入/離開GateMP保護:GateMP_enter()/GateMP_leave();
- 獲取當前GateMP的保護型別:GateMP_getLocalProtect()/GateMP_getRemoteProtect();
注:如果某個處理器在想使用被某個GateMP保護的共享資源,那麼該處理器會被阻塞,直到該資源被釋放(即GateMP_leave())。
3.5、HeapMP
HeapMP主要包括HeapBufMP和HeapMemMP,用於共享記憶體區的堆記憶體配置和管理。
HeapMP具備以下幾個特徵:
- 支援多宿主,即無論是執行HLOS的主處理器還是執行SYS/BIOS的從處理器都可以配置和管理堆記憶體;
- 可以將共享記憶體區配置成緩衝池(buffer pools);
- 可以從共享記憶體區分配和釋放緩衝區;
3.5.1、HeapBufMP
HeapBufMP為使用者提供了固定大小的緩衝池管理介面;
HeapBufMP元件為使用者提供的API介面如下:
- HeapBufMP建立/刪除:HeapBufMP_create();HeapBufMP_delete();
- HeapBufMP開啟/關閉:HeapBufMP_open();HeapBufMP_close();
- HeapBufMP引數初始化:HeapBufMP_Params_init();
- HeapBufMP分配/釋放記憶體:HeapBufMP_alloc();HeapBufMP_free();
- HeapBufMP獲取所有狀態:HeapBufMP_getExtendedStats();HeapBufMP_getStats();
3.5.2、HeapMultiBufMP
在SysLink包中並沒有找到相關API,但SysLink UserGuide中有提到.
HepMultiBufMP為使用者提供了可配置大小的緩衝池管理介面。
3.5.3、HeapMemMP
HeapMemMp為使用者提供了基於堆的可變大小的記憶體管理機制。
HeapMemMp元件為使用者提供的介面如下:
- HeapMemMP引數初始化:HeapMemMP_Params_init();
- HeapMemMP建立/刪除:HeapMemMP_create()/HeapMemMP_delete();
- HeapMemMP開啟/關閉:HeapMemMP_open()/HeapMemMP_close();
- HeapMemMP分配/釋放記憶體:HeapMemMP_alloc()/HeapMemMP_free();
- HeapMemMP獲取記憶體狀態:HeapMemMP_getExtendedStats()/HeamMemMP_getStats();
- HeapMemMP恢復記憶體初始狀態:HeapMemMP_restore();
3.6、FrameQ
FrameQ是專門為傳遞視訊幀而設計出來的元件。FrameQ的基本資料結構是可用於queue/dequeue資料的資料佇列,封裝了視訊幀快取指標、幀資料型別、幀寬、幀高、時間戳等資訊。 對於FrameQ模組具有以下特徵:- 支援多個讀者,但寫者唯一;
- 可以分配和釋放Frames;
- 可以對指向同一塊記憶體區多次分配和初始化成新的幀buffer;
- FrameQ允許有多個佇列,在多通道的運用中,視訊幀會根據通道號被分配到相應的幀佇列中;
FrameQ提供以下API介面:
- FrameQ元件初始化/銷燬:FrameQ_setup()/FrameQ_destroy();
- 建立/刪除FrameQ例項:FrameQ_create()/FrameQ_delete();
- 開啟/關閉FrameQ例項:FrameQ_open()/FrameQ_close();FrameQ_openByAddr();
- 為FrameQ例項分配/釋放記憶體:FrameQ_alloc()/FrameQ_free();FrameQ_allocv/FrameQ_freev();
- 插入/釋放FrameQ中幀:FrameQ_put()/FrameQ_get();FrameQ_putv()/FrameQ_getv();
- 複製給定的幀:FrameQ_dup();
- 註冊/登出FrameQ通知:FrameQ_registerNotifier()/FrameQ_unregisterNotifier();
- 強制傳送通知:FrameQ_sendNotify()
- 獲取FrameQ中有效幀數/已被釋放的幀數:FrameQ_getNumFrames()/FrameQ_getNumFreeFrames(); FrameQ_getvNumFrames()/FrameQ_getvNumFreeFrames()
- FrameQ控制:FrameQ_control()
- 獲取FrameQ的頭指標:FrameQ_getExtendedHeaderPtr();
- 獲取幀buffer/幀大小/幀數:FrameQ_getFrameBuffer()/FrameQ_getFrameBufSize()/FrameQ_getNumFrameBuffers();
- 獲取空資料幀大小/位置:FrameQ_getFrameBufValidSize()/FrameQ_getFrameBufDataStartOffset();
- 設定空資料幀大小/位置:FrameQ_setFrameBufValidSize()/FrameQ_setFrameBufDataStartOffset();
- 獲取FrameQ預設設定:FrameQ_getConfig();
3.7、RingIO
RingIO是基於資料流的環形緩衝buffer,而且針對於音視訊資料的特性做了優化。 RingIO支援一下特性:- 僅支援一個讀者和一個寫者;
- 讀寫相對獨立,可以在不同的程序或者處理器中同時進行讀寫操作;
- RingIO引數初始化:RingIO_Params_init();
- 建立/刪除RingIO物件:RingIO_create()/RingIO_delete();
- 開啟/關閉RingIO物件:RingIO_open()/RingIO_close();RingIO_openByAddr();
- 獲取共享記憶體請求:RingIO_sharedMemReq();
- 註冊/登出RingIO通知:RingIO_registerNotifier()/RingIO_unregisterNotifier();
- 強制傳送RingIO通知:RingIO_sendNotify();
- 獲取RingIO通知型別:RingIO_setNotifyType();
- 設定/獲取水印標誌/通知型別:RIngIO_setWaterMark()/RIngIO_getWaterMark()
- 獲取/釋放RingIO資料:RingIO_acquire()/RingIO_release();
- 設定/獲取RingIO屬性:RingIO_setvAttribute()/RingIO_getvAttribute();
- 設定/獲取RingIO固定大小的屬性:RingIO_setAttribute()/RingIO_getAttribute();
- 重新整理RingIO的buffer:RingIO_flush();
- 獲取有效/空buffer大小:RingIO_getValidSize()/RingIO_getEmptySize();
- 獲取有效/空屬性大小:RingIO_getValidAttrSize()/RingIO_getEmptyAttrSize();
- 獲取使用者需求buffer的大小/位置:RingIO_getAcquiredSize()/RingIO_getAcquiredOffset();
4、公共元件(基礎元件)
Utility Modules包括SharedRegion(IPC中屬於類庫ti.sdo.ipc.SharedRegion)、List、Trace、MultiProc、NameServer等,這些模組是上層元件實現的基礎。在IPC包中,該元件對應於類庫ti.sdo.utils.
4.1、SharedRegion(非常重要,SysLink模組最基礎的模組)
4.1.1、SharedRegion總覽
SharedRegion顧名思義,是共享記憶體區的意思。SharedRegion模組負責管理共享記憶體區。在一個有共享記憶體的多核架構中,普遍會遇到共享記憶體對映虛擬地址轉換問題,如下圖所示:
SharedRegion有兩種配置方式,即靜態配置方法(對於SYS/BIOS側可以通過cfg指令碼配置,而對於HLOS則當從處理器被載入的時候會通過讀取SYS/BIOS共享記憶體區配置資訊來獲取,請參考核心driver/dsp/syslink/notify_shmdriver/notify_shm_drv.c中實現)和動態配置方法(通過SharedRegion模組提供的API SharedRegion_setEntry()來設定,但值得注意的是這個API只是把入口資訊放入該處理器對應的共享記憶體查詢表中,而其他處理器也需要在自己的系統中使用該API來加入該入口)。實際配置中需要指明共享記憶體區在各個處理器中對映的虛擬地址及堆疊設定等,如