IPC的使用


IPC,Inter-Processor Communication是SYS/BIOS處理核間通信的組件:

IPC的幾種應用方式:

1.最小使用(Minimal use)

這種情況是通過核間的通知機制(notification)來實施的,而一個通知所攜帶的信息是非常小的(一般是32bits),所以稱為最小使用。這種方式一般是用於處理核間的簡單同步,卻無法處理復雜的消息傳遞。

這種情況下,需要利用到Notify模塊的APIs函數,比如通過Notify_sendEvent()函數給某個特定核傳遞一個事件,我們可以給特定事件動態注冊反饋函數。由於一個通知(notification)所攜帶的信息是極少的,所以只能給處理器發送一個事件號,而這個事件號所代表的反饋函數決定之后的動作。另外一些數據以函數參數方式,也可以被送出。

2.增加數據通路(Add data passing)

這種情況是在前面的最小使用機制下,在核間增加了一個傳遞鏈表元素的數據通路,這個鏈表的實施一般是使用共享內容並通過門(gates)來管理同步。 

這種情況是在最小使用上,增加了一個ListMP模塊用於共享鏈表元素。  
ListMP模塊是一個雙向鏈表,另外ListMP需要用到共離內存,所以SharedRegion模塊也需要被使用,另外ListMP通過NameServer模塊中來管理名稱/值,同時使用GateMP模塊來防止鏈表元素被多個處理器同時讀取。

3.增加動態分配(Add dynamic allocation)

這種情況下,增加了從堆中動態分配鏈表元素的功能。

這種情況在上種情況下,增加了一個Heap*MP模塊,這個模塊主要用於從堆中給鏈表動態分配內存。

 

4.強大但易用的消息機制(Powerful but easy-to-use messaging)

這種情況下利用MessageQ模塊來傳遞消息。 

除了Notify通知機制,還可以利用MessageQ來實現更為復雜的核間通信,在這種情況下,只需要配置MultiProc和SharedRegion模塊就可以了,而Ipc_start()函數將自動為我們實現上面灰色模塊的配置。

最小使用(Minimal use)情況舉例

打開CCS自帶例程

點擊編譯后,查看有無出錯;導入目標配置文件.ccxml,這里選擇的仍然是C6678 Device Functional Simulator, Little Endian:

選中Group,點擊運行:

  結果分析

1.各核打印:

 這段是在main()中出現的結果,每個核都會執行各自的main():

 System_printf("main: MultiProc id = %d\n", MultiProc_self()); System_printf("main: MultiProc name = %s\n", MultiProc_getName(MultiProc_self()));

 2.各核注冊事件,並表明其反饋函數:

 status = Notify_registerEvent(srcProc, INTERRUPT_LINE, EVENTID, (Notify_FnNotifyCbck)cbFxn, NULL);

核0執行同時釋放信號量,在核0釋放信號量semHandle之前,其他核都處理等待信號量釋放中

 

核0通過給核1發送事件,觸發反饋函數,在反饋函數中semHandle歸一,注意這個激活的信號量是在核1中的

 status = Notify_sendEvent(dstProc, INTERRUPT_LINE, EVENTID, seq, TRUE);

 激活核1的信號量后,核0打印結果,並等待其信號量的結果,所有核的信號量都初始為0:

 System_printf("tsk1_func: Sent request #%d to %s\n", seq, MultiProc_getName(dstProc));
 /* wait forever on a semaphore, semaphore is posted in callback */ Semaphore_pend(semHandle, BIOS_WAIT_FOREVER);

以下是總共八個核,分別執行了NUMLOOPS次(這里設置的是10次)

 下一個核信號被激活,開始執行:

  /* wait forever on a semaphore, semaphore is posted in callback */ Semaphore_pend(semHandle, BIOS_WAIT_FOREVER); System_printf("tsk1_func: Received request #%d from %s\n", seq, MultiProc_getName(recvProcId));

同時通過反饋函數將當前核的下一個核激活:

 status = Notify_sendEvent(dstProc, INTERRUPT_LINE, EVENTID, seq, TRUE);

完成發送事件:

 System_printf("tsk1_func: Sent request #%d to %s\n", seq, MultiProc_getName(dstProc));

退出任務循環,同時退出當前核的BIOS:

多核IPC的配置

1.IPC的啟動非常簡單,在導入Ipc頭文件后,在main()函數中調用Ipc_start()就能根據.cfg文件中配置啟動IPC所需要的模塊,比如默認情況下Ipc_start()會調用Notify_start()之類,不過要啟動這些模塊,需要保證提前在.cfg中添加了這些模塊。
2.IPC的配置是在.cfg中完成的,配置IPC首先需要申明,當前Notify等相關模塊也需要提前申明,這里如果不清楚IPC所需要的相關模塊,最好使用自帶IPC例程作為模塊。
 var Ipc = xdc.useModule('ti.sdo.ipc.Ipc');

3.設置同步的核數

       Ipc.procSync = Ipc.ProcSync_ALL;
       這里Ipc.ProcSync_ALL 表示Ipc_start會自動將所有的核都啟動了
       Ipc.ProcSync_PAIR 表示只啟動部分核,需要啟動的核要通過Ipc_attach()來啟動,這個默認選項
       Ipc.ProcSync_NONE 表示Ipc_start()不會同步任何核

4.核間的連接方法Ipc_attach()及Ipc_detach()

       這兩個函數的使用,需要.cfg文件中配置了Ipc.ProcSync_PAIR 
       Ipc_attach的使用方法非常簡單,在Ipc_start()之后直接輸入:
  Ipc_attach(#coreID),#coreID表示需要連接的核ID號,如Ipc_attach(0)表示連接核0。

   不過需要注意的是:

       a) 核的連接一定要按照ID號從小到大的順序進行,比如當前核必須先連接了核0,才能連接核1,之后才能連接核2
       b) 另外兩核之間的相互連接必須先滿足ID號小的先連接ID號大的,比如只有當核0連接核1后,核1才能連接到核0
       c) 由於核的連接並不是一次就能成功的,所以一般需要加一個循環等待的過程,一般使用方法如下:
       
 
 while(Ipc_attach(#coreID)<0) { Task_sleep(1); }

       Ipc_detach()的使用方法同Ipc_attach()是類似的,不過它的功能是解除連接。

主從核之間的通信

前面介紹IPC核間通信例子,是每個核同所有核之間都有連接,而各核之間連接都是相同且雙向,而在很多情況下,我們並不需要如此多的核,或者許多核間連接也是不必要的,這些情況下使用Ipc.ProcSync_ALL未免太不高效。 
下面我們介紹的例子是核間的主從通信,選擇三個核,選擇一個主核,另外兩個是副核,主核core0同副核之間有相互連接,而副核core1與副核core2之間沒有連接,這個主從通信主要完成以下事件:
主核向兩從核發送事件,激活從核,使其執行任務。
兩核完成任務后,向主核發送事件,主核繼續執行其任務。
1.在.cfg文件中設置procSync
      改為Ipc.procSync = Ipc.ProcSync_PAIR;

2.修改源文件為:

#include <xdc/std.h>

/* -----------------------------------XDC.RUNTIME module Headers */ #include <xdc/runtime/System.h>

/* ----------------------------------- IPC module Headers */ #include <ti/ipc/MultiProc.h> #include <ti/ipc/Notify.h> #include <ti/ipc/Ipc.h>
/* ----------------------------------- BIOS6 module Headers */ #include <ti/sysbios/knl/Semaphore.h> #include <ti/sysbios/knl/Task.h> #include <ti/sysbios/BIOS.h>

/* ----------------------------------- To get globals from .cfg Header */ #include <xdc/cfg/global.h>

#define INTERRUPT_LINE  0

/* Notify event number that the app uses */
#define EVENTID         10

/* Number of times to run the loop */
#define NUMLOOPS        3 UInt32 times = 0; UInt16 recvnumes = 0; #define masterProc 0
#define sloverProc1 1
#define sloverProc2 2
#define sloverNum 2

/* * ======== cbFxn ======== * This function was registered with Notify. It is called when any event is * sent to this processor. */ Void cbFxn(UInt16 procId, UInt16 lineId, UInt32 eventId, UArg arg, UInt32 payload) { /* The payload is a sequence number. */

    if(procId!=masterProc) // 主核注冊函數
 { recvnumes++;  // 接收從核的數目
        if(recvnumes==sloverNum) // 當收到全部從核回復的信息
 { recvnumes=0; Semaphore_post(semHandle); } } else { times = payload; // 執行次數
 Semaphore_post(semHandle); } } /* * ======== tsk0_func ======== * Sends an event to the next processor then pends on a semaphore. * The semaphore is posted by the callback function. */ Void tsk0_func(UArg arg0, UArg arg1) { Int i = 1; Int status; if (MultiProc_self() == masterProc) { while (i <= NUMLOOPS) { /* 這里可以添加主核需要執行的任務代碼*/

            /* Send an event to the next processor */ status = Notify_sendEvent(sloverProc1, INTERRUPT_LINE, EVENTID, i, TRUE); status = Notify_sendEvent(sloverProc2, INTERRUPT_LINE, EVENTID, i, TRUE); /* Continue until remote side is up */
            if (status < 0) { continue; } System_printf("MasterCore Sent Event to SloverCores in %d\n", i); /* Wait to be released by the cbFxn posting the semaphore */ Semaphore_pend(semHandle, BIOS_WAIT_FOREVER); // 主核等待所有從核完成其工作返回
 System_printf("MasterCore Received Event from All SloverCores in %d\n",i); /* increment for next iteration */ i++; } } else { while (times < NUMLOOPS) { /* wait forever on a semaphore, semaphore is posted in callback */ Semaphore_pend(semHandle, BIOS_WAIT_FOREVER); // 等待主核通知開始執行任務
 System_printf("SloverCore%d Received Event from MasterCore in %d\n", MultiProc_self(),times); /* 這里可以添加從核執行的任務*/

            /* Send an event to the next processor */ status = Notify_sendEvent(masterProc, INTERRUPT_LINE, EVENTID, times, TRUE); if (status < 0) { System_abort("sendEvent to MasterCore failed\n"); } System_printf("SloverCore%d sent Event from MasterCore in %d\n", MultiProc_self(),times); } } System_printf("Test completed\n"); BIOS_exit(0); } /* * ======== main ======== * Synchronizes all processors (in Ipc_start), calls BIOS_start, and registers * for an incoming event */ Int main(Int argc, Char* argv[]) { Int status; status = Ipc_start(); if (status < 0) { System_abort("Ipc_start failed\n"); } /* 這里主要根據主核和從核的角色分別添加連接任務:主核同兩個從核都有連接,而從核只與主核有鏈接 在添加核間連接后,分別給核間連接注冊事件 */
    if(MultiProc_self()==masterProc) { while(Ipc_attach(sloverProc1)){ Task_sleep(1); }// 完成從核1的連接
        while(Ipc_attach(sloverProc2)){ Task_sleep(1); }// 完成從核2的連接
 status = Notify_registerEvent(sloverProc1, INTERRUPT_LINE, EVENTID, (Notify_FnNotifyCbck)cbFxn, NULL); if (status < 0) { System_abort("Notify_registerEvent for sloverCore1 failed\n"); }// 完成從核1的事件注冊
 status = Notify_registerEvent(sloverProc2, INTERRUPT_LINE, EVENTID, (Notify_FnNotifyCbck)cbFxn, NULL); if (status < 0) { System_abort("Notify_registerEvent for sloverCore2 failed \n"); }// 完成從核2的事件注冊
 } else{ while(Ipc_attach(masterProc)) { Task_sleep(1); }// 完成主核0的連接
 status = Notify_registerEvent(masterProc, INTERRUPT_LINE, EVENTID, (Notify_FnNotifyCbck)cbFxn, NULL); if (status < 0) { System_abort("Notify_registerEvent for masterCore0 failed\n"); }// 完成主核0的事件注冊
 } BIOS_start(); return (0); }

仿真調試的結果:

從結果上看,當從核分別收到了來自主核的事件時,同時開始任務,當從核任務全部完成后,主核才開始其任務。  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM