NOTICE: The Processors Wiki will End-of-Life in December of 2020. It is recommended to download any files or other content you may need that are hosted on processors.wiki.ti.com. The site is now set to read only.

IPC Users Guide/MultiProc Module

From Texas Instruments Wiki
Jump to: navigation, search


Table of Contents IPC User's Guide Previous; List Module NameServer Module Next


API Reference
Links
Book config.png
Book run.png

Many IPC modules require the ability to uniquely specify and identify processors in a multi-processor environment. The MultiProc module centralizes processor ID management into one module. Most multi-processor IPC applications require that you configure this module using the MultiProc.setConfig() function in the *.cfg script. The setConfig() function tells the MultiProc module:

  • The specific processor for which this application is being built.
  • The processors in this cluster. A "cluster" is a set of processors within a system that share some memory and for which notification between those processors is needed.

Most systems contain a single cluster. For systems with multiple clusters, you also need to configure the numProcessors and baseIdOfCluster properties.

Each processor reference by the MultiProc module can be uniquely identified by either its name string or an integer ranging from 0 to MultiProc.maxProcessors - 1.

The following XDCtools configuration statements set up a MultiProc array. At runtime, the "DSP" processor running this configuration gets assigned an ID value of 2. The other processors in the system are "VIDEO" with a processor ID of 0 and "DSS" with a processor ID of 1.

<syntaxhighlight lang='javascript'>/* DSP will get assigned processor id 2. */ var MultiProc = xdc.useModule('ti.sdo.utils.MultiProc'); MultiProc.setConfig("DSP", ["VIDEO", "DSS", "DSP"]); </syntaxhighlight>The ID is a software-only setting. It does not correlate to hardware core IDs or any other type of hardware identification. For devices with more than one core, each core must have its own unique processor ID. The ID is also independent of any OS setting.

The processor ID is not always known at configuration time. It might need to be determined at initialization time via a GPIO pin, flash setting, or some other method. You can call the MultiProc_setLocalId() API (with the restriction that it must be called before module startup) to set the processor ID. However, other modules that use MultiProc need to know that the static ID will be changed during initialization. Setting the local name to NULL in the MultiProc.setConfig statement in the configuration indicates that the MultiProc_setLocalId() API will be used at runtime. Other modules that use MultiProc should act accordingly by deferring processing until the actual ID is known.

For example, the following fragment of configuration code requires that the MultiProc_setLocalId() API be run during startup to fill in the NULL processor name.

<syntaxhighlight lang='javascript'>/* Specify startup function */ var Startup = xdc.useModule('xdc.runtime.Startup'); Startup.firstFxns.$add('&setMyId');

/* Specify MultiProc config; current processor unknown */ var MultiProc = xdc.useModule('ti.sdo.utils.MultiProc'); MultiProc.setConfig(null, ["CORE0", "CORE1", "CORE2"]); </syntaxhighlight>Then, the application code could contain the following setMyID() function to be run at startup:

<syntaxhighlight lang='c'>Void setMyId() {

   UInt16 procId;
   Int    status;
   //
   // Board specific determination of processor id.
   // Example: GPIO_READ reads register of GPIO pin 5
   //
   if (GPIO_READ(5) == 0) {
       procId = 0;
   }
   else {
       procId = 1;
   }
   MultiProc_setLocalId(procId);

} </syntaxhighlight>Your application can query the MultiProc table using various runtime APIs.

At runtime, the MultiProc_getId() call returns the MultiProc ID for any processor name. At config-time, the MultiProc.getIdMeta() call returns the same value. For example:

<syntaxhighlight lang='c'>core1ProcId = MultiProc_getId("CORE1"); </syntaxhighlight>MultiProc_self() returns the processor ID of the processor running the API. For example:

<syntaxhighlight lang='c'>System_printf("My MultiProc id = %d\n", MultiProc_self()); </syntaxhighlight>MultiProc_getBaseIdOfCluster() returns the MultiProc ID of the base processor in the cluster to which this processor belongs.

The MultiProc_getName() API returns that processor name if given the MultiProc ID. For example:

<syntaxhighlight lang='c'>core0Name = MultiProc_getName(0); </syntaxhighlight>MultiProc_getNumProcessors() evaluates to the total number of processors.

<syntaxhighlight lang='c'>System_printf("Number of processors in the system = %d\n",

             MultiProc_getNumProcessors() );

</syntaxhighlight>MultiProc_getNumProcsInCluster() returns the number of processors in the cluster to which this processor belongs.

Configuring Clusters With the MultiProc Module

A "cluster" is a set of processors within a system that share some memory and for which notification between those processors is needed. If your system has multiple clusters, you need to configure the MultiProc module's numProcessors and baseIdOfCluster properties in addition to calling the MultiProc.setConfig() function.

Notifications are not supported between different clusters.

For example, in a system with two 'C6678 devices that each use eight homogeneous cores, you could configure the first 'C6678 device as follows:

<syntaxhighlight lang='javascript'>var MultiProc = xdc.useModule('ti.sdo.utils.MultiProc'); MultiProc.baseIdOfCluster = 0; MultiProc.numProcessors = 16; MultiProc.setConfig(null, ["CORE0", "CORE1", "CORE2",

        "CORE3", "CORE4", "CORE5", "CORE6", "CORE7"]);

</syntaxhighlight>You could configure the second 'C6678 device as follows:

<syntaxhighlight lang='javascript'>var MultiProc = xdc.useModule('ti.sdo.utils.MultiProc'); MultiProc.baseIdOfCluster = 8; MultiProc.numProcessors = 16; MultiProc.setConfig(null, ["CORE0", "CORE1", "CORE2",

        "CORE3", "CORE4", "CORE5", "CORE6", "CORE7"]);

</syntaxhighlight>Notice that the MultiProc.numProcessors property specifies the total number of processors in the system, while the length of the array passed to setConfig() specifies the number of processors in the cluster. (If you are not using multiple clusters, the numProcessors property is configured automatically.)

The MultiProc.baseIdOfCluster property is set to the MultiProc ID number you want to use for the first processor in the array for this cluster. For example, if there are 8 processors in a cluster, the baseIdOfCluster property should be 0 for the first cluster and 8 for the second cluster.

Book config.png

The latest version of the MultiProc module configuration documentation is available online.

Creating Connections with MultiProc

The Ipc_start() and Ipc_attach() APIs can only be used to attach and synchronizes with processors in the same cluster.

To create a connection between cores in different clusters, you must manually create a connection using the MessageQ and ti.sdo.ipc.NameServerMessageQ modules. The NameServerMessageQ module supports NameServer requests between different clusters by using MessageQ, which in turns uses the MessageQ transport to send a NameServer request.

You can control the timeout period for the NameServerMessageQ module by configuring its timeoutInMicroSecs parameter, which defaults to 1 second. If a response is not received within the timeout period, the NameServer request returns a failure status. The NameServerRemoteNotify module also has a timeoutInMicroSecs parameter that you can configure; it defaults to wait forever.

Creating a connection between cores in different clusters allows you to call MessageQ_open() even for a core on a different cluster. Note that these calls must occur after the MessageQ heap has been registered, because they allocate memory from the heap.

Once the connection has been created, MessageQ can be used between different processors on different clusters just as it is used between different processors in the same cluster.

Book run.png

The latest version of the MultiProc module run-time API documentation is available online.

Example

The following example function creates a NameServerMessageQ and TransportXXX to communicate remotely with a processor in a different cluster. The "remoteProcId" would be specified to be the MultiProc ID of the processor in the system. "TransportXXX" must be a copy-based transport that does not require any shared memory. You would need to create such a transport, because IPC does not provide one.

<syntaxhighlight lang='c'>Void myRemoteCreateFunction(Uint16 remoteProcId) {

   NameServerMessageQ_Params  nsParams;
   NameServerMessageQ_Handle  nsHandle;
   TransportXXX_Handle        tranHandle;
   TransportXXX_Params        tranParams;
   Error_Block eb;
   Error_init(&eb);
   /*
    *  Note: You must register a MessageQ heap prior to
    *  calling NameServerMessageQ_create().
    */
   /* init nsParams */
   NameServerMessageQ_Params_init(&nsParams);
   /* create driver to remote processor */
   nsHandle = NameServerMessageQ_create(
       remoteProcId, /* MultiProc ID of proc on 2nd cluster */
       &nsParams,
       &eb);
   if (nsHandle == NULL) {
       SYS_abort("NameServerMessageQ_create() failed");
   }
   /* initialize the transport parameters */
   TransportXXX_Params_init(&tranParams);
   tranHandle = TransportXXX_create(
      remoteProcId, /* MultiProc ID of proc on 2nd cluster */
      &tranParams,
      &eb);
   if (tranHandle == NULL) {
       SYS_abort("TransportXXX_create() failed");
   }

} </syntaxhighlight>


Table of Contents IPC User's Guide Previous; List Module NameServer Module Next