4.1. TI-RTOS Kernel

4.2. XDAIS

4.3. FC

4.4. IPC

Note

This section mainly provides details of the software distributed part of the IPC 3.x package (installed under ipc_<version>). The IPC package provides the IPC APIs with higher level software abstraction. In addition,starting from Processor SDK 6.1 release, a lower level IPC driver (IPC LLD) using rpmsg based transport is included specifically for AM6x/J7 platforms. See the following link for additional details IPC 3.x vs IPC LLD

Note

Starting with releases after the 6.3 release, IPC3.x will be deprecated on the AM65x device and IPC LLD is the only IPC stack supported. Please stop new development on top of IPC3.x for AM65x, and migrate to IPC LLD stack. Please see IPC LLD for AM65x/J721E for information on IPC LLD.

4.4.1. IPC User’s Guide

Inter-Processor Communication (IPC) provides a processor-agnostic API which can be used for communication between processors in a multi-processor environment (inter-core), communication to other threads on same processor (inter-process), and communication to peripherals (inter-device). The API supports message passing, streams, and linked lists. IPC can be used to communicate with the following:

  • Other threads on the same processor

  • Threads on other processors running SYS/BIOS

  • Threads on other processors running an HLOS (e.g., Linux, QNX, Android)

../_images/IPC_comm_features.JPG

4.4.2. Overview

This user’s guide contains the topics in the following list. It also links to API reference documentation for static configuration (ipcCfg_Img) and run-time C processing (ipcRun_Img) for each module.

Getting Started

Application Development

IPC Internal/API & Other Useful Links

FAQ


Note

Please see the release notes in your IPC installation before starting to use IPC. The release notes contain important information about feature support, issues, and compatibility information for a particular release.


4.4.3. Use Cases for IPC

You can use IPC modules in a variety of combinations. From the simplest setup to the setup with the most functionality, the use case options are as follows. A number of variations of these cases are also possible:

  • Minimal use of IPC. (BIOS-to-BIOS only) This scenario performs inter-processor notification. The amount of data passed with a notification is minimal–typically on the order of 32 bits. This scenario is best used for simple synchronization between processors without the overhead and complexity of message-passing infrastructure.

  • Add data passing. (BIOS-to-BIOS only) This scenario adds the ability to pass linked list elements between processors to the previous minimal scenario. The linked list implementation may optionally use shared memory and/or gates to manage synchronization.

  • Add dynamic allocation. (BIOS-to-BIOS only) This scenario adds the ability to dynamically allocate linked list elements from a heap.

  • Powerful but easy-to-use messaging. (HLOS and BIOS) This scenario uses the MessageQ module for messaging. The application configures other modules. However, the APIs for other modules are then used internally by MessageQ, rather than directly by the application.

In the following sections, figures show modules used by each scenario.

  • Blue boxes, identify modules for which your application will call C API functions other than those used to dynamically create objects.

  • Red boxes, identify modules that require only configuration by your application. Static configuration is performed in an XDCtools configuration script (.cfg). Dynamic configuration is performed in C code.

  • Grey boxes, identify modules that are used internally but do not need to be configured or have their APIs called.

4.4.3.1. Minimal Use Scenario (BIOS-to-BIOS only)

This scenario performs inter-processor notification using a Notify driver, which is used by the Notify module. This scenario is best used for simple synchronization in which you want to send a message to another processor to tell it to perform some action and optionally have it notify the first processor when it is finished.

../_images/IpcUG_over_1_2_1.png

In this scenario, you make API calls to the Notify module. For example, the Notify_sendEvent() function sends an event to the specified processor. You can dynamically register callback functions with the Notify module to handle such events.

You must statically configure MultiProc module properties, which are used by the Notify module.

The amount of data passed with a notification is minimal. You can send an event number, which is typically used by the callback function to determine what action it needs to perform. Optionally, a small “payload”? of data can also be sent.

See Notify Module and MultiProc Module.

4.4.3.2. Data Passing Scenario (BIOS-to-BIOS only)

In addition to the IPC modules used in the previous scenario, you can use the ListMP module to share a linked list between processors.

../_images/IpcUG_over_1_2_2.png

In this scenario, you make API calls to the Notify and ListMP modules.

The ListMP module is a doubly-linked-list designed to be shared by multiple processors. ListMP differs from a conventional “local”? linked list in the following ways:

  • Address translation is performed internally upon pointers contained within the data structure.

  • Cache coherency is maintained when the cacheable shared memory is used.

  • A multi-processor gate (GateMP) is used to protect read/write accesses to the list by two or more processors.

ListMP uses SharedRegion’s lookup table to manage access to shared memory, so configuration of the SharedRegion module is required.

Internally, ListMP can optionally use the NameServer module to manage name/value pairs. The ListMP module also uses a GateMP object, which your application must configure. The GateMP is used internally to synchronize access to the list elements.

See ListMP Module, GateMP Module, SharedRegion Module, and NameServer Module.

4.4.3.3. Dynamic Allocation Scenario (BIOS-to-BIOS only)

To the previous scenario, you can add dynamic allocation of ListMP elements using one of the Heap*MP modules.

../_images/IpcUG_over_1_2_3.png

In this scenario, you make API calls to the Notify and ListMP modules and a Heap*MP module.

In addition to the modules that you configured for the previous scenario, the Heap*MP modules use a GateMP that you must configure. You may use the same GateMP instance used by ListMP.

See Heap*MP Modules and GateMP Module.

4.4.3.4. Powerful But Easy-to-Use Messaging with MessageQ (HLOS and BIOS)

Finally, to use the most sophisticated inter-processor communication scenario supported by IPC, you can add the MessageQ module. Note that the following diagram shows one particular transport (TransportShm) and may not apply to all devices and/or environments.

../_images/IpcUG_over_1_2_4.png

In this scenario, you make API calls to the MessageQ module for inter-processor communication.

API calls made to the Notify, ListMP, and Heap*MP modules in the previous scenarios are not needed. Instead, your application only needs to configure the MultiProc and (if the underlying MessageQ transport requires it) the SharedRegion modules.

Note

Some MessageQ transports do not use SharedRegion, but instead copy the message payloads. This may be done because of hardware limitations (e.g. no shared memory is available) or software design (e.g. transports built on Linux kernel-friendly rpmsg drivers).

The Ipc_start() API call configures all the necessary underlying modules (e.g. Notify, HeapMemMP, ListMP, TransportShm, NameServer, and GateMP). The actual details of what modules Ipc_start() initializes varies from environment to environment.

It is possible to use MessageQ in a single-processor SYS/BIOS application. In such a case, only API calls to MessageQ and configuration of any xdc.runtime.IHeap implementation are needed.

4.4.4. IPC Examples

4.4.4.1. Building IPC Examples

The IPC BIOS examples are located in the Processor SDK RTOS IPC directory within the examples folder. Please refer to the readme of each example for details on each example.

The examples are makefile based and can be built using the top-level makefile provided in the Processor SDK RTOS folder. The following commands will build all of the IPC examples.

Windows

cd (Processor SDK RTOS folder)
setupenv.bat
gmake ipc_examples

Linux

cd (Processor SDK RTOS folder)
source ./setupenv.sh
make ipc_examples

4.4.4.2. IPC Examples: Details

This section explains some of the common details about IPC examples. The sub-directories under the examples are organised into the code for each of the cores in the SOC. For example

  • Host

  • DSP1

  • DSP2

  • IPU1

  • IPU2

Typically we have a host core which is the main core in the SOC and other slave cores. The directory name of the slave cores have a base name (like DSP, IPU etc) which indicates the type of core and a core number. Depending on the example, the Host can run TI BIOS or Linux or QNX and the slave cores in general run TI BIOS only. So the specific build related files need to be interpreted accordingly.

4.4.4.2.1. BIOS Application Configuration Files

The cores running BIOS in general has a config file which brings in all the modules needed to complete the application running on the specific core. This section explains the details of the entries in the config file. Note: The details here are just representative of a typical configuration. In general the configuration is customized based on the particular example.

4.4.4.2.1.1. BIOS Configuration

The following configuration are related to configuring BIOS

var BIOS        = xdc.useModule('ti.sysbios.BIOS');
/*  This adds ipc Startup to be done part of BIOS  startup before main*/
BIOS.addUserStartupFunction('&IpcMgr_ipcStartup');

/* The following configures Debug libtype with Debug build */
if (Program.build.profile == "debug") {
     BIOS.libType = BIOS.LibType_Debug;
} else {
    BIOS.libType = BIOS.LibType_Custom;
}

var Sem = xdc.useModule('ti.sysbios.knl.Semaphore');
var instSem0_Params = new Sem.Params();
instSem0_Params.mode = Sem.Mode_BINARY;
Program.global.runOmpSem = Sem.create(0, instSem0_Params);
Program.global.runOmpSem_complete = Sem.create(0, instSem0_Params);

var Task = xdc.useModule('ti.sysbios.knl.Task');
Task.common$.namedInstance = true;

/* default memory heap */
var Memory = xdc.useModule('xdc.runtime.Memory');
var HeapMem = xdc.useModule('ti.sysbios.heaps.HeapMem');
var heapMemParams = new HeapMem.Params();
heapMemParams.size = 0x8000;
Memory.defaultHeapInstance = HeapMem.create(heapMemParams);

/* create a heap for MessageQ messages */
var HeapBuf = xdc.useModule('ti.sysbios.heaps.HeapBuf');
var params = new HeapBuf.Params;
params.align = 8;
params.blockSize = 512;
params.numBlocks = 20;
4.4.4.2.1.2. XDC Runtime

The following configuration are in general used by an IPC application in BIOS

/* application uses the following modules and packages */
xdc.useModule('xdc.runtime.Assert');
xdc.useModule('xdc.runtime.Diags');
xdc.useModule('xdc.runtime.Error');
xdc.useModule('xdc.runtime.Log');
xdc.useModule('xdc.runtime.Registry');

xdc.useModule('ti.sysbios.knl.Semaphore');
xdc.useModule('ti.sysbios.knl.Task');
4.4.4.2.1.3. IPC Configuration

The following IPC modules are used in a typical IPC application.

xdc.useModule('ti.sdo.ipc.Ipc');
xdc.useModule('ti.ipc.ipcmgr.IpcMgr');
var MultiProc = xdc.useModule('ti.sdo.utils.MultiProc');
/* The following configures the PROC List */
MultiProc.setConfig("CORE0", ["HOST", "CORE0"]);

var msgHeap = HeapBuf.create(params);

var MessageQ  = xdc.useModule('ti.sdo.ipc.MessageQ');
/* Register msgHeap with messageQ */
MessageQ.registerHeapMeta(msgHeap, 0);

The following lines configure placement of Resource table in memory. Note that some platforms or applications the placement of memory can be in a different section in the memory map.

/* Enable Memory Translation module that operates on the Resource Table */
var Resource = xdc.useModule('ti.ipc.remoteproc.Resource');
Resource.loadSegment = Program.platform.dataMemory;
4.4.4.2.1.4. Transport Configuration

Typically the transport to be used by IPC is specified here. The following snippet configures RPMsg based transport.

/* Setup MessageQ transport */
var VirtioSetup = xdc.useModule('ti.ipc.transports.TransportRpmsgSetup');
MessageQ.SetupTransportProxy = VirtioSetup;
4.4.4.2.1.5. NameServer Configuration

The Name server to be used is specified here.

/* Setup NameServer remote proxy */
var NameServer = xdc.useModule("ti.sdo.utils.NameServer");
var NsRemote = xdc.useModule("ti.ipc.namesrv.NameServerRemoteRpmsg");
NameServer.SetupProxy = NsRemote;
4.4.4.2.1.6. Instrumentation Configuration

The following configuration are required for system logging and diagnostics.

  /* system logger */
var LoggerSys = xdc.useModule('xdc.runtime.LoggerSys');
var LoggerSysParams = new LoggerSys.Params();
var Defaults = xdc.useModule('xdc.runtime.Defaults');
Defaults.common$.logger = LoggerSys.create(LoggerSysParams);

/* enable runtime Diags_setMask() for non-XDC spec'd modules */
var Diags = xdc.useModule('xdc.runtime.Diags');
Diags.setMaskEnabled = true;

/* override diags mask for selected modules */
xdc.useModule('xdc.runtime.Main');
Diags.setMaskMeta("xdc.runtime.Main",
    Diags.ENTRY | Diags.EXIT | Diags.INFO, Diags.RUNTIME_ON);

var Registry = xdc.useModule('xdc.runtime.Registry');
Registry.common$.diags_ENTRY = Diags.RUNTIME_OFF;
Registry.common$.diags_EXIT  = Diags.RUNTIME_OFF;
Registry.common$.diags_INFO  = Diags.RUNTIME_OFF;
Registry.common$.diags_USER1 = Diags.RUNTIME_OFF;
Registry.common$.diags_LIFECYCLE = Diags.RUNTIME_OFF;
Registry.common$.diags_STATUS = Diags.RUNTIME_OFF;

var Main = xdc.useModule('xdc.runtime.Main');
Main.common$.diags_ASSERT = Diags.ALWAYS_ON;
Main.common$.diags_INTERNAL = Diags.ALWAYS_ON;
4.4.4.2.1.7. Other Optional Configurations

In addition to the above configurations there are other platform specific configurations may be used to enable certain features.

For example the following sections shows the sections used to enable device exception handler. ( But the deh module may not be available on all devices)

var Idle = xdc.useModule('ti.sysbios.knl.Idle');
var Deh = xdc.useModule('ti.deh.Deh');

/* Must be placed before pwr mgmt */
Idle.addFunc('&ti_deh_Deh_idleBegin');

4.4.4.3. Building IPC examples using products.mak

The following sections discuss how to individually build the IPC examples by modifying the product.mak file.

The IPC product contains an examples/archive directory with device-specific examples. Once identifying your device, the examples can be unzipped anywhere on your build host. Typically once unzipped, the user edits the example’s individual products.mak file and simply invokes make.

Note

A common place to unzip the examples is into the IPC_INSTALL_DIR/examples/ directory. Each example’s products.mak file is smart enough to look up two directories (in this case, into IPC_INSTALL_DIR) for a master products.mak file, and if found it uses those variables. This technique enables users to set the dependency variables in one place, namely IPC_INSTALL_DIR/products.mak.

Each example contains a readme.txt with example-specific details.

4.4.4.3.1. Generating Examples

The IPC product will come with the generated examples directory. The IPC product is what is typically delivered with SDKs such as Processor SDK. However, some SDKs point directly to the IPC git tree for the IPC source. In this case, the IPC Examples can be generated separately.

4.4.4.3.2. Tools

The following tools need to be installed:
  • XDC tools (check the IPC release notes for compatible version)

4.4.4.3.3. Source Code

mkdir ipc
cd ipc
git clone git://git.ti.com/ipc/ipc-metadata.git
git clone git://git.ti.com/ipc/ipc-examples.git

Then checkout the IPC release tag that is associated with the IPC version being used. Do this for both repos. For example:

git checkout 3.42.01.03

4.4.4.3.4. Build

cd ipc-examples/src
make .examples XDC_INSTALL_DIR=<path_to_xdc_tools> IPCTOOLS=<path_to_ipc-metadata>/src/etc

For example:

make .examples XDC_INSTALL_DIR=/opt/ti/xdctools_3_32_00_06_core IPCTOOLS=/home/user/ipc/ipc-metadata/src/etc

The “examples” director will be generated in the path “ipc-examples/src/”:

ipc-examples/src/examples

4.4.5. IPC Training

Introduction to Inter-Processor Communication (IPC) for KeyStone and Sitara™ Devices - The IPC software package is designed to hide the lower-layer hardware complexity of multi-core devices and help users to quickly develop applications for data transfer between cores or devices. IPC also maximizes application software reuse by providing a common API interface across all supported platforms, including AM65x, AM57x, 66AK2Gx, 66AK2Ex, 66AK2Hx, TCI663x, TDA3XX, OMAP-L138, OMAP54XX, DRA7XX, and more. This training video introduces the IPC features and modules, shows how to build the IPC libraries for your platform, examines the RPMsg framework, and provides a look at the included examples and benchmarks.


Building and Running Inter-Processor Communication (IPC) Examples on the AM572x GP EVM - For TI embedded processors with heterogeneous multi-core architectures – like the AM57x family of devices, which have integrated ARM, DSP, and M4 cores – the TI Processor Software Development Kit (SDK) provides a software component called Inter-processor Communication (IPC). This video demonstrates how to build and run IPC examples on the AM572x General Purpose Evaluation Module (GP EVM) using Processor SDK for Linux.


IPC Training v2.21 - IPC 3.x Full Training Material (PowerPoint) (PowerPointShow) (PDF)


IPC Lab 1 - Hello (PowerPoint) (PowerPointShow) (PDF)

IPC Lab 2 - MessageQ (PowerPoint) (PowerPointShow) (PDF)

IPC Lab 3 - Scalability (PowerPoint) (PowerPointShow) (PDF)

4.4.6. TI SDO IPC Package

4.4.6.1. Introduction

This page introduces the modules in the ti.sdo.ipc package.

Note

This package is not used on HLOS-based cores. Although this is a BIOS-only package, note that the BIOS-side of a HLOS<->BIOS IPC-using application will need to bring in a subset of these packages into the BIOS-side configuration scripts.

The ti.sdo.ipc package contains the following modules that you may use in your applications:

Module

Module Path

Description

GateMP Module

GateMP

Manages gates for mutual exclusion of shared resources by multiple processors and threads. See GateMP Module

HeapBufMP Module

ti.sdo.ipc.heaps.HeapBufMP

Fixed-sized shared memory Heaps. Similar to SYS/BIOS’s ti.sysbios.heaps.HeapBuf module, but with some configuration differences. See HeapMP Module

HeapMemMP Module

ti.sdo.ipc.heaps.HeapMemMP

Variable-sized shared memory Heaps. See HeapMP Module

HeapMultiBufMP Module

ti.sdo.ipc.heaps.HeapMultiBufMP

Multiple fixed-sized shared memory Heaps. See HeapMP Module

Ipc Module

ti.sdo.ipc.Ipc

Provides Ipc_start() function and allows startup sequence configuration. See IPC Module

ListMP Module

ti.sdo.ipc.ListMP

Doubly-linked list for shared-memory, multi-processor applications. Very similar to the ti.sdo.utils.List module. See ListMP Module

MessageQ Module

ti.sdo.ipc.MessageQ

Variable size messaging module. See MessageQ Module

TransportShm

ti.sdo.ipc.transports.TransportShm

Transport used by MessageQ for remote communication with other processors via shared memory. See MessageQ Module

Notify Module

ti.sdo.ipc.Notify

Low-level interrupt mux/demuxer module. See Notify Module

NotifyDriverShm

ti.sdo.ipc.notifyDrivers. NotifyDriverShm

Shared memory notification driver used by the Notify module to communicate between a pair of processors. See Notify Module

SharedRegion Module

ti.sdo.ipc.SharedRegion

Maintains shared memory for multiple shared regions. See SharedRegion Module

Additional modules in the subfolders of the ti.sdo.ipc package contain specific implementations of gates, heaps, notify drivers, transports, and various device family-specific modules.

In addition, the ti.sdo.ipc package defines the following interfaces that you may implement as your own custom modules:

Module

Module Path

IGateMPSupport

ti.sdo.ipc.interfaces.IGateMPSupport

IInterrupt

ti.sdo.ipc.notifyDrivers.IInterrupt

IMessageQTransport

ti.sdo.ipc.interfaces.IMessageQTransport

INotifyDriver

ti.sdo.ipc.interfaces.INotifyDriver

INotifySetup

ti.sdo.ipc.interfaces.INotifySetup

The <ipc_install_dir>/packages/ti/sdo/ipc directory contains the following packages that you may need to know about:

  • examples. Contains examples.

  • family. Contains device-specific support modules (used internally).

  • gates. Contains GateMP implementations (used internally).

  • heaps. Contains multiprocessor heaps.

  • interfaces. Contains interfaces.

  • notifyDrivers. Contains NotifyDriver implementations (used internally).

  • transports. Contains MessageQ transport implementations that are used internally.

4.4.6.1.1. Including Header Files

BIOS applications that use modules in the ti.sdo.ipc or ti.sdo.utils package should include the common header files provided in <ipc_install_dir>/packages/ti/ipc/. These header files offer a common API for both SYS/BIOS and HLOS users of IPC.

The following example C code includes header files applications may need to use. Depending on the APIs used in your application code, you may need to include different XDC, IPC, and SYS/BIOS header files.

#include <xdc/std.h>
#include <string.h>

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

/* ----- IPC module Headers          */
#include <ti/ipc/GateMP.h>
#include <ti/ipc/Ipc.h>
#include <ti/ipc/MessageQ.h>
#include <ti/ipc/HeapBufMP.h>
#include <ti/ipc/MultiProc.h>

/* ---- BIOS6 module Headers         */
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>

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

Note that the appropriate include file location has changed from previous versions of IPC. The XDCtools-generated header files are still available in <ipc_install_dir>/packages/ti/sdo/ipc/, but these should not directly be included in runtime .c code. You should search your applications for “ti/sdo/ipc” and “ti/sdo/utils” and change the header file references found as needed. Additional changes to API calls will be needed.

ipcSdoRun_Img1 Documentation for all common-header APIs is provided in Doxygen format in your IPC installation at <ipc_install_dir>/docs/doxygen/html/index.html. The latest version of that documentation is available online.

4.4.6.1.2. Standard IPC Function Call Sequence

For instance-based modules in IPC, the standard IPC methodology when creating object dynamically (that is, in C code) is to have the creator thread first initialize a MODULE_Params structure to its default values via a MODULE_Params_init() function. The creator thread can then set individual parameter fields in this structure as needed. After setting up the MODULE_Params structure, the creator thread calls the MODULE_create() function to creates the instance and initializes any shared memory used by the instance. If the instance is to be opened remotely, a unique name must be supplied in the parameters.

Other threads can access this instance via the MODULE_open() function, which returns a handle with access to the instance. The name that was used for instance creation must be used in the MODULE_open() function.

In most cases, MODULE_open() functions must be called in the context of a Task. This is because the thread running the MODULE_open() function needs to be able to block (to pend on a Semaphore in this case) while waiting for the remote processor to respond. The response from the remote processor triggers a hardware interrupt, which then posts a Semaphore to allow to Task to resume execution. The exception to this rule is that MODULE_open() functions do not need to be able to block when opening an instance on the local processor.

When the threads have finished using an instance, all threads that called MODULE_open() must call MODULE_close(). Then, the thread that called MODULE_create() can call MODULE_delete() to free the memory used by the instance.

Note that all threads that opened an instance must close that instance before the thread that created it can delete it. Also, a thread that calls MODULE_create() cannot call MODULE_close(). Likewise, a thread that calls MODULE_open() cannot call MODULE_delete().

4.4.6.1.3. Error Handling in IPC

Many of the APIs provided by IPC return an integer as a status code. Your application can test the status value returned against any of the provided status constants. For example:

MessageQ_Msg     msg;
MessageQ_Handle  messageQ;
Int              status;

...
status = MessageQ_get(messageQ, &msg, MessageQ_FOREVER);
    if (status < 0) {
        System_abort("Should not happen\n");
    }

Status constants have the following format: MODULE_[S|E]_CONDITION. For example, Ipc_S_SUCCESS, MessageQ_E_FAIL, and SharedRegion_E_MEMORY are status codes that may be returned by functions in the corresponding modules. Success codes always have values greater or equal to zero. For example, Ipc_S_SUCCESS=0 and Ipc_S_ALREADYSETUP=1; both are success codes. Failure codes always have values less than zero. Therefore, the presence of an error can be detected by simply checking whether the return value is negative.

Other APIs provided by IPC return a handle to a created object. If the handle is NULL, an error occurred when creating the object. For example:

messageQ = MessageQ_create(DSP_MESSAGEQNAME, NULL);
if (messageQ == NULL) {
    System_abort("MessageQ_create failed\n");
}

Refer to the Doxygen documentation for status codes returned by IPC functions.

4.4.6.2. IPC Module


API Reference Links

ipcCfg_Ipc_Img1

ipcRun_Ipc_Img1

The main purpose of the Ipc module is to initialize the various subsystems of IPC. All applications that use IPC modules must call the Ipc_start() API, which does the following:

  • Initializes a number of objects and modules used by IPC

  • Synchronizes multiple processors so they can boot in any order

An application that uses IPC APIs–such as MessageQ–must include the Ipc module header file and call Ipc_start() before any calls to IPC modules. Here is a BIOS-side example:

   #include <ti/ipc/Ipc.h>

 int main(int argc, char* argv[])
{
     Int status;

     /* Call Ipc_start() */
    status = Ipc_start();
     if (status < 0) {
        System_abort("Ipc_start failed\n");
     }

    BIOS_start();
     return (0);
}

By default, the BIOS implementation of Ipc_start() internally calls Notify_start() if it has not already been called, then loops through the defined SharedRegions so that it can set up the HeapMemMP and GateMP instances used internally by the IPC modules. It also sets up MessageQ transports to remote processors.

The SharedRegion with an index of 0 (zero) is often used by BIOS-side IPC_start() to create resource management tables for internal use by other IPC modules. Thus SharedRegion “0” must be accessible by all processors. See SharedRegion Module for more about the SharedRegion module.

4.4.6.2.1. Ipc Module Configuration (BIOS-side only)

In an XDCtools configuration file, you configure the Ipc module for use as follows:

Ipc = xdc.useModule('ti.sdo.ipc.Ipc');

You can configure what the Ipc_start() API will do–which modules it will start and which objects it will create–by using the Ipc.setEntryMeta method in the configuration file to set the following properties:

  • setupNotify. If set to false, the Notify module is not set up. The default is true.

  • setupMessageQ. If set to false, the MessageQ transport instances to remote processors are not set up and the MessageQ module does not attach to remote processors. The default is true.

For example, the following statements from the notify example configuration turn off the setup of the MessageQ transports and connections to remote processors:

/* To avoid wasting shared memory for MessageQ transports */
for (var i = 0; i < MultiProc.numProcessors; i++) {
    Ipc.setEntryMeta({
        remoteProcId: i,
        setupMessageQ: false,
    });
}

You can configure how the IPC module synchronizes processors by configuring the Ipc.procSync property. For example:

Ipc.procSync = Ipc.ProcSync_ALL;

The options are:

  • Ipc.ProcSync_ALL. If you use this option, the Ipc_start() API automatically attaches to and synchronizes all remote processors. If you use this option, your application should never call Ipc_attach(). Use this option if all IPC processors on a device start up at the same time and connections should be established between every possible pair of processors.

  • Ipc.ProcSync_PAIR. (Default) If you use this option, you must explicitly call Ipc_attach() to attach to a specific remote processor.

    If you use this option, Ipc_start() performs system-wide IPC initialization, but does not make connections to remote processors. Use this option if any or all of the following are true: - You need to control when synchronization with each remote processor occurs. - Useful work can be done while trying to synchronize with a remote processor by yielding a thread after each attempt to Ipc_attach() to the processor. - Connections to some remote processors are unnecessary and should be made selectively to save memory. - Ipc.ProcSync_NONE. If you use this option, Ipc_start() doesn’t synchronize any processors before setting up the objects needed by other modules.

    Use this option with caution. It is intended for use in cases where the application performs its own synchronization and you want to avoid a potential deadlock situation with the IPC synchronization.

    If you use the ProcSync_NONE option, Ipc_start() works exactly as it does with ProcSync_PAIR. : However, in this case, Ipc_attach() does not synchronize with the remote processor. As with other ProcSync options, Ipc_attach() still sets up access to GateMP, SharedRegion, Notify, NameServer, and MessageQ transports, so your application must still call Ipc_attach() for each remote processor that will be accessed. Note that an Ipc_attach() call for a remote processor whose ID is less than the local processor’s ID must occur after the corresponding remote processor has called Ipc_attach() to the local processor. For example, processor #2 can call Ipc_attach(1) only after processor #1 has called Ipc_attach(2).:

You can configure a function to perform custom actions in addition to the default actions performed when attaching to or detaching from a remote processor. These functions run near the end of Ipc_attach() and near the beginning of Ipc_detach(), respectively. Such functions must be non-blocking and must run to completion. The following example configures two attach functions and two detach functions. Each set of functions will be passed a different argument:

var Ipc = xdc.useModule('ti.sdo.ipc.Ipc');

var fxn = new Ipc.UserFxn;
fxn.attach = '&userAttachFxn1';
fxn.detach = '&userDetachFxn1';
Ipc.addUserFxn(fxn, 0x1);

fxn.attach = '&userAttachFxn2';
fxn.detach = '&userDetachFxn2';
Ipc.addUserFxn(fxn, 0x2);

ipcCfg_Ipc_Img2 The latest version of the IPC module configuration documentation is available here

4.4.6.2.2. Ipc Module APIs

In addition to the Ipc_start() API, which all applications that use IPC modules are required to call, the Ipc module also provides the following APIs for processor synchronization:

  • Ipc_attach() Creates a connection to the specified remote processor.

  • Ipc_detach() Deletes the connection to the specified remote processor.

You must call Ipc_start() on a processor before calling Ipc_attach().

Note

Call Ipc_attach() to the processor that owns shared memory region 0–usually the processor with an id of 0–before making a connection to any other remote processor. For example, if there are three processors configured with MultiProc, processor 1 should attach to processor 0 before it can attach to processor 2.

Use these functions unless you are using the Ipc.ProcSync_ALL configuration setting. With that option, Ipc_start() automatically attaches to and synchronizes all remote processors, and your application should never call Ipc_attach().

The Ipc.ProcSync_PAIR configuration option expects that your application will call Ipc_attach() for each remote processor with which it should be able to communicate.

Note

In ARM-Linux/DSP-RTOS scenario, Linux application gets the IPC configuration from LAD which has Ipc.ProcSync_ALL configured. DSP has Ipc.ProcSync_PAIR configured.

Processor synchronization means that one processor waits until the other processor signals that a particular module is ready for use. Within Ipc_attach(), this is done for the GateMP, SharedRegion (region 0), and Notify modules and the MessageQ transports.

You can call the Ipc_detach() API to delete internal instances created by Ipc_attach() and to free the memory used by these instances.

ipcRun_Ipc_Img2 The latest version of the IPC module run-time API documentation is available online

4.4.6.3. MessageQ Module


API Reference Links

msgCfg_Img1

msgRun_Img1

The MessageQ module supports the structured sending and receiving of variable length messages. It is OS independent and works with any threading model. For each MessageQ you create, there is a single reader and may be multiple writers.

MessageQ is the recommended messaging API for most applications. It can be used for both homogeneous and heterogeneous multi-processor messaging, along with single-processor messaging between threads.

Note

The MessageQ module in IPC is similar in functionality to the MSGQ module in DSP/BIOS 5.x.

The following are key features of the MessageQ module:

  • Writers and readers can be relocated to another processor with no runtime code changes.

  • Timeouts are allowed when receiving messages.

  • Readers can determine the writer and reply back.

  • Receiving a message is deterministic when the timeout is zero.

  • Messages can reside on any message queue.

  • Supports zero-copy transfers (BIOS only)

  • Messages can be sent and received from any type of thread.

  • The notification mechanism is specified by the application (BIOS only)

  • Allows QoS (quality of service) on message buffer pools. For example, using specific buffer pools for specific message queues. (BIOS only)

Messages are sent and received via a message queue. A reader is a thread that gets (reads) messages from a message queue. A writer is a thread that puts (writes) a message to a message queue. Each message queue has one reader and can have many writers. A thread may read from or write to multiple message queues.

  • Reader. The single reader thread calls MessageQ_create(), MessageQ_get(), MessageQ_free(), and MessageQ_delete().

  • Writer. Writer threads call MessageQ_open(), MessageQ_alloc(), MessageQ_put(), and MessageQ_close().

The following figure shows the flow in which applications typically use the main runtime MessageQ APIs:

../_images/IpcUG_ipc_2_3_1.png

Conceptually, the reader thread creates and owns the message queue. Writer threads then open a created message queue to get access to them.

4.4.6.3.1. Configuring the MessageQ Module (BIOS only)

On BIOS-based systems, you can configure a number of module-wide properties for MessageQ in your XDCtools configuration file.

msgCfg_Img2 A snapshot of the MessageQ module configuration documentation is available online.

To configure the MessageQ module, you must enable the module as follows:

var MessageQ = xdc.useModule('ti.sdo.ipc.MessageQ');

Some example Module-wide configuration properties you can set follow; refer to the IPC documentation for details.

// Maximum length of MessageQ names
MessageQ.maxNameLen = 32;

// Max number of MessageQs that can be dynamically created
MessageQ.maxRuntimeEntries = 10;

4.4.6.3.2. Creating a MessageQ Object

You can create message queues dynamically. Static creation is not supported. A MessageQ object is not a shared resource. That is, it resides on the processor, within the process, that creates it.

The reader thread creates a message queue. To create a MessageQ object dynamically, use the MessageQ_create() C API, which has the following syntax:

MessageQ_Handle MessageQ_create(String name, MessageQ_Params *params);

When you create a queue, you specify a name string. This name will be needed by the MessageQ_open() function, which is called by threads on the same or remote processors that want to send messages to the created message queue. While the name is not required (that is, it can be NULL), an unnamed queue cannot be opened.

An ISync handle is associated with the message queue via the synchronizer parameter.

If the call is successful, the MessageQ_Handle is returned. If the call fails, NULL is returned.

You initialize the params struct by using the MessageQ_Params_init() function, which initializes the params structure with the default values. A NULL value for params can be passed into the create call, which results in the defaults being used. The default synchronizer is SyncSem.

The following code creates a MessageQ object using SyncSem as the synchronizer.

MessageQ_Handle   messageQ;
MessageQ_Params   messageQParams;
SyncSem_Handle    syncSemHandle;

...

syncSemHandle = SyncSem_create(NULL, NULL);
MessageQ_Params_init(&messageQParams);
messageQParams.synchronizer = SyncSem_Handle_upCast(syncSemHandle);
messageQ = MessageQ_create(CORE0_MESSAGEQNAME, &messageQParams);

In this example, the CORE0_MESSAGEQNAME constant may be defined in header shared by multiple cores.

msgRun_Img2 A snapshot of the MessageQ module run-time API documentation is available online.

4.4.6.3.3. Opening a Message Queue

Writer threads open a created message queue to get access to them. In order to obtain a handle to a message queue that has been created, a writer thread must call MessageQ_open(), which has the following syntax.

Int MessageQ_open(String name, MessageQ_QueueId *queueId);

This function expects a name, which must match with the name of the created object. Internally MessageQ calls NameServer_get() to find the 32-bit queueId associated with the created message queue. NameServer looks both locally and remotely.

If no matching name is found on any processor, MessageQ_open() returns MessageQ_E_NOTFOUND. If the open is successful, the Queue ID is filled in and MessageQ_S_SUCCESS is returned.

The following code opens the MessageQ object created by the processor.

MessageQ_QueueId   remoteQueueId;
Int                status;

...

/* Open the remote message queue. Spin until it is ready. */
do {
   status = MessageQ_open(CORE0_MESSAGEQNAME, &remoteQueueId);
}
while (status < 0);

4.4.6.3.4. Allocating a Message

MessageQ manages message allocation via the MessageQ_alloc() and MessageQ_free() functions. MessageQ uses Heaps for message allocation. MessageQ_alloc() has the following syntax:

MessageQ_Msg MessageQ_alloc(UInt16        heapId,
                            UInt32        size);

The allocation size in MessageQ_alloc() must include the size of the message header, which is 32 bytes.

The following code allocates a message:

#define MSGSIZE                  256
#define HEAPID                     0
...
MessageQ_Msg     msg;

...

msg = MessageQ_alloc(HEAPID, sizeof(MessageQ_MsgHeader));
if (msg == NULL) {
    System_abort("MessageQ_alloc failed\n");
}

Once a message is allocated, it can be sent on any message queue. Once the reader receives the message, it may either free the message or re-use the message. Messages in a message queue can be of variable length. The only requirement is that the first field in the definition of a message must be a MsgHeader structure. For example:

typedef struct MyMsg {
    MessageQ_MsgHeader header;     // Required
    SomeEnumType       type        // Can be any field
    ...                            // ...
} MyMsg;

The MessageQ APIs use the MessageQ_MsgHeader internally. Your application should not modify or directly access the fields in the MessageQ_MsgHeader structure.

4.4.6.3.4.1. MessageQ Allocation and Heaps

All messages sent via the MessageQ module must be allocated from a xdc.runtime.IHeap implementation, such as ti.sdo.ipc.heaps.HeapBufMP. The same heap can also be used for other memory allocation not related to MessageQ.

The MessageQ_registerHeap() API assigns a MessageQ heapId to a heap. When allocating a message, the heapId is used, not the heap handle. The heapIds should start at zero and increase. The maximum number of heaps is determined by the numHeap module configuration property. See the online documentation for MessageQ_registerHeap() for details.

/* Register this heap with MessageQ */
status = MessageQ_registerHeap( HeapBufMP_Handle_upCast(heapHandle), HEAPID);

If the registration fails (for example, the heapId is already used), this function returns FALSE.

An application can use multiple heaps to allow an application to regulate its message usage. For example, an application can allocate critical messages from a heap of fast on-chip memory and non-critical messages from a heap of slower external memory. Additionally, heaps MessageQ uses can be shared with other modules and/or the application.

MessageQ alternatively supports allocating messages without the MessageQ_alloc() function.

Heaps can be unregistered via MessageQ_unregisterHeap().

4.4.6.3.4.2. MessageQ Allocation Without a Heap

It is possible to send MessageQ messages that are allocated statically instead of being allocated at run-time via MessageQ_alloc(). However the first field of the message must still be a MsgHeader. To make sure the MsgHeader has valid settings, the application must call MessageQ_staticMsgInit(). This function initializes the header fields in the same way that MessageQ_alloc() does, except that it sets the heapId field in the header to the MessageQ_STATICMSG constant.

If an application uses messages that were not allocated using MessageQ_alloc(), it cannot free the messages via the MessageQ_free() function, even if the message is received by a different processor. Also, the transport may internally call MessageQ_free() and encounter an error.

If MessageQ_free() is called on a statically allocated message, it asserts that the heapId of the message is not MessageQ_STATICMSG.

4.4.6.3.5. Sending a Message

Once a message queue is opened and a message is allocated, the message can be sent to the MessageQ via the MessageQ_put() function, which has the following syntax.

Int MessageQ_put(MessageQ_QueueId queueId,
               MessageQ_Msg     msg);

For example:

status = MessageQ_put(remoteQueueId, msg);

Opening a queue is not required. Instead the message queue ID can be “discovered” via the MessageQ_getReplyQueue() function, which returns the 32-bit queueId.

MessageQ_QueueId replyQueue;
MessageQ_Msg     msg;

/* Use the embedded reply destination */
replyMessageQ = MessageQ_getReplyQueue(msg);
if (replyMessageQ == MessageQ_INVALIDMESSAGEQ) {
    System_abort("Invalid reply queue\n");
}

/* Send the response back */
status = MessageQ_put(replyQueue, msg);
    if (status < 0) {
        System_abort("MessageQ_put was not successful\n");
    }

If the destination queue is local, the message is placed on the appropriate priority linked list and the ISync signal function is called. If the destination queue is on a remote processor, the message is given to the proper transport and returns.

If MessageQ_put() succeeds, it returns MessageQ_S_SUCCESS. If MessageQ_E_FAIL is returned, an error occurred and the caller still owns the message.

There can be multiple senders to a single message queue. MessageQ handles the thread safety.

Before you send a message, you can use the MessageQ_setMsgId() function to assign a numeric value to the message that can be checked by the receiving thread.

/* Increment...the remote side will check this */
msgId++;
MessageQ_setMsgId(msg, msgId);

You can use the MessageQ_setMsgPri() function to set the priority of the message.

4.4.6.3.6. Receiving a Message

To receive a message, a reader thread calls the MessageQ_get() API.

Int MessageQ_get(MessageQ_Handle handle,
                 MessageQ_Msg    *msg,
                 UInt            timeout)

If a message is present, it returned by this function. In this case the ISync’s wait() function is not called. For example:

/* Get a message */
status = MessageQ_get(messageQ, &msg, MessageQ_FOREVER);
if (status < 0) {
     System_abort("Should not happen; timeout is forever\n");
}

If no message is present and no error occurs, this function blocks while waiting for the timeout period for the message to arrive. If the timeout period expires, MessageQ_E_FAIL is returned. If an error occurs, the msg argument will be unchanged.

After receiving a message, you can use the following APIs to get information about the message from the message header:

  • MessageQ_getMsgId() gets the ID value set by MessageQ_setMsgId(). For example:

/* Get the id and increment it to send back */
msgId = MessageQ_getMsgId(msg);
msgId += NUMCLIENTS;
MessageQ_setMsgId(msg, msgId);
  • MessageQ_getMsgPri() gets the priority set by MessageQ_setMsgPri().

  • MessageQ_getMsgSize() gets the size of the message in bytes.

  • MessageQ_getReplyQueue() gets the ID of the queue provided by MessageQ_setReplyQueue().

4.4.6.3.7. Deleting a MessageQ Object

MessageQ_delete() frees a MessageQ object stored in local memory. If any messages are still on the internal linked lists, they will be freed. The contents of the handle are nulled out by the function to prevent use after deleting.

Void MessageQ_delete(MessageQ_Handle *handle);

The queue array entry is set to NULL to allow re-use.

Once a message queue is deleted, no messages should be sent to it. A MessageQ_close() is recommended, but not required.

4.4.6.3.8. Message Priorities

MessageQ supports three message priorities as follows:

  • MessageQ_NORMALPRI = 0

  • MessageQ_HIGHPRI = 1

  • MessageQ_URGENTPRI = 3

You can set the priority level for a message before sending it by using the MessageQ_setMsgPri() function:

Void MessageQ_setMsgPri(MessageQ_Msg        msg,
                      MessageQ_Priority   priority)

Internally a MessageQ object maintains two linked lists: normal and high-priority. A normal priority message is placed onto the “normal” linked list in FIFO manner. A high priority message is placed onto the “high-priority” linked list in FIFO manner. An urgent message is placed at the beginning of the high linked list.

Note

Since multiple urgent messages may be sent before a message is read, the order of urgent messages is not guaranteed.

When getting a message, the reader checks the high priority linked list first. If a message is present on that list, it is returned. If not, the normal priority linked list is checked. If a message is present there, it is returned. Otherwise the synchronizer’s wait function is called.

Note

The MessageQ priority feature is enabled by the selecting different MessageQ transports. Some MessageQ implementations (e.g. Linux) may not support multiple transports and therefore may not support this feature.

4.4.6.3.9. Thread Synchronization (BIOS only)

MessageQ supports reads and writes of different thread models. It can work with threading models that include SYS/BIOS’s Hwi, Swi, and Task threads.

This flexibility is accomplished by using an implementation of the xdc.runtime.knl.ISync interface. The creator of the message queue must also create an object of the desired ISync implementation and assign that object as the “synchronizer” of the MessageQ. Each message queue has its own synchronizer object.

An ISync object has two main functions: signal() and wait(). Whenever MessageQ_put() is called, the signal() function of the ISync implementation is called. If MessageQ_get() is called when there are no messages on the queue, the wait() function of the ISync implementation is called. The timeout passed into the MessageQ_get() is directly passed to the ISync wait() API.

Note

Since ISync implementations must be binary, the reader thread must drain the MessageQ of all messages before waiting for another signal.

For example, if the reader is a SYS/BIOS Swi, the instance could be a SyncSwi. When a MessageQ_put() is called, the Swi_post() API would be called. The Swi would run and it must call MessageQ_get() until no messages are returned. If the Swi does not get all the messages, the Swi will not run again, or at least will not run until a new message is placed on the queue.

The calls to ISync functions occurs directly in MessageQ_put() when the call occurs on the same processor where the queue was created. In the remote case, the transport calls MessageQ_put(), which is then a local put, and the signal function is called.

The following are ISync implementations provided by XDCtools and SYS/BIOS:

  • xdc.runtime.knl.SyncNull. The signal() and wait() functions do nothing. Basically this implementation allows for polling.

  • xdc.runtime.knl.SyncSemThread. An implementation built using the xdc.runtime.knl.Semaphore module, which is a binary semaphore.

  • xdc.runtime.knl.SyncGeneric.xdc. This implementation allows you to use custom signal() and wait() functions as needed.

  • ti.sysbios.syncs.SyncSem. An implementation built using the ti.sysbios.ipc.Semaphore module. The signal() function runs a Semaphore_post(). The wait() function runs a Semaphore_pend().

  • ti.sysbios.syncs.SyncSwi. An implementation built using the ti.sysbios.knl.Swi module. The signal() function runs a Swi_post(). The wait() function does nothing and returns FALSE if the timeout elapses.

  • ti.sysbios.syncs.SyncEvent. An implementation built using the ti.sysbios.ipc.Event module. The signal() function runs an Event_post(). The wait() function does nothing and returns FALSE if the timeout elapses. This implementation allows waiting on multiple events.

The following code from the “message” example creates a SyncSem instance and assigns it to the synchronizer field in the MessageQ_Params structure before creating the MessageQ instance:

#include <ti/sysbios/syncs/SyncSem.h>
...

MessageQ_Params   messageQParams;
SyncSem_Handle    syncSemHandle;

/* Create a message queue using SyncSem as synchronizer */
syncSemHandle = SyncSem_create(NULL, NULL);
MessageQ_Params_init(&messageQParams);
messageQParams.synchronizer = SyncSem_Handle_upCast(syncSemHandle);
messageQ = MessageQ_create(CORE1_MESSAGEQNAME, &messageQParams, NULL);

4.4.6.3.10. ReplyQueue

For some applications, doing a MessageQ_open() on a queue is not realistic. For example, a server may not want to open all the clients’ queues for sending responses. To support this use case, the message sender can embed a reply queueId in the message using the MessageQ_setReplyQueue() function.

Void MessageQ_setReplyQueue(MessageQ_Handle handle,
                          MessageQ_Msg msg)

This API stores the message queue’s queueId into fields in the MsgHeader.

The MessageQ_getReplyQueue() function does the reverse. For example:

MessageQ_QueueId replyQueue;
MessageQ_Msg     msg;
...

/* Use the embedded reply destination */
replyMessageQ = MessageQ_getReplyQueue(msg);
if (replyMessageQ == MessageQ_INVALIDMESSAGEQ) {
   System_abort("Invalid reply queue\n");
}

The MessageQ_QueueId value returned by this function can then be used in a MessageQ_put() call.

The queue that is embedded in the message does not have to be the sender’s queue.

4.4.6.3.11. Remote Communication via Transports (BIOS only)

MessageQ is designed to support multiple processors. To allow this, different transports can be plugged into MessageQ.

In a multi-processor system, MessageQ communicates with other processors via ti.sdo.ipc.interfaces.IMessageQTransport instances. There can be up to two IMessageQTransport instances for each processor to which communication is desired. One can be a normal-priority transport and the other for handling high-priority messages. This is done via the priority parameter in the transport create() function. If there is only one register to a remote processor (either normal or high), all messages go via that transport.

There can be different transports on a processor. For example, there may be a shared memory transport to processor A and an sRIO one to processor B.

When your application calls Ipc_start(), the default transport instance used by MessageQ is created automatically. Internally, transport instances are responsible for registering themselves with MessageQ via the MessageQ_registerTransport() function.

IPC provides an implementation of the IMessageQTransport interface called ti.sdo.ipc.transports.TransportShm (shared memory). You can write other implementations to meet your needs.

When a transport is created via a transport-specific create() call, a remote processor ID (defined via the MultiProc module) is specified. This ID denotes which processor this instance communicates with. Additionally there are configuration properties for the transport–such as the message priority handled–that can be defined in a Params structure. The transport takes these pieces of information and registers itself with MessageQ. MessageQ now knows which transport to call when sending a message to a remote processor.

Trying to send to a processor that has no transport results in an error.

4.4.6.3.11.1. Custom Transport Implementations

Transports can register and unregister themselves dynamically. That is, if the transport instance is deleted, it should unregister with MessageQ.

When receiving a message, transports need to form the MessageQ_QueueId that allows them to call MessageQ_put(). This is accomplished via the MessageQ_getDstQueue() API.

MessageQ_QueueId MessageQ_getDstQueue(MessageQ_Msg msg)

4.4.6.3.12. Sample Runtime Program Flow (Dynamic)

The following figure shows the typical sequence of events when using a MessageQ. A message queue is created by a Task. An open on the same processor then occurs. Assume there is one message in the system. The opener allocates the message and sends it to the created message queue, which gets and frees it.

../_images/IpcUG_ipc_2_3_2.png

4.4.6.4. ListMP Module


API Reference Links

lmpCfg_Img1

lmpRun_Img1

The ti.sdo.ipc.ListMP module is a linked-list based module designed to be used in a multi-processor environment. It is designed to provide a means of communication between different processors. ListMP uses shared memory to provide a way for multiple processors to share, pass, or store data buffers, messages, or state information. ListMP is a low-level module used by several other IPC modules, including MessageQ, HeapBufMP, and transports, as a building block for their instance and state structures.

A common challenge that occurs in a multi-processor environment is preventing concurrent data access in shared memory between different processors. ListMP uses a multi-processor gate to prevent multiple processors from simultaneously accessing the same linked-list. All ListMP operations are atomic across processors.

You create a ListMP instance dynamically as follows:

# Initialize a ListMP_Params structure by calling ListMP_Params_init(). # Specify the name, regionId, and other parameters in the ListMP_Params structure. # Call ListMP_create().

ListMP uses a ti.sdo.utils.NameServer instance to store the instance information. The ListMP name supplied must be unique for all ListMP instances in the system.

ListMP_Params params;
GateMP_Handle gateHandle;
ListMP_Handle handle1;

/* If gateHandle is NULL, the default remote gate will be
   automatically chosen by ListMP */
gateHandle = GateMP_getDefaultRemote();
ListMP_Params_init(&params);
params.gate = gateHandle;
params.name = "myListMP";
params.regionId = 1;
handle1 = ListMP_create(&params, NULL);

Once created, another processor or thread can open the ListMP instance by calling ListMP_open().

while (ListMP_open("myListMP", &handle1, NULL) < 0) {
    ;
}

ListMP uses SharedRegion pointers (see SharedRegion Module), which are portable across processors, to translate addresses for shared memory. The processor that creates the ListMP instance must specify the shared memory in terms of its local address space. This shared memory must have been defined in the SharedRegion module by the application. The ListMP module has the following constraints:

  • ListMP elements to be added/removed from the linked-list must be stored in a shared memory region.

  • The linked list must be on a worst-case cache line boundary for all the processors sharing the list.

  • ListMP_open() should be called only when global interrupts are enabled.

A list item must have a field of type ListMP_Elem as its first field. For example, the following structure could be used for list elements:

typedef struct Tester {
  ListMP_Elem elem;
  Int         scratch[30];
  Int         flag;
} Tester;

Besides creating, opening, and deleting a list instance, the ListMP module provides functions for the following common list operations:

  • ListMP_empty(). Test for an empty ListMP.

  • ListMP_getHead(). Get the element from the front of the ListMP.

  • ListMP_getTail(). Get the element from the end of the ListMP.

  • ListMP_insert(). Insert element into a ListMP at the current location.

  • ListMP_next(). Return the next element in the ListMP (non-atomic).

  • ListMP_prev(). Return previous element in the ListMP (non-atomic).

  • ListMP_putHead(). Put an element at the head of the ListMP.

  • ListMP_putTail(). Put an element at the end of the ListMP.

  • ListMP_remove(). Remove the current element from the middle of the ListMP.

This example prints a “flag” field from the list elements in a ListMP instance in order:

System_printf("On the List: ");
testElem = NULL;
while ((testElem = ListMP_next(handle, (ListMP_Elem *)testElem)) != NULL) {
    System_printf("%d ", testElem->flag);
}

This example prints the same items in reverse order:

System_printf("in reverse: ");
elem = NULL;
while ((elem = ListMP_prev(handle, elem)) != NULL) {
    System_printf("%d ", ((Tester  *)elem)->flag);
}

This example determines if a ListMP instance is empty:

if (ListMP_empty(handle1) == TRUE) {
  System_printf("Yes, handle1 is empty\n");
}

This example places a sequence of even numbers in a ListMP instance:

/* Add 0, 2, 4, 6, 8 */
for (i = 0; i < COUNT; i = i + 2) {
    ListMP_putTail(handle1, (ListMP_Elem *)&(buf[i]));
}

The instance state information contains a pointer to the head of the linked-list, which is stored in shared memory. Other attributes of the instance stored in shared memory include the version, status, and the size of the shared address. Other processors can obtain a handle to the linked list by calling ListMP_open().

The following figure shows local memory and shared memory for processors Proc 0 and Proc 1, in which Proc 0 calls ListMP_create() and Proc 1 calls ListMP_open().

../_images/IpcUG_ipc_2_4_1.png

The cache alignment used by the list is taken from the SharedRegion on a per-region basis. The alignment must be the same across all processors and should be the worst-case cache line boundary.

4.4.6.5. HeapMP Module


API Reference Links

hmpCfg_Img1

hmpRun_Img1

HeapBuf

hmpCfg_Img2

hmpRun_Img2

HeapMem

hmpCfg_Img3

hmpRun_Img3

HeapMultiBuf

The ti.sdo.ipc.heaps package provides three implementations of the xdc.runtime.IHeap interface.

  • HeapBufMP. Fixed-size memory manager. All buffers allocated from a HeapBufMP instance are of the same size. There can be multiple instances of HeapBufMP that manage different sizes. The ti.sdo.ipc.heaps.HeapBufMP module is modeled after SYS/BIOS 6’s HeapBuf module (ti.sysbios.heaps.HeapBuf).

  • HeapMultiBufMP. Each instance supports up to 8 different fixed sizes of buffers. When an allocation request is made, the HeapMultiBufMP instance searches the different buckets to find the smallest one that satisfies the request. If that bucket is empty, the allocation fails. The ti.sdo.ipc.heaps.HeapMultiBufMP module is modeled after SYS/BIOS 6’s HeapMultiBuf module (ti.sysbios.heaps.HeapMultiBuf).

  • HeapMemMP. Variable-size memory manager. HeapMemMP manages a single buffer in shared memory from which blocks of user-specified length are allocated and freed. The ti.sdo.ipc.heaps.HeapMemMP module is modeled after SYS/BIOS 6’s HeapMem module (ti.sysbios.heaps.HeapMem).

The main addition to these modules is the use of shared memory and the management of multi-processor exclusion.

The SharedRegion modules, and therefore the MessageQ module and other IPC modules that use SharedRegion, use a HeapMemMP instance internally.

The following subsections use “Heap*MP” to refer to the HeapBufMP, HeapMultiBufMP, and HeapMemMP modules.

Note

These Heap*MP Modules are only available on SYS/BIOS-based cores, they are not available when running an HLOS (e.g. Linux). As an HLOS shared memory solution, many Linux SDKs are recommending/providing CMEM. There are limitations (e.g. you cannot alloc on HLOS and free on RTOS), but these are in line with using the HLOS as a master (owning all resources) and RTOS as a slave (using resources provided by the master).

4.4.6.5.1. Configuring a Heap*MP Module

In addition to configuring Heap*MP instances, you can set module-wide configuration properties. For example, the maxNameLen property lets you set the maximum length of heap names. The track[Max]Allocs module configuration property enables/disables tracking memory allocation statistics.

A Heap*MP instance uses a NameServer instance to manage name/value pairs.

The Heap*MP modules make the following assumptions:

  • The SharedRegion module handles address translation between a virtual shared address space and the local processor’s address space. If the memory address spaces are identical across all processors, or if a single processor is being used, no address translation is required and the SharedRegion module must be appropriately configured.

  • Both processors must have the same endianness.

4.4.6.5.2. Creating a Heap*MP Instance

Heaps can be created dynamically. You use the Heap*MP_create() functions to dynamically create Heap*MP instances. As with other IPC modules, before creating a Heap*MP instance, you initialize a Heap*MP_Params structure and set fields in the structure to the desired values. When you create a heap, the shared memory is initialized and the Heap*MP object is created in local memory. Only the actual buffers and some shared information reside in shared memory.

The following code example initializes a HeapBufMP_Params structure and sets fields in it. It then creates and registers an instance of the HeapBufMP module.

/* Create the heap that will be used to allocate messages. */
HeapBufMP_Params_init(&heapBufMPParams);
heapBufMPParams.regionId      = 0;        /* use default region */
heapBufMPParams.name          = "myHeap";
heapBufMPParams.align         = 256;
heapBufMPParams.numBlocks     = 40;
heapBufMPParams.blockSize     = 1024;
heapBufMPParams.gate          = NULL;     /* use system gate */
heapHandle = HeapBufMP_create(&heapBufMPParams);
if (heapHandle == NULL) {
    System_abort("HeapBufMP_create failed\n");
}

/* Register this heap with MessageQ */
MessageQ_registerHeap(HeapBufMP_Handle_upCast(heapHandle), HEAPID);

The parameters for the various Heap*MP implementations vary. For example, when you create a HeapBufMP instance, you can configure the following parameters after initializing the HeapBufMP_Params structure:

  • regionId. The index corresponding to the shared region from which shared memory will be allocated.

  • name. A name of the heap instance for NameServer (optional).

  • align. Requested alignment for each block.

  • numBlocks. Number of fixed size blocks.

  • blockSize. Size of the blocks in this instance.

  • gate. A multiprocessor gate for context protection.

  • exact. Only allocate a block if the requested size is an exact match. Default is false.

Of these parameters, the ones that are common to all three Heap*MP implementations are gate, name and regionId.

4.4.6.5.3. Opening a Heap*MP Instance

Once a Heap*MP instance is created on a processor, the heap can be opened on another processor to obtain a local handle to the same shared instance. In order for a remote processor to obtain a handle to a Heap*MP that has been created, the remote processor needs to open it using Heap*MP_open().

The Heap*MP modules use a NameServer instance to allow a remote processor to address the local Heap*MP instance using a user-configurable string value as an identifier. The Heap*MP name is the sole parameter needed to identify an instance.

The heap must be created before it can be opened. An open call matches the call’s version number with the creator’s version number in order to ensure compatibility. For example:

HeapBufMP_Handle heapHandle; …

/* Open heap created by other processor. Loop until open. */
do {
    status = HeapBufMP_open("myHeap", &heapHandle);
}
while (status < 0);

/* Register this heap with MessageQ */
MessageQ_registerHeap(HeapBufMP_Handle_upCast(heapHandle), HEAPID);

4.4.6.5.4. Closing a Heap*MP Instance

Heap*MP_close() frees an opened Heap*MP instance stored in local memory. Heap*MP_close() may only be used to finalize instances that were opened with Heap*MP_open() by this thread. For example:

HeapBufMP_close(&heapHandle);

Never call Heap*MP_close() if some other thread has already called Heap*MP_delete().

4.4.6.5.5. Deleting a Heap*MP Instance

The Heap*MP creator thread can use Heap*MP_delete() to free a Heap*MP object stored in local memory and to flag the shared memory to indicate that the heap is no longer initialized. Heap*MP_delete() may not be used to finalize a heap using a handle acquired using Heap*MP_open()–Heap*MP_close() should be used by such threads instead.

4.4.6.5.6. Allocating Memory from the Heap

The HeapBufMP_alloc() function obtains the first buffer off the heap’s freeList.

The HeapMultiBufMP_alloc() function searches through the buckets to find the smallest size that honors the requested size. It obtains the first block on that bucket.

If the “exact” field in the Heap*BufMP_Params structure was true when the heap was created, the alloc only returns the block if the blockSize for a bucket is the exact size requested. If no exact size is found, an allocation error is returned.

The HeapMemMP_alloc() function allocates a block of memory of the requested size from the heap.

For all of these allocation functions, the cache coherency of the message is managed by the SharedRegion module that manages the shared memory region used for the heap.

4.4.6.5.7. Freeing Memory to the Heap

The HeapBufMP_free() function returns an allocated buffer to its heap.

The HeapMultiBufMP_free() function searches through the buckets to determine on which bucket the block should be returned. This is determined by the same algorithm as the HeapMultiBufMP_alloc() function, namely the smallest blockSize that the block can fit into.

If the “exact” field in the Heap*BufMP_Params structure was true when the heap was created, and the size of the block to free does not match any bucket’s blockSize, an assert is raised.

The HeapMemMP_free() function returns the allocated block of memory to its heap.

For all of these deallocation functions, cache coherency is managed by the corresponding Heap*MP module.

4.4.6.5.8. Querying Heap Statistics

Both heap modules support use of the xdc.runtime.Memory module’s Memory_getStats() and Memory_query() functions on the heap.

In addition, the Heap*MP modules provide the Heap*MP_getStats(), Heap*MP_getExtendedStats(), and Heap*MP_isBlocking() functions to enable you to gather information about a heap.

By default, allocation tracking is often disabled in shared-heap modules for performance reasons. You can set the HeapBufMP.trackAllocs and HeapMultiBufMP.trackMaxAllocs configuration properties to true in order to turn on allocation tracking for their respective modules. Refer to the CDOC documentation for further information.

Sample Runtime Program Flow The following diagram shows the program flow for a two-processor (or two-thread) application. This application creates a Heap*MP instance dynamically.

../_images/Heapmp_bigfig.png

4.4.6.6. GateMP Module


API Reference Links

gpmCfg_Img1

gmpRun_Img1

A GateMP instance can be used to enforce both local and remote context protection. That is, entering a GateMP can prevent preemption by another thread/process running on the same processor and simultaneously prevent a remote processor from entering the same gate. GateMP’s are typically used to protect reads/writes to a shared resource, such as shared memory.

Note

Initial IPC 3.x releases only supported GateMP on BIOS. It was introduced on QNX in IPC 3.10, and on Linux/Android in 3.21.

4.4.6.6.1. Creating a GateMP Instance

As with other IPC modules, GateMP instances can only be created dynamically.

Before creating the GateMP instance, you initialize a GateMP_Params structure and set fields in the structure to the desired values. You then use the GateMP_create() function to dynamically create a GateMP instance.

When you create a gate, shared memory is initialized, but the GateMP object is created in local memory. Only the gate information resides in shared memory.

The following code creates a GateMP object:

GateMP_Params gparams;
GateMP_Handle gateHandle;

GateMP_Params_init(&gparams);
gparams.localProtect = GateMP_LocalProtect_THREAD;
gparams.remoteProtect = GateMP_RemoteProtect_SYSTEM;
gparams.name = "myGate";
gparams.regionId = 1;
gateHandle = GateMP_create(&gparams, NULL);

A gate can be configured to implement remote processor protection in various ways. This is done via the params.remoteProtect configuration property. The options for params.remoteProtect are as follows:

  • GateMP_RemoteProtect_NONE. Creates only the local gate specified by the localProtect property.

  • GateMP_RemoteProtect_SYSTEM. Uses the default device-specific gate protection mechanism for your device. Internally, GateMP automatically uses device-specific implementations of multi-processor mutexes implemented via a variety of hardware mechanisms. Devices typically support a single type of system gate, so this is usually the correct configuration setting for params.remoteProtect.

  • GateMP_RemoteProtect_CUSTOM1 and GateMP_RemoteProtect_CUSTOM2 (BIOS-only). Some devices support multiple types of system gates. If you know that GateMP has multiple implementations of gates for your device, you can use one of these options.

Several gate implementations used internally for remote protection are provided in the ti.sdo.ipc.gates package.

A gate can be configured to implement local protection at various levels. This is done via the params.localProtect configuration property (BIOS-only). The options for params.localProtect are as follows:

  • GateMP_LocalProtect_NONE. Uses the XDCtools GateNull implementation, which does not offer any local context protection. For example, you might use this option for a single-threaded local application that still needs remote protection.

  • GateMP_LocalProtect_INTERRUPT. Uses the SYS/BIOS GateHwi implementation, which disables hardware interrupts.

  • GateMP_LocalProtect_TASKLET. Uses the SYS/BIOS GateSwi implementation, which disables software interrupts.

  • GateMP_LocalProtect_THREAD. Uses the SYS/BIOS GateMutexPri implementation, which is based on Semaphores. This option may use a different gate than the following option on some operating systems. When using SYS/BIOS, they are equivalent.

  • GateMP_LocalProtect_PROCESS. Uses the SYS/BIOS GateMutexPri implementation, which is based on Semaphores.

This property is currently ignored (as of IPC 3.23) on non-BIOS OSes. A thread-level mutex is used for local protection independent of this property setting.

Other fields you are required to set in the GateMP_Params structure are:

  • name. The name of the GateMP instance.

  • regionId. The ID of the SharedRegion to use for shared memory used by this GateMP instance (BIOS-only).

gmpRun_Img2 The latest version of the GateMP module run-time API documentation is available online

4.4.6.6.2. Opening a GateMP Instance

Once an instance is created on a processor, the gate can be opened on another processor to obtain a local handle to the same instance.

The GateMP module uses a NameServer instance to allow a remote processor to address the local GateMP instance using a user-configurable string value as an identifier rather than a potentially dynamic address value.

status = GateMP_open("myGate", &gateHandle);
if (status < 0) {
    System_printf("GateMP_open failed\n");
}

4.4.6.6.3. Closing a GateMP Instance

GateMP_close() frees a GateMP object stored in local memory.

GateMP_close() should never be called on an instance whose creator has been deleted.

4.4.6.6.4. Deleting a GateMP Instance

GateMP_delete() frees a GateMP object stored in local memory and flags the shared memory to indicate that the gate is no longer initialized.

A thread may not use GateMP_delete() if it acquired the handle to the gate using GateMP_open(). Such threads should call GateMP_close() instead.

4.4.6.6.5. Entering a GateMP Instance

Either the GateMP creator or opener may call GateMP_enter() to enter a gate. While it is necessary for the opener to wait for a gate to be created to enter a created gate, it isn’t necessary for a creator to wait for a gate to be opened before entering it.

GateMP_enter() enters the caller’s local gate. The local gate (if supplied) blocks if entered on the local processor. If entered by the remote processor, GateMP_enter() spins until the remote processor has left the gate.

No matter what the params.localProtection configuration property is set to, after GateMP_enter() returns, the caller has exclusive access to the data protected by this gate.

A thread may reenter a gate without blocking or failing.

GateMP_enter() returns a “key” that is used by GateMP_leave() to leave this gate; this value is used to restore thread preemption to the state that existed just prior to entering this gate.

IArg key;

/* Enter the gate */
key = GateMP_enter(gateHandle);

4.4.6.6.6. Leaving a GateMP Instance

GateMP_leave() may only called by a thread that has previously entered this gate via GateMP_enter().

After this method returns, the caller must not access the data structure protected by this gate (unless the caller has entered the gate more than once and other calls to leave remain to balance the number of previous calls to enter).

IArg key;

/* Leave the gate */
GateMP_leave(gateHandle, key);

4.4.6.6.7. Querying a GateMP Instance

GateMP_query() returns TRUE if a gate has a given quality, and FALSE otherwise, including cases when the gate does not recognize the constant describing the quality. The qualities you can query are:

  • GateMP_Q_BLOCKING. If GateMP_Q__BLOCKING is FALSE, the gate never blocks.

  • GateMP_Q_PREEMPTING. If GateMP_Q_PREEMPTING is FALSE, the gate does not allow other threads to preempt the thread that has already entered the gate.

4.4.6.6.8. NameServer Interaction

The GateMP module uses a ti.sdo.utils.NameServer instance to store instance information when an instance is created and the name parameter is non-NULL. The length of this name is limited to 16 characters (by default) including the null terminator (’0’). This length can be increased by configuring the GateMP.maxNameLen module configuration property. If a name is supplied, it must be unique for all GateMP instances.

Other modules can use GateMP instances to protect access to their shared memory resources. For example, the NameServer name tables are protected by setting the “gate” property of the ti.sdo.utils.NameServer module.

These examples set the “gate” property for various modules:

heapBufMPParams.gate  = GateMP_getDefaultRemote();
listMPParams.gate     = gateHandle;

4.4.6.6.9. Sample Runtime Program Flow (Dynamic)

The following diagram shows the program flow for a two-processor (or two-thread) application. This application creates a Gate dynamically.

../_images/IpcUG_ipc_2_6_1.png

4.4.6.7. GateMP Support for UIO and Misc Driver

4.4.6.7.1. Introduction

Starting in IPC 3.42, the GateMP module added support for accessing device memory without using /dev/mem. Instead of accessing /dev/mem, a uio driver for Shared Region 0 access and a misc driver to expose a hwspinlock user interface can be used. These drivers are added in the Android SDK and GLSDK kernels. This feature was added because in some environments, /dev/mem may not be accessible to the LAD.

Note

There is no change to how LAD is launched in this case, and if the uio and misc driver are not available in the kernel, it will fall back to using /dev/mem.

Note

This applies only to Linux/Android IPC.

4.4.6.7.2. Config

Enable the uio and misc drivers in the kernel by enabling the following config options:

CONFIG_HWSPINLOCK_USER=y CONFIG_UIO=y CONFIG_UIO_PDRV_GENIRQ=y Update your dts file to define the hwspinlocks being used by the misc driver. Must start from hwspinlock “0” and go up sequentially:

gatemp {
        compatible = "hwspinlock-user";
        hwlocks = <&hwspinlock 0>,
                  <&hwspinlock 1>,
                  <&hwspinlock 2>,
                  <&hwspinlock 3>,
                  <&hwspinlock 4>,
                  <&hwspinlock 5>,
                  <&hwspinlock 6>,
                  <&hwspinlock 7>,
                  <&hwspinlock 8>,
                  <&hwspinlock 9>;
};

The hwspinlock configuration in the dts file MUST match the BIOS-side IPC HWSpinlock configuration. Configuration the BIOS-side IPC module by adding the following to your cfg file (where <N> is equal to the number of spinlocks in the dts listing):

var HWSpinlock = xdc.useModule(‘ti.sdo.ipc.gates.GateHWSpinlock’);
HWSpinlock.numLocks = <N>;

Update your dts file to add the UIO-based node for Shared Region 0 (modify address and size so that it matches the Shared Region 0 definition in the remote core image):

Kernel 3.14-based releases:

sr0 {
        compatible = "generic-uio";
        reg = <0xbfd00000 0x100000>;
};

Kernel 4.4-based releases:

sr0 {
        compatible = "generic-uio";
        reg = <0x0 0xbfb00000 0x0 0x100000>;
};

Note

Make sure that the “compatible” property in the sr0 dts node matches the UIO’s of_id string defined in the kernel command line.

Append the following to the kernel’s CONFIG_CMDLINE:

uio_pdrv_genirq.of_id=generic-uio

For example:

CONFIG_CMDLINE="root=/dev/mmcblk0p2 rw console=ttyS0,119200 androidboot.console=ttyS0 init=/init rootfstype=ext4 rootwait drm.rnodes=1 snd.slots_reserved=1,1 androidboot.selinux=permissive androidboot.hardware=jacinto6evmboard uio_pdrv_genirq.of_id=generic-uio"

Note

Changes for the command line are already present in the Android 6AM.1.0 release. For GLSDK, the command line comes from the bootargs set in the uenv.txt file in the boot partition.

Also, specific owner/group must be set for the following files:

  • /dev/uio0

  • /dev/hwspinlock

  • Android: /data/lad, /data/lad/LAD

  • Linux: /tmp/LAD

4.4.6.8. Notify Module


API Reference Links

notCfg_Img1

notRun_Img1

he ti.sdo.ipc.Notify module manages the multiplexing/demultiplexing of software interrupts over hardware interrupts. In order to use any Notify APIs, you must first call Ipc_start(). This sets up all the necessary Notify drivers, shared memory, and interprocessor interrupts. However, note that if you’ve configured Ipc.setupNotify = false, you will need to explicitly call Notify_start() outside the scope of Ipc_start().

To be able to receive notifications, a processor registers one or more callback functions to an eventId by calling Notify_registerEvent(). The callback function must have the following signature:

Void cbFxn(UInt16 procId, UInt16 lineId, UInt32 eventId, UArg arg, UInt32 payload);

Notify_registerEvent(), like most other Notify APIs, uses a MultiProc ID and line ID to target a specific interrupt line to/from a specific processor on a device.

Int status;
armProcId = MultiProc_getId("ARM");

Ipc_start();

/* Register cbFxn with Notify. It will be called when ARM
 * sends event number EVENTID to line #0 on this processor.
 * The argument 0x1010 is passed to the callback function. */
status = Notify_registerEvent(armProcId, 0, EVENTID,
                 (Notify_FnNotifyCbck)cbFxn, 0x1010);
if (status < 0) {
    System_abort("Notify_registerEvent failed\n");
}

The line ID number is typically 0 (zero), but is provided for use on systems that have multiple interrupt lines between processors.

When using Notify_registerEvent(), multiple callbacks may be registered with a single event. If you plan to register only one callback function for an event on this processor, you can call Notify_registerEventSingle() instead of Notify_registerEvent(). Better performance is provided with Notify_registerEventSingle(), and a Notify_E_ALREADYEXISTS status is returned if you try to register a second callback for the same event.

Once an event has been registered, a remote processor may “send” an event by calling Notify_sendEvent(). If the specified event and interrupt line are both enabled, all callback functions registered to the event will be called sequentially.

while (seq < NUMLOOPS) {
  Semaphore_pend(semHandle, BIOS_WAIT_FOREVER);
  /* Semaphore_post is called by callback function*/
  status = Notify_sendEvent(armProcId, 0, EVENTID, seq, TRUE);
}

In this example, the seq variable is sent as the “payload” along with the event. The payload is limited to a fixed size of 32 bits.

Since the fifth argument in the previous example call to Notify_sendEvent() is TRUE, if any previous event to the same event ID was sent, the Notify driver waits for an acknowledgement that the previous event was received.

A specific event may be disabled or enabled using Notify_disableEvent() and Notify_enableEvent(). All notifications on an entire interrupt line may be disabled or restored using Notify_disable() and Notify_restore() calls. Notify_disable() does not alter the state of individual events. Instead, it just disables the ability of the Notify module to receive events on the specified interrupt line.

“Loopback” mode, which is enabled by default, allows notifications to be registered and sent locally. This is accomplished by supplying the processor’s own MultiProc ID to Notify APIs. Line ID 0 (zero) is always used for local notifications. It is important to be aware of some subtle (but important) differences between remote and local notifications:

  • Loopback callback functions execute in the context of the same thread that called Notify_sendEvent(). This is in contrast to callback functions called due to another processor’s sent notification–such “remote” callback functions execute in an ISR context.

  • Loopback callback functions execute with interrupts disabled.

  • Disabling the local interrupt line causes all notifications that are sent to the local processor to be lost. By contrast, a notification sent to an enabled event on a remote processor that has called Notify_disableEvent() results in a pending notification until the disabled processor has called Notify_restore().

  • Local notifications do not support events of different priorities. By contrast, Notify driver implementations may correlate event IDs with varying priorities.

Note

The Notify Module is only supported in SYS/BIOS environments. It is not provided on HLOS’s.

4.4.6.9. Shared Region Module


API Reference Links

srmCfg_Img1

srmRun_Img1

The SharedRegion module is designed to be used in a multi-processor environment where there are memory regions that are shared and accessed across different processors. In an environment with shared memory regions, a common problem is that these shared regions are memory mapped to different address spaces on different processors. This is shown in the following figure. The shared memory region “DDR2” is mapped into Proc0’s local memory space at base address 0x80000000 and Proc1’s local memory space at base address 0x90000000. Therefore, the pointers in “DDR2” need to be translated in order for them to be portable between Proc0 and Proc1. The local memory regions for Proc0 and Proc1 are not shared thus they do not need to be added to the SharedRegion module.

../_images/IpcUG_ipc_2_8_1.png

On systems where address translation is not required, translation is a noop, so performance is not affected.

The SharedRegion module itself does not use any shared memory, because all of its state is stored locally. The APIs use the system gate for thread protection.

This module creates a shared memory region lookup table. The lookup table contains the processor’s view of every shared region in the system. In cases where a processor cannot view a certain shared memory region, that shared memory region should be left invalid for that processor. Each processor has its own lookup table.

Each processor’s view of a particular shared memory region can be determined by the same region ID across all lookup tables. At runtime, this table, along with the shared region pointer, is used to do a quick address translation.

The lookup table contains the following information about each shared region:

  • base. The base address of the region. This may be different on different processors, depending on their addressing schemes.

  • len. The length of the region. This should be should be the same across all processors.

  • ownerProcId. MultiProc ID of the processor that manages this region. If an owner is specified, the owner creates a HeapMemMP instance at runtime. The other cores open the same HeapMemMP instance.

  • isValid. Boolean to specify whether the region is valid (accessible) or not on this processor.

  • cacheEnable. Boolean to specify whether a cache is enabled for the region on the local processor.

  • cacheLineSize. The cache line size for the region. It is crucial that the value specified here be the same on all processors.

  • createHeap. Boolean to specify if a heap is created for the region.

  • name. The name associated with the region.

The maximum number of entries in the lookup table is statically configurable using the SharedRegion.numEntries property. Entries can be added during static configuration or at runtime. When you add or remove an entry in one processor’s table, you must update all of the remaining processors’ tables to keep them consistent. The larger the maximum number of entries, the longer it will take to traverse the lookup table when searching for the index. Therefore, keep the lookup table small for better performance and footprint.

Because each processor stores information about the caching of a shared memory region in the SharedRegion lookup table, other modules can (and do) make use of this caching information to maintain coherency and alignment when using items stored in shared memory.

In order to use the SharedRegion module, the following must be true:

  • The SharedRegion.numEntries property must be the same on all processors.

  • The size of a SharedRegion pointer is 32-bits wide.

  • The SharedRegion lookup table must contain at least 1 entry for address translation to occur.

  • Shared memory regions must not overlap each other from a single processor’s viewpoint.

  • Regions are not allowed to overlap from a single processor’s view.

The SharedRegion with an index of 0 (zero) is used by IPC_start() to create resource management tables for internal use by other IPC modules. Thus SharedRegion “0” must be accessible by all processors. Your applications can also make use of SharedRegion “0”, but must be aware of memory limitations.

4.4.6.9.1. Adding Table Entries Statically

To create a shared region lookup table in the XDCtools configuration, first determine the shared memory regions you plan to use.

Next, specify the maximum number of entries in the lookup table with the SharedRegion.numEntries property. You can specify a value for the SharedRegion.cacheLineSize configuration property, which is the default cache line size if no size is specified for a region. You can also specify the value of the SharedRegion.translate property, which should only be set to false if all shared memory regions have the same base address on all processors. Setting the translate property to false improves performance because no address translation is performed.

For example:

var SharedRegion = xdc.useModule('ti.sdo.ipc.SharedRegion');
SharedRegion.cacheLineSize = 32;
SharedRegion.numEntries = 4;
SharedRegion.translate = true;

Then, use the SharedRegion.setEntryMeta() method in the configuration file to specify the parameters of the entry.

var SHAREDMEM      = 0x0C000000;
var SHAREDMEMSIZE  = 0x00200000;

SharedRegion.setEntryMeta(0,
    { base: SHAREDMEM,
      len: SHAREDMEMSIZE,
      ownerProcId: 0,
      isValid: true,
      cacheEnable: true,
      cacheLineSize: 128,
      createHeap: true,
      name: "internal_shared_mem"
});

If, during static configuration, you don’t know the base address for every processor, you should set the “isValid” field for an entry for which you don’t yet know the base address to “false”. Storing this information will allow it to be completed at runtime.

The following figure shows the configuration of a SharedRegion table for the system in the following figure. This system has seven processors and two shared memory regions. Region 0 (“ext”) is accessible by all processors. Region 1 (“local”) is accessible only by processors 1 to 6.

../_images/IpcUG_ipc_2_8_2.png

If the “createHeap” field is set to true, a HeapMemMP instance is created within the SharedRegion.

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

4.4.6.9.2. Modifying Table Entries Dynamically

In the application’s C code, a shared memory region can be modified in the SharedRegion table by calling SharedRegion_setEntry().

Typically, applications configure SharedRegion table entries statically as described in the previous section, and only modify the table entries dynamically in applications where it is possible for shared memory region availability to change dynamically.

The call to SharedRegion_setEntry() must specify all the fields in the SharedRegion_Entry structure. The index specified must be the same across all processors for the same shared memory region. The index also must be smaller than the maxNumEntries property, otherwise an assert will be triggered.

typedef struct SharedRegion_Entry {
  Ptr base;
  SizeT len;
  UInt16 ownerProcId;
  Bool isValid;
  Bool cacheEnable;
  SizeT cacheLineSize;
  Bool createHeap;
  String name;
} SharedRegion_Entry;

You can use SharedRegion_getEntry() to fill the fields in a SharedRegion_Entry structure. Then, you can modify fields in the structure and call SharedRegion_setEntry() to write the modified fields back to the SharedRegion table.

If you want to reuse an index location in the SharedRegion table, you can call SharedRegion_clear() on all processors to erase the existing entry at that index location.

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

4.4.6.9.3. Using Memory in a Shared Region

Note that the SharedRegion with an index of 0 (zero) is used by IPC_start() to create resource management tables for internal use by the GateMP, NameServer, and Notify modules. Thus SharedRegion “0” must be accessible by all processors.

This example allocates memory from a SharedRegion:

buf = Memory_alloc(SharedRegion_getHeap(0), sizeof(Tester) * COUNT, 128, NULL);

4.4.6.9.4. Getting Information About a Shared Region

The shared region pointer (SRPtr) is a 32-bit portable pointer composed of an ID and offset. The most significant bits of a SRPtr are used for the ID. The ID corresponds to the index of the entry in the lookup table. The offset is the offset from the base of the shared memory region. The maximum number of table entries in the lookup table determines the number of bits to be used for the ID. An increase in the id means the range of the offset would decrease. The ID is limited to 16-bits.

Here is sample code for getting the SRPtr and then getting the real address pointer back.

SharedRegion_SRPtr srptr; UInt16 id;

// Get the id of the address if id is not already known.
id = SharedRegion_getId(addr);

// Get the shared region pointer for the address
srptr = SharedRegion_getSRPtr(addr, id);

// Get the address back from the shared region pointer
addr = SharedRegion_getPtr(srptr);

In addition, you can use the SharedRegion_getIdByName() function to pass the name of a SharedRegion and receive the ID number of the region.

You can use SharedRegion_getHeap() to get a handle to the heap associated with a region using the heap ID.

You can retrieve a specific shared region’s cache configuration from the SharedRegion table by using SharedRegion_isCacheEnabled() and SharedRegion_getCacheLineSize().

Note

In order to account for the case where the contents of the SharedRegion with an index of 0 cannot be known before the cores are booted, it is recommended to place the SharedRegion 0 owner procId in the MultiProc config array before all other cores that will be attaching to the owner of SharedRegion 0. In this way, it is guaranteed that the SharedRegion 0 owner is able to zero-init the memory before other cores start to access it during Ipc_attach. For example, if IPU1 is the SharedRegion 0 owner, the order would look like this in order to put IPU1 first in the array: [“IPU1”,”EVE1”,”DSP1”]

Note

The SharedRegion Module is only supported in SYS/BIOS environments. It is not provided on HLOS’s. For HLOS environments, we suggest using native shared memory APIs when available, for example ION on Android. Other alternatives include CMEM (for Linux) and the QNX-specific SharedMemoryAlloctor, provided in IPC’s qnx/ directory.

4.4.7. TI SDO Utils Package

This page introduces the modules in the ti.sdo.utils package.

The ti.sdo.utils package contains modules that are used as utilities by other modules in the IPC product.

  • List. This module provides a doubly-linked list manager for use by other modules. See List Module.

  • MultiProc. This module stores processor IDs in a centralized location for multi-processor applications. See MultiProc Module.

  • NameServer. This module manages name/value pairs for use by other modules. See NameServer Module.

4.4.7.1. List Module


API Reference Links

lisCfg_Img1

lisRun_Img1

The ti.sdo.utils.List module provides support for creating lists of objects. A List is implemented as a doubly-linked list, so that elements can be inserted or removed from anywhere in the list. Lists do not have a maximum size.

4.4.7.1.1. Basic FIFO Operation of a List

To add a structure to a List, its first field needs to be of type List_Elem. The following example shows a structure that can be added to a List. A List has a “head”, which is the front of the list. List_put() adds elements to the back of the list, and List_get() removes and returns the element at the head of the list. Together, these functions support a FIFO queue.

Run-time example: The following example demonstrates the basic List operations–List_put() and List_get().

/* This structure can be added to a List because the first
 * field is a List_Elem. Declared globally. */
typedef struct Rec {
    List_Elem elem;
    Int data;
} Rec;

...

List_Handle myList;              /* in main() */
Rec r1, r2;
Rec* rp;

r1.data = 100;
r2.data = 200;

/* No parameters are needed to create a List. */
myList = List_create(NULL, NULL);

/* Add r1 and r2 to the back of myList. */
List_put(myList, &(r1.elem));
List_put(myList, &(r2.elem));

/* get the records and print their data */
while ((rp = List_get(myList)) != NULL) {
    System_printf("rec: %d\n", rp->data);
}

The example prints the following:

rec: 100
rec: 200

4.4.7.1.2. Iterating Over a List

The List module also provides several APIs for looping over a List.

List_next() with NULL returns the element at the front of the List (without removing it). List_next() with an elem returns the next elem. NULL is returned when the end of the List is reached.

Similarly, List_prev() with NULL returns the tail. List_prev() with an elem returns the previous elem. NULL is returned when the beginning of the List is reached.

Run-time example: The following example demonstrates one way to iterate over a List once from beginning to end. In this example, “myList” is a List_Handle.

List_Elem   *elem = NULL;
Rec* rp;

...

/* To start the search at the beginning of the List */
rp = NULL;

/* Begin protection against modification of the List */
key = Gate_enterSystem();

while ((elem = List_next(myList, elem)) != NULL) {
     System_printf("rec: %d\n", rp->data);
}
/* End protection against modification of the List */
Gate_leaveSystem(key);

4.4.7.1.3. Inserting and Removing List Elements

Elements can also be inserted or removed from anywhere in the middle of a List using List_insert() and List_remove(). List_insert() inserts an element in front of the specified element. Use List_putHead() to place an element at the front of the List and List_put() to place an element at the end of the List.

List_remove() removes the specified element from whatever List it is in.

Note that List does not provide any APIs for inserting or removing elements at a given index in the List.

Run-time example: The following example demonstrates List_insert() and List_remove():

/* Insert r2 in front of r1 in the List. */
List_insert(myList, &(r1.elem), &(r2.elem));

/* Remove r1 from the List. */
List_remove(myList, &(r1.elem));

Run-time example: The following example treats the List as a LIFO stack using List_putHead() and List_get():

List_Elem  elem[NUMELEM];
List_Elem *tmpElem;

// push onto the top (i.e. head)
for (i = 0; i < NUMELEM; i++) {
    List_putHead(listHandle, &(elem[i]));
}

// remove the buffers in FIFO order.
while((tmpElem = List_get(listHandle)) != NULL) {
    // process tmpElem
}

4.4.7.1.4. Atomic List Operations

Lists are commonly shared across multiple threads in the system, which might lead to concurrent modifications of the List by different threads, which would corrupt the List. List provides several “atomic” APIs that disable interrupts before operating on the List. These APIs are List_get() List_put(), List_putHead(), and List_empty().

An atomic API completes in core functionality without being interrupted. Therefore, atomic APIs are thread-safe. An example is List_put(). Multiple threads can call this API at the same time. The threads do not have to manage the synchronization.

Other APIs–List_prev(), List_next(), List_insert(), and List_remove()–should be protected by the application.

4.4.7.2. MultiProc Module


API Reference Links

mpmCfg_Img1

mpmRun_Img1

4.4.7.2.1. Introduction

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.

/* DSP will get assigned processor id 2. */
var MultiProc = xdc.useModule('ti.sdo.utils.MultiProc');
MultiProc.setConfig("DSP", ["VIDEO", "DSS", "DSP"]);

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.

/* 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"]);

Then, the application code could contain the following setMyID() function to be run at startup:

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);
}

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:

core1ProcId = MultiProc_getId("CORE1");

MultiProc_self() returns the processor ID of the processor running the API. For example:

System_printf("My MultiProc id = %d\n", MultiProc_self());

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:

core0Name = MultiProc_getName(0);

MultiProc_getNumProcessors() evaluates to the total number of processors.

System_printf("Number of processors in the system = %d\n",
              MultiProc_getNumProcessors() );

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:

core0Name = MultiProc_getName(0);

MultiProc_getNumProcessors() evaluates to the total number of processors.

System_printf("Number of processors in the system = %d\n",
              MultiProc_getNumProcessors() );

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

4.4.7.2.2. 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:

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"]);

You could configure the second ‘C6678 device as follows:

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"]);

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.

mpmCfg_Img2 The latest version of the MultiProc module configuration documentation is available in MultiProc page

4.4.7.2.3. 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.

mpmRun_Img2 The latest version of the MultiProc module run-time API documentation is available in MultiProc run-time API

4.4.7.2.4. 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.

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");
    }
}

4.4.7.3. NameServer Module


API Reference Links

nsmCfg_Img1

nsmRun_Img1

The NameServer module manages local name/value pairs. This enables an application and other modules to store and retrieve values based on a name. The NameServer module maintains thread-safety for its APIs. However, NameServer APIs cannot be called from an interrupt (that is, Hwi context). They can be called from Swis (BIOS-only) and Tasks.

This module supports different lengths of values. The NameServer_add() and NameServer_get() functions support variable-length values. The NameServer_addUInt32() function is optimized for UInt32 variables and constants.

The NameServer module currently does not perform any endian or word size conversion. Also there is no asynchronous support at this time.

You can create NameServer instances dynamically. On BIOS, you can also create NameServer instances statically.

4.4.7.3.1. Creating NameServer Instances Statically (BIOS-only)

To create a NameServer instance statically, you can add statements similar to the following to your XDCtools configuration script:

var NameServer = xdc.useModule('ti.sdo.utils.NameServer');

var nameServerParams = new NameServer.Params;
nameServerParams.maxRuntimeEntries = 10;
nameServerParams.maxNameLen = 32;
var nameServer0 = NameServer.create("nameServer0", nameServerParams);

If you want to specify the heap to be used by the NameServer module and a NameServer instance, use configuration statements similar to the following:

var NameServer = xdc.useModule('ti.sdo.utils.NameServer');
var HeapMem = xdc.useModule('ti.sysbios.heaps.HeapMem');

var heapParams = new HeapMem.Params;
heapParams.size = 1024;
var heapMem = HeapMem.create(heapParams);

var nameServerParams = new NameServer.Params;
nameServerParams.tableHeap = heapMem;
var nameServer = NameServer.create("staticNameServer", nameServerParams);

To create a NameServer instance dynamically, initialize a NameServer_Params structure with NameServer_Params_init() and customize the values as needed. The parameters include the following:

  • checkExisting. If true, NameServer check to see if a name already exists in the name/value table before adding it.

  • maxNameLen. Specify the maximum length, in characters, of the name field in the table.

  • maxRuntimeEntries. Specify the maximum number of name/value pairs this table can hold. If you set this parameter to NameServer_ALLOWGROWTH, then NameServer allows dynamic growth of the table.

  • maxValueLen. Specify the maximum length, in MAUs, of the value field in the table.

  • tableHeap. The heap to allocate the name/value table from when allocating dynamically. If this parameter is NULL, the heap used for object allocation is also used here.

After setting parameters, use NameServer_create() to create an instance. Each NameServer instance manages its own name/value table.

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

4.4.7.3.2. Creating and Removing NameServer Instances Dynamically

The following C example creates a NameServer instance dynamically. The instance allows a maximum of 10 runtime entries (instead of using ALLOWGROWTH). This example also specifies where to allocate the memory needed for the tables (instead of using the default).

NameServer_Handle NSHandle;
NameServer_Params params;

NameServer_Params_init(&params);
params.tableHeap = HeapStd_Handle_upCast(myHeap);
params.maxRuntimeEntries = 10;
NSHandle = NameServer_create("myTable", &params);
if (NSHandle == NULL) {
    // manage error
}

This example C code adds and removes entries at run-time:

Ptr key;

key = NameServer_addUInt32(NSHandle, "volume", 5);
if (key == NULL) {
    // manage error
}

NameServer_removeEntry(NSHandle, key);
// or
NameServer_remove(NSHandle, "volume");

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

4.4.7.3.3. Searching NameServer Instances

The following example searches the NameServer instance pointed to by “handle” on the specified processor for a name-value pair with the name stored in nameToFind. It returns the value of the pair to valueBuf.

/* Search NameServer */
status = NameServer_get(NSHandle, nameToFind, valueBuf, sizeof(UInt32), procId);

Using different parameters for different table instances allows you to meet requirements like the following:

  • Size differences. The maxValueLen parameter specifies the maximum length, in MAUs, of the value field in the table. One table could allow long values (for example, > 32 bits), while another table could be used to store integers. This customization enables better memory usage.

  • Performance. Multiple NameServer tables can improve the search time when retrieving a name/value pair.

  • Relax name uniqueness. Names in a specific table must be unique, but the same name can be used in different tables.

When you call NameServer_delete(), the memory for the name/values pairs is freed. You do not need to call NameServer_remove() on the entries before deleting a NameServer instance.

4.4.7.3.4. Other NameServer APIs

In addition to the functions mentioned above, the NameServer module provides the following APIs:

  • NameServer_get() Retrieves the value portion of a local name/value pair from the specified processor.

  • NameServer_getLocal() Retrieves the value portion of a local name/value pair.

  • NameServer_remove() Removes a name/value pair from the table given a name.

  • NameServer_removeEntry() Removes an entry from the table given a pointer to an entry.

NameServer maintains the name/values table in local memory, not in shared memory. However the NameServer module can be used in a multiprocessor system. The module communicates with other processors via NameServer Remote drivers, which are implementations of the INameServerRemote interface. The communication to the other processors is dependent on the Remote drivers implementation. When a remote driver is created, it registers with NameServer via the NameServer_registerRemoteDriver() API.

The NameServer module uses the MultiProc module to identify different processors. Which remote processors to query and the order in which they are queried is determined by the procId array passed to the NameServer_get() function.

4.4.8. Optimizing IPC Applications

4.4.8.1. Compiler and Linker Optimization

You can optimize your application for better performance and code size or to give you more debugging information by selecting different ways of compiling and linking your application. For example, you can do this by linking with versions of the SYS/BIOS and IPC libraries that were compiled differently.

The choices you can make related to compiler and linker optimization are located in the following places:

  • RTSC Build-Profile. You see this field when you are creating a new CCS project or modifying the CCS Build settings. We recommend that you use the “release” setting. The “release” option is preferred even when you are creating and debugging an application; the “debug” option is mainly intended for internal use by Texas Instruments. The “release” option results in a somewhat smaller executable that can still be debugged. This build profile primarily affects how Codec Engine and some device drivers are built.

Note

The “whole_program” and “whole_program_debug” options for the RTSC Build-Profile have been deprecated, and are no longer recommended. The option that provides the most similar result is to set the BIOS.libType configuration property to BIOS.LibType_Custom.

  • CCS Build Configuration. This setting in the CCS Build settings allows you to choose between and customize multiple build configurations. Each configuration can have the compiler and linker settings you choose.

  • BIOS.libType configuration property. You can set this property in XGCONF or by editing the .cfg file in your project. This property lets you select from two pre-compiled versions of the SYS/BIOS and IPC libraries or to have a custom version of the SYS/BIOS and IPC libraries compiled based on the needs of your application. See the table and discussion that follow for more information.

The options for the BIOS.libType configuration property are as follows:

BIOS.libTyp e

Compile Time

Logging

Code Size

Run-Time Performance

Instrumente d (BIOS.LibTy pe_Instrume nted)

Fast

On

Good

Good

Non-Instrum ented (BIOS.LibTy pe_NonInstr umented)

Fast

Off

Better

Better

Custom (BIOS.LibTy pe_Custom)

Fast (slow first time)

As configured

Best

Best

Debug (BIOS.LibTy pe_Debug)

Slower

As configured

  • Instrumented. (default) This option links with pre-built SYS/BIOS (and IPC) libraries that have instrumentation available. All Asserts and Diags settings are checked. Your configuration file can enable or disable various Diags and logging related settings. However, note that the checks to see if Diags are enabled before outputting a Log event are always performed, which has an impact on performance even if you use the ALWAYS_ON or ALWAYS_OFF setting. The resulting code size when using this option may be too large to fit on some targets, such as C28x and MSP430. This option is easy to use and debug and provides a fast build time.

  • Non-Instrumented. This option links with pre-built SYS/BIOS (and IPC) libraries that have instrumentation turned off. No Assert or Diag settings are checked, and logging information is not available at run-time. The checking for Asserts and Diags is compiled out of the libraries, so run-time performance and code size are optimized. Checking of Error_Blocks and handling errors in ways other than logging an event are still supported. This option is easy to use and provides a fast build time.

  • Custom. This option builds custom versions of the SYS/BIOS (and IPC) libraries that contain the modules and APIs that your application needs to access. If you have not used a particular module in your .cfg file or your C code (and it is not required internally by a SYS/BIOS module that is used), that module is not contained in the custom libraries compiled for your application. This option provides the best run-time performance and best code size given the needs of your application. Instrumentation is available to whatever extent your application configures it.

    The first time you build a project with the custom libType, the build will be longer. The custom libraries are stored in the “src” directory of your project. Subsequent builds may be faster; libraries do not need to be rebuilt unless you change one of the few configuration properties that affect the build settings, or you use an additional module that wasn’t already used in the previous configuration.:

Note

If you disable SYS/BIOS Task or Swi scheduling, you must use the “custom” option in order to successfully link your application.

The custom option uses program optimization that removes many initialized constants and small code fragments (often “glue” code) from the final executable image. Such classic optimizations as constant folding and function inlining are used, including across module boundaries. The custom build preserves enough debug information to make it still possible to step through the optimized code in CCS and locate global variables.:

  • Debug. This option is not recommended; it is intended for internal use by Texas Instruments developers.

The following example statements set the BIOS.libType configuration property:

var BIOS = xdc.useModule('ti.sysbios.BIOS');
BIOS.libType = BIOS.LibType_Custom;

If you use the custom option for the BIOS.libType, you can also set the BIOS.customCCOpts property to customize the C compiler command-line options used when compiling the SYS/BIOS libraries. If you want to change this property, it is important to first examine and understand the default command-line options used to compile the SYS/BIOS libraries for your target. You can see the default in XGCONF or by placing the following statement in your configuration script and building the project:

print("customCCOpts =", BIOS.customCCOpts);

Be careful not to cause problems for the SYS/BIOS compilation when you modify this property. For example, the –program_level_compile option is required. (Some –define and –include_path options are used on the compiler command line but are not listed in the customCCOpts definition; these also cannot be removed.) For example, to create a debuggable custom library, you can remove the -o3 option from the BIOS.customCCOpts definition by specifying it with the following string for a C64x+ target:

BIOS.customCCOpts = "-mv64p --abi=eabi -q -mi10 -mo -pdr -pden -pds=238 -pds=880
  -pds1110  --embed_inline_assembly --program_level_compile -g";

4.4.8.2. Optimizing Runtime Performance

You can use one or more of the following techniques to improve the runtime performance of IPC applications:

  • After you have finished debugging an application, you can disable asserts and logging with the following configuration statements:

var Diags = xdc.useModule("xdc.runtime.Diags");
var Defaults = xdc.useModule('xdc.runtime.Defaults');
Defaults.common$.diags_ASSERT = Diags.ALWAYS_OFF;
Defaults.common$.logger = null;
  • If shared memory has the same address on all processors, you can use the following configuration statement to set the SharedRegion.translate property to false. See SharedRegion Module for more about SharedRegion configuration.

SharedRegion.translate = false;
  • Ensure that code, data, and shared data are all placed in cacheable memory. Refer to the SYS/BIOS documentation for information on how to configure a cache. See the TI SYS/BIOS Real-time Operating System v6.x User’s Guide (SPRUEX3) for details.

  • You can reduce contention between multiple processors and multiple threads by creating a new gate for use by a new IPC module instance. Leaving the params.gate property set to NULL causes the default system GateMP instance to be used for context protection. However, in some cases it may be optimal to create a new GateMP instance and supply it to the instance creation. See GateMP Module for more information. For example:

GateMP_Params gateParams;
GateMP_Handle gateHandle;
HeapBufMP_Params heapParams;

GateMP_Params_init(&gateParams);
gateHandle = GateMP_create(&gateParams);

HeapBufMP_Params_init(&heapParams);
heapParams.gate = gateHandle;
  • If a unicache is shared between two cores in shared memory and you expect to share certain IPC instances (such as a GateMP or ListMP) solely between those two cores, you may be able to improve performance by creating a SharedRegion with cache disabled for use between those two cores only. Since region 0 needs to be accessible by all cores on a system, region 1 can be created with a cache line size of 0 and a cacheEnable configuration of FALSE. Any IPC instance created within a SharedRegion inherits the cache settings (the cacheEnabled flag and the cacheLineSize) from this region. Therefore, unnecessary cache operations can be avoided by creating an instance in region 1.

    The following configuration statements create a SharedRegion with the cache disabled (on OMAP4430):

    SharedRegion.setEntryMeta(1, /* Create shared region 1 */
        {   base: 0x86000000,
            len: 0x10000,
            ownerProcId: 0,
            isValid: true,
            cacheEnabled: false, /* Cache operations unneeded */
            cacheLineSize: 0, /* Cache padding unneeded */
            name: "DDR2",
        });


The following C code creates a HeapBufMP instance in this
SharedRegion::
    HeapBufMP_Params heapParams;
    HeapBufMP_Handle heapHandle;

    HeapBufMP_Params_init(&heapParams);
    heapParams.regionId = 1;

    heapHandle = HeapBufMP_create(&heapParams);

This heap can be used by either of the Cortex M3 cores on an
OMAP4430, because they both share a unicache. Do not use this heap
(or anything else belonging to a SharedRegion with caching disabled)
from any other processor if the shared memory belonging to the
SharedRegion is cacheable.

4.4.8.3. Optimizing Notify and MessageQ Latency

By default, IPC applications are configured to use the ti.sdo.ipc.notifyDrivers.NotifyDriverShm Notify driver and the ti.sdo.ipc.transports.TransportShm MessageQ transport. These modules are used by default because they offer backward compatibility with older IPC/SysLink releases. In addition, these modules may offer functionality not supported by their newer, lower-latency counterparts.

If your application does not need functionality provided only by the default Notify drivers or MessageQ transport, you can reduce the latency by switching to alternative MessageQ transports and/or Notify drivers.

4.4.8.4. Choosing and Configuring Notify Drivers

To switch to a different Notify driver, set the Notify.SetupProxy configuration to the family-specific Notify setup module. For example, the following statements configure an application on the DM6446 to use the NotifyDriverCirc driver for that device:

var Notify = xdc.useModule('ti.sdo.ipc.Notify');
Notify.SetupProxy = xdc.useModule('ti.sdo.ipc.family.dm6446.NotifyCircSetup');

IPC provides the following Notify drivers. Each has a corresponding setup module that should be used as the Notify.SetupProxy module.

Modules and Description

Supports Disabling and Enabling Events

Latency

ti.sdo.ipc.notifyDrivers.NotifyDriverShm ti.sdo.ipc.family.<family>.NotifySetup


This shared-memory Notify driver offers room for a single pending notification in shared memory per event.

Yes

Default

ti.sdo.ipc.notifyDrivers.NotifyDriverCirc ti.sdo.ipc.family.<family>.NotifyCircSetup


This shared-memory Notify driver uses a circular buffer to store notifications. Unlike NotifyDriverShm, this driver stores all notifications in the same circular buffer (whose size is configurable).

No

Better than NotifyDriverShm

ti.sdo.ipc.family.ti8 1xx.NotifyDriverM bx ti.sdo.ipc.family.ti8 1xx.NotifyMbxSetup


This TI81xx-only Notify driver uses the hardware mailbox. This driver is not usable by other devices. Notifications are stored in hardware mailbox queues present on TI81xx devices.

No

Better than NotifyDriverCirc and NotifyDriverShm

4.4.8.5. Choosing and Configuring MessageQ Transports

Similarly, to use an alternative MessageQ transport, configure the MessageQ.SetupTransportProxy property to use the transport’s corresponding Transport Setup proxy. For example, to use the TransportShmNotify module, use the following configuration:

var MessageQ = xdc.module('ti.sdo.ipc.MessageQ');
MessageQ.SetupTransportProxy =
      xdc.module('ti.sdo.ipc.transports.TransportShmNotifySetup');

Unlike the Notify setup modules, Transport setup modules are generally not family-specific; most are located in the ti.sdo.ipc.transports package. IPC provides the following transports. Each has a corresponding setup module for use as the MessageQ.SetupTransportProxy module.

Modules and Description

Transport Speed

ti.sdo.ipc.transports.Transpo rtShm ti.sdo.ipc.transports.TransportSh mSetup


This shared-memory MessageQ transport uses ListMP to temporarily queue messages in shared memory before the messages are moved to the destination queue. This transport is typically slowest because of the overhead of queuing messages using a linked list. This is the default MessageQ transport.

Slowest

ti.sdo.ipc.transports.Transpo rtShmCirc ti.sdo.ipc.transports.TransportSh mCircSetup


This shared-memory MessageQ transport uses a fixed-length circular buffer to temporarily queue messages in shared memory before the messages are moved to the destination queue. This transport is typically faster than TransportShm because of the efficiencies gained by using a circular buffer instead of a linked list.

Medium

ti.sdo.ipc.transports.Transpo rtShmNotify ti.sdo.ipc.transports.TransportSh mNotifySetup


This shared-memory MessageQ transport does no buffering before the messages are moved to the destination queue. Because of the lack of buffering, this transport tends to offer lower MessageQ latency than either TransportShm or TransportShm. However, If messages aren’t received quickly enough by the receiver, the sender may spin while waiting for the receiver to move the message to its local queue.

Fastest, but depends on fast processing of messages by receiver

4.4.8.6. Optimizing Shared Memory Usage

You can use one or more of the following techniques to reduce the shared memory footprint of IPC applications:

  • If some connections between processors are not needed, it is not necessary to attach to those cores. To selectively attach between cores, use pair-wise synchronization as described in Ipc Module. Your C code must call Ipc_attach() for processors you want to connect to if you are using pair-wise synchronization. The following configuration statement causes the Ipc module to expect pair-wise synchronization.

Ipc.procSync = Ipc.ProcSync_PAIR;

At run-time, only call Ipc_attach() to a remote processor if one or more of the following conditions is true:

-  The remote processor is the owner of region 0.
-  It is necessary to send Notifications between this processor and
   the remote processor.
-  It is necessary to send MessageQ messages between this processor
   and the remote processor.
-  It is necessary for either the local or remote processor to open a
   module instance using *MODULE*\ \_open() that has been created on
   the other processor.
  • Configure the Ipc.setEntryMeta property to disable components of IPC that are not required. For example, if an application uses Notify but not MessageQ, disabling MessageQ avoids the creation of MessageQ transports during Ipc_attach().

/* To avoid wasting shared mem for MessageQ transports */
for (var i = 0; i < MultiProc.numProcessors; i++) {
    Ipc.setEntryMeta({
        remoteProcId: 1,
        setupMessageQ: false,
    });
}
  • Configure Notify.numEvents to a lower number. The default value of 32 is often significantly more than the total number of Notify events required on a system. See Notify Module for more information.

    For example, a simple MessageQ application may simply use two events (one for NameServer and one for the MessageQ transport). In this case, we can optimize memory use with the following configuration:

var Notify = xdc.useModule('ti.sdo.ipc.Notify');

/* Reduce the total number of supported events from 32 to 2 */
Notify.numEvents = 2;

var NameServerRemoteNotify = xdc.useModule('ti.sdo.ipc.NameServerRemoteNotify');
NameServerRemoteNotify.notifyEventId = 1;

var TransportShm = xdc.useModule('ti.sdo.ipc.transports.TransportShm');
TransportShm.notifyEventId = 0;
  • Reduce the cacheLineSize property of a SharedRegion to reflect the actual size of the cache line. IPC uses the cacheLineSize setting to pad data structures in shared memory. Padding is required so that cache write-back and invalidate operations on data in shared memory do not affect the cache status of adjacent data. The larger the cacheLineSize setting, the more shared memory is used for the sole purpose of padding. Therefore, the cacheLineSize setting should optimally be set to the actual size of the cache line. The default cacheLineSize for SharedRegion is 128. Using the correct size has both performance and size benefits.

    The following example (for C6472) sets the cacheLineSize property to 64 because the shared L2 memory has this cache line size.

SharedRegion.setEntryMeta(0,
    { base: SHAREDMEM,
      len: SHAREDMEMSIZE,
      ownerProcId: 0,
      isValid: true,
      cacheLineSize: 64, /* SL2 cache line size = 64 */
      name: "SL2_RAM",
    });

4.4.8.7. Optimizing Local Memory Usage

If the Custom1 and Custom2 GateMP proxies will never be used, make sure they are both plugged with the ti.sdo.ipc.gates.GateMPSupportNull GateMP delegate. By default, GateMP plugs the Custom1 proxy with the GatePeterson delegate. A considerable amount of local memory is reserved for use by GatePeterson. You can plug the Custom1 proxy with the GateMPSupportNull delegate by adding the following configuration statements to your application:

var GateMP = xdc.useModule('ti.sdo.ipc.GateMP');
GateMP.RemoteCustom1Proxy = xdc.useModule('ti.sdo.ipc.gates.GateMPSupportNull');

4.4.8.8. Optimizing Code Size

This section provides tips and suggestions for minimizing the code size of a SYS/BIOS-based application that uses IPC.

  • For a number of ways to configure SYS/BIOS that reduce code size by using custom built SYS/BIOS libraries and by disabling various features, see Section E.3 of the TI SYS/BIOS Real-time Operating System v6.x User’s Guide (SPRUEX3). In particular, after you have debugged your code, disabling Asserts as follows helps reduce the size of your code.

::

var Defaults = xdc.useModule(‘xdc.runtime.Defaults’); var Diags = xdc.useModule(‘xdc.runtimg.Diags’); Defaults.common$.diags_ASSERT = Diags.ALWAYS_OFF;

  • The NotifyDriverCirc notification driver and the TransportShmNotify or TransportShmCirc MessageQ transports described in Optimizing IPC Applications use less code space than the default Notify driver and MessageQ transport.

  • You can reduce code size by not using the HeapBufMP Heap implementation. Since IPC uses the HeapMemMP implementation internally, using HeapMemMP in your application does not increase the code size. However, you should be aware that, depending on how your application uses heaps, HeapMemMP may lead to problems with heap fragmentation. See Heap*MP Modules for more about Heap implementations.


4.4.9. Rebuilding IPC

This page describes how to rebuild the IPC source code.

The IPC product includes source files and build scripts that allow you to modify the IPC sources and rebuild its libraries. You can do this in order to modify, update, or add functionality. If you edit the IPC source code and/or corresponding build scripts, you must also rebuild IPC in order to create new libraries containing these modifications.

Note that you can cause the BIOS-side IPC (and BIOS) libraries to be rebuilt as part of the application build - this is called a ‘Custom Build’. The custom-built libraries will be stored with other output from executing your config script, and will contain only modules and APIs that your application needs to access. You can cause such a Custom Build to occur using the BIOS.libType config param as follows.

var BIOS = xdc.useModule('ti.sysbios.BIOS');
BIOS.libType = BIOS.LibType_Custom;

Note

This page provides details about rebuilding the IPC source code. We strongly recommend that you copy the IPC installation to a directory with a different name and rebuild that copy, rather than rebuilding the original installation.

4.4.9.1. Build Procedure

The IPC package now comes bundled with Processor SDK RTOS and can be rebuilt using the top-level makefile located in the Processor SDK RTOS folder. The following commands will build the BIOS-side IPC package.

Windows

cd (Processor SDK RTOS folder)
setupenv.bat
gmake ipc_bios

Linux

cd (Processor SDK RTOS folder)
source ./setupenv.sh
make ipc_bios

The IPC package can also be cleaned using the following commands.

Windows

cd (Processor SDK RTOS folder)
    setupenv.bat
    gmake ipc_clean

Linux

cd (Processor SDK RTOS folder)
source ./setupenv.sh
make ipc_clean

The install guides below have been kept here for those using older, legacy versions of IPC outside of Processor SDK.

The various IPC Install Guides describe the mechanics of rebuilding IPC libraries. Please consult the Install Guide appropriate for your environment.

4.4.9.2. Pointing a CCS Project at a Rebuilt IPC

To build your application using the version of IPC you have rebuilt, you must point your project to this rebuilt version by following these steps:

  1. Open CCS and select the application project you want to rebuild.

  2. Right-click on your project and choose Build Properties. If you have a configuration project that is separate from your application project, open the build properties for the configuration project.

  3. In the CCS Build category of the Properties dialog, choose the RTSC tab.

  4. Under the Products and Repositories tab, uncheck all the boxes for IPC. This ensures that no version is selected.

    ../_images/Rebuild_deselect.png
  5. Click the Add button next to the Products and Repositories tab.

  6. Choose Select repository from file-system, and browse to the “packages” directory of the location where you copied and rebuilt IPC. For example, the location may be C:myIpcBuildscustom_ipc_1_22_##-##packages.

    ../_images/Rebuild_addrep.png
  7. Click OK to apply these changes to the project.

  8. You may now rebuild your project using the re-built version of IPC.



4.4.10. IPC Transports

4.4.10.1. Introduction

IPC transports are the underlying configurable data paths over shared memory and hardware resources, which implement the IPC MessageQ APIs. MessageQ provides a common IPC interface between processors within a system containing a single or multiple KeyStone devices. Communication between the processors is enabled through the use of IPC transports. The transports supplied with the IPC component are shared memory based, capable of intra-device communication. Additional transports, utilizing the QMSS and SRIO LLDs, are supplied via Yocto/bitbake for ARMv7 Linux IPC and PROCSDK PDK for SYS/BIOS DSP IPC, enabling intra-SoC and inter-SoC communication, respectively.

The below table gives an overview of the transport offerings, their location, and the communication path they enable.

Transport

MessageQ Interface Type

Location

Communication Route

Enabled Communication Path

Special Considerations

Transport Shm…

MessageQ (priority based)

IPC component - SYS/BIOS subdirect ories

Shared memory

SYS/BIOS DSP to DSP

There are multiple implementations of Transport Shm delivered within the IPC component Please see the IPC documentation provided with the component for more information on these shared memory transport implementions

Transport Rpmsg

MessageQ (priority based)

  • IPC component - ARMv7 Linux subdirect ories

  • IPC component - SYS/BIOS subdirect ories

  • Yocto/ bitbake ti-ipc recipe

Shared memory

  • ARMv7 Linux to/from SYS/BIOS DSP

MessageQ messages sent over Transport Rpmsg traveling from/to ARMv7 user space go through the Linux kernel before reaching the DSP. This provides clean partitioning between user memory and DSP memory. However, Transport Rpmsg is considered a slow path since the user space MessageQ messages must be copied from/to DSP memory by kernel and DSP.

SYS/BIOS DSP Transport Srio

Network

PROCSDK RTOS PDK

SRIO LLD

  • SYS/BIOS DSP to/from SYS/BIOS DSP (intra and inter-device)

  • SYS/BIOS DSP to/from ARMv7 Linux (intra- and inter- device)

TransportSrio can send MessageQ messages to ARMv7 and DSP processors on remote devices in a multiple device system. IPC MultiProc must be configured to be aware of all processors existing on all devices and all devices must be connected over a SRIO interconnect.

The main purpose of TransportSrio is for multi-device communication over MessageQ. The transmission latency is greater for this transport due to the latter capability Therefore it is recommended a shared memory or other LLD-based transport is used for intra-device communication due to their lower latency costs.

ARMv7 Linux Transport Srio

Network

Yocto/bit bake ti-transp ort-srio recipe

SRIO LLD

  • ARMv7 Linux to/from ARMv7 Linux (intra- and inter-device)

  • SYS/BIOS DSP to/from ARMv7 Linux (intra- and inter-device)

TransportSrio can send MessageQ messages to ARMv7 and DSP processors on remote devices in a multiple device system. IPC MultiProc must be configured to be aware of all processors existing on all devices and all devices must be connected over a SRIO interconnect.

The main purpose of TransportSrio is for multi-device communication over MessageQ. The transmission latency is greater for this transport due to the latter capability. Therefore, it is recommended a shared memory or other LLD-based transport is used for intra-device communication due to their lower latency costs.

SYS/BIOS DSP Transport Qmss

Network

QMSS LLD

  • SYS/BIOS DSP to/from SYS/BIOS DSP

  • SYS/BIOS DSP to/from ARMv7 Linux

  • ARMv7 Linux process to process.

  • ARMv7 Linux to/from SYS/BIOS DSP

The IPC component (ARMv7 and SYS/BIOS) is available in PROCSDK RTOS and Linux installations. It will be installed in <PROCSDK RTOS or Linux install root>/ipc_#_##_##_##<version>. Additionally, the IPC component’s ARMv7 source is packaged in a Yocto/bitbake recipe. A user can develop ARMv7 Linux user-space applications with IPC on Keystone I and KeyStone II devices by building the ti-ipc package in Yocto.

4.4.10.2. Modes of Operation

4.4.10.2.1. SYS/BIOS DSP TransportSrio

The following SRIO socket types are supported:

TransportSrio_srioSockType_TYPE_9: In this mode, the SRIO IPC transport will use Type 9 sockets to communicate with other SRIO IPC transport endpoints.

TransportSrio_srioSockType_TYPE_11: In this mode, the SRIO IPC transport will use Type 11 sockets to communicate with other SRIO IPC transport endpoints.

4.4.10.2.2. ARM Linux TransportSrio

The following SRIO socket types are supported:

sock_TYPE_9: In this mode, the ARM Linux SRIO IPC transport will use Type 9 sockets to communicate with other SRIO IPC transport endpoints.

sock_TYPE_11: In this mode, the ARM Linux SRIO IPC transport will use Type 11 sockets to communicate with other SRIO IPC transport endpoints.

4.4.10.2.3. SYS/BIOS DSP TransportQmss

The following QMSS receive queue types are supported:

TransportQmss_queueRcvType_ACCUMULATOR: In this mode, the QMSS accumulator logic is used as the reception mechanism for the QMSS Transport.

TransportQmss_queueRcvType_QPEND: In this mode, a QMSS QPEND (direct interrupt) queue is used as the reception mechanism for the QMSS Transport.

4.4.10.2.4. ARM Linux TransportQmss

A QMSS QPEND queue will always be used as the reception mechanism for the ARM Linux QMSS Transport.

4.4.10.3. Transport Configuration

4.4.10.3.1. SYS/BIOS DSP TransportSrio

4.4.10.3.1.1. SRIO SERDES and Lane Configuration

Configuration of the SRIO SERDES and lanes are required before calling any driver APIs. Examples of the initialization sequences for supported EVMs are provided in the TransportSrio example folder. The examples call the SrioDevice_init() API prior to configuring the transport. SrioDevice_init() reference:

ti/transport/ipc/c66/srio/example/src/device_srio.c

Developers can modify the configurations made in the stock device_srio.c to change SRIO endpoint IDs, routing information, lane rates, loopback modes, etc.

4.4.10.3.1.2. APIs

API reference for application:

#include <ti/transport/ipc/c66/srio/TransportSrio.h>

4.4.10.3.2. ARM Linux TransportSrio

4.4.10.3.2.1. SRIO SERDES and Lane Configuration

Configuration of the SRIO SERDES and lanes are required before calling any driver APIs. Examples of the initialization sequences for supported EVMs are provided in the TransportSrio test folder. The examples call the SrioDevice_init() API prior to configuring the transport. SrioDevice_init() reference from keystone-linux/ipc-transport git repository:

linux/srio/test/[consumer or producer]_device_srio.c

Developers can modify the configurations made in the stock device_srio.c to change SRIO endpoint IDs, routing information, lane rates, loopback modes, etc.

4.4.10.3.2.2. MPM Transport SRIO Configuration

TransportSrio leverages MPM Transport in order to manage configuration of the QMSS, CPPI, and SRIO LLDs. As a result, the transport’s descriptor and descriptor buffer management is pushed to MPM Transport in the ARM Linux version of TransportSrio. The MPM Transport JSON configuration file can be modified to change QMSS descriptor and buffer related parameters.

The MPM Transport JSON configuration file is located in the Linux file system at /etc/mpm/mpm_config.json

4.4.10.3.2.3. APIs

API reference for application from keystone-linux/ipc-transport git repository:

#include <linux/srio/TransportSrio.h>

4.4.10.3.3. SYS/BIOS DSP TransportQmss

4.4.10.3.3.1. APIs

API reference for application:

#include <ti/transport/ipc/c66/qmss/TransportQmss.h>

4.4.10.3.4. ARM Linux TransportQmss

4.4.10.3.4.1. MPM Transport QMSS Configuration

TransportQmss leverages MPM Transport in order to manage configuration of the QMSS and CPPI LLDs. As a result, the transport’s descriptor and descriptor buffer management is pushed to MPM Transport in the ARM Linux version of TransportQmss. The MPM Transport JSON configuration file can be modified to change QMSS descriptor and buffer related parameters.

The MPM Transport JSON configuration file is located in the Linux file system at /etc/mpm/mpm_config.json

4.4.10.3.4.2. APIs

API reference for application from keystone-linux/ipc-transport git repository:

#include <linux/qmss/TransportQmss.h>

4.4.10.4. Source Delivery and Recompilation

4.4.10.4.1. SYS/BIOS DSP TransportSrio

The SYS/BIOS DSP TransportSrio source code and examples are delivered within the PROCSDK RTOS PDK component. DSP TransportSrio can be rebuilt using the environment setup scripts provided with the PDK package. DSP TransportSrio example applications are created as part of the pdkProjectCreate scripts. They can be imported and built the same as PDK LLD example and test CCS projects.

4.4.10.4.1.1. Recompiling on Windows
  1. Open a Windows command terminal and navigate to <pdk_install_dir>/packages.

  2. Run pdksetupenv.bat >pdksetupenv.bat

  3. Navigate to <pdk_install_path>/packages/ti/transport/ipc/c66/srio/

  4. Build the IPC SRIO Transport library >gmake

Issue the following commands if the SRIO transport ever needs to be rebuilt:

>gmake clean >gmake

4.4.10.4.1.2. Recompiling on Linux
  1. Open a Linux bash terminal and navigate to <pdk_install_dir>/packages.

  2. Run pdksetupenv.sh $ source pdksetupenv.sh

  3. Navigate to <pdk_install_path>/packages/ti/transport/ipc/c66/srio/

  4. Build the IPC SRIO Transport library $ make

Issue the following commands if the SRIO transport ever needs to be rebuilt:

$ make clean $ make

4.4.10.4.2. ARM Linux TransportSrio Source Delivery and Recompilation

The ARM Linux TransportSrio source code can be downloaded and built two ways. The transport source code is delivered and built as part of Yocto/bitbake. The source code can also be downloaded and built directly from the GIT repository.

4.4.10.4.2.1. Recompiling Through Yocto/bitbake
  1. Follow the instructions in the Exploring section of the user guide to configure the Yocto build environment. The tisdk-server-rootfs-image does not need to be built. Instead look at the section for building other components

  2. Build the TransportSrio libraries, ipc-transport-srio recipe, and user-space tests, ipc-transport-srio-test recipe: $ MACHINE=k2hk-evm TOOLCHAIN_BRAND=linaro ARAGO_BRAND=mcsdk bitbake ipc-transport-srio $ MACHINE=k2hk-evm TOOLCHAIN_BRAND=linaro ARAGO_BRAND=mcsdk bitbake ipc-transport-srio-test

    Note

    The initial build may take quite some time since the kernel is built as a dependency

    Note

    Building with just the ipc-transport-srio-test recipe will also build the ipc-transport-srio recipe since the test recipe depends on the library recipe.

  3. The built TransportSrio static library will be located in <base_path>/oe-layersetup/build/arago-tmp-external-linaro-toolchain/work/cortexa15hf-vfp-neon-3.8-oe-linux-gnueabi/ipc-transport-srio/<tag-ver_recipe-ver>/packages-split/ipc-transport-srio-staticdev/usr/lib/libTransportSrio.a The built TransportSrio shared library will be located in <base_path>/oe-layersetup/build/arago-tmp-external-linaro-toolchain/work/cortexa15hf-vfp-neon-3.8-oe-linux-gnueabi/ipc-transport-srio/<tag-ver_recipe-ver>/packages-split/ipc-transport-srio/usr/lib/libTransportSrio.so.1.0.0

  4. The ipc-transport-srio-test recipe will build test static and shared library executables for all supported devices. The executables will be located in base_path>/oe-layersetup/build/arago-tmp-external-linaro-toolchain/work/cortexa15hf-vfp-neon-3.8-oe-linux-gnueabi/ipc-transport-srio-test/<tag-ver_recipe-ver>/packages-split/ipc-transport-srio-test/usr/bin/

4.4.10.4.2.2. Recompiling Through GIT Repository

Recompiling through the ARM Linux TransportSrio GIT repository requires that the latest PROCSDK Linux installation. The PROCSDK Linux PDK component and the Linux devkit must be installed. The Linux devkit installation script can be found in <PROCSDK Linux install root>/procsdk_linux_3_XX_YY_ZZ/linux-devkit/

  1. Clone the keystone-linux/ipc-transport repository from git.ti.com $ git clone git://git.ti.com/keystone-linux/ipc-transport.git

  2. Navigate to the PROCSDK Linux installation of pdk_3_XX_YY_ZZ/packages and source armv7setupenv.sh.

    Note

    The armv7setupenv.sh script must be modified to point to the linaro toolchain and installed devkit path

    $ source armv7setupenv.sh

  3. Navigate back to the SRIO transport directory in the ipc-transport GIT repository $ cd <repo_root_path>/ipc-transport/linus/srio

  4. Build the TransportSrio library and user-space test executables: $ make lib $ make tests

  5. The TransportSrio static and shared libraries will be copied directly into the Linux devkit’s /usr/lib folder as long as the devkit install path was setup correctly prior to running the armv7setupenv.sh script

  6. The test executables will be generated in the <base_repo_path>/ipc-transport/bin/<k2 device>/test/ folder. Only the device specified in the armv7setupenv.sh will be built.

Note

Setting the USEDYNAMIC_LIB environment variable to “yes” will generate the shared library test executables

$ export USEDYNAMIC_LIB=yes

4.4.10.4.3. SYS/BIOS DSP TransportQmss Source Delivery and Recompilation

The SYS/BIOS DSP TransportQmss source code and examples are delivered within the PROCSDK RTOS PDK component. DSP TransportQmss can be rebuilt using the environment setup scripts provided with the PDK package. DSP TransportQmss example applications are created as part of the pdkProjectCreate scripts. They can be imported and built the same as PDK LLD example and test CCS projects.

4.4.10.4.3.1. Recompiling on Windows
  1. Open a Windows command terminal and navigate to <pdk_install_dir>/packages.

  2. Run pdksetupenv.bat >pdksetupenv.bat

  3. Navigate to <pdk_install_path>/packages/ti/transport/ipc/c66/qmss/

  4. Build the IPC QMSS Transport library >gmake

Issue the following commands if the QMSS transport ever needs to be rebuilt:

>gmake clean >gmake

4.4.10.4.3.2. Recompiling on Linux
  1. Open a Linux bash terminal and navigate to <pdk_install_dir>/packages.

  2. Run pdksetupenv.sh $ source pdksetupenv.sh

  3. Navigate to <pdk_install_path>/packages/ti/transport/ipc/c66/qmss/

  4. Build the IPC QMSS Transport library $ make

Issue the following commands if the QMSS transport ever needs to be rebuilt:

$ make clean $ make

4.4.10.4.4. ARM Linux TransportQmss Source Delivery and Recompilation

The ARM Linux TransportQmss source code can be downloaded and built two ways. The transport source code is delivered and built as part of Yocto/bitbake. The source code can also be downloaded and built directly from the GIT repository.

4.4.10.4.4.1. Recompiling Through Yocto/bitbake
  1. Follow the instructions in the Exploring section of the user guide to configure the Yocto build environment. The tisdk-server-rootfs-image does not need to be built. Instead look at the section for building other components

  2. Build the TransportQmss libraries, ipc-transport-qmss recipe, and user-space tests, ipc-transport-qmss-test recipe: $ MACHINE=k2hk-evm TOOLCHAIN_BRAND=linaro ARAGO_BRAND=mcsdk bitbake ipc-transport-qmss $ MACHINE=k2hk-evm TOOLCHAIN_BRAND=linaro ARAGO_BRAND=mcsdk bitbake ipc-transport-qmss-test

    Note

    The initial build may take quite some time since the kernel is built as a dependency

    Note

    Building with just the ipc-transport-qmss-test recipe will also build the ipc-transport-qmss recipe since the test recipe depends on the library recipe.

  3. The built TransportQmss static library will be located in <base_path>/oe-layersetup/build/arago-tmp-external-linaro-toolchain/work/cortexa15hf-vfp-neon-3.8-oe-linux-gnueabi/ipc-transport-qmss/<tag-ver_recipe-ver>/packages-split/ipc-transport-qmss-staticdev/usr/lib/libTransportQmss.a The built TransportQmss shared library will be located in <base_path>/oe-layersetup/build/arago-tmp-external-linaro-toolchain/work/cortexa15hf-vfp-neon-3.8-oe-linux-gnueabi/ipc-transport-qmss/<tag-ver_recipe-ver>/packages-split/ipc-transport-qmss/usr/lib/libTransportQmss.so.1.0.0

  4. The ipc-transport-qmss-test recipe will build test static and shared library executables for all supported devices. The executables will be located in base_path>/oe-layersetup/build/arago-tmp-external-linaro-toolchain/work/cortexa15hf-vfp-neon-3.8-oe-linux-gnueabi/ipc-transport-qmss-test/<tag-ver_recipe-ver>/packages-split/ipc-transport-qmss-test/usr/bin/

4.4.10.4.4.2. Recompiling Through GIT Repository

Recompiling through the ARMv7 Linux TransportQmss GIT repository requires that the latest PROCSDK Linux installation. The PROCSDK Linux PDK component and the Linux devkit must be installed. The Linux devkit installation script can be found in <PROCSDK Linux install root>/processor_sdk_linux_3_XX_YY_ZZ/linux-devkit/

  1. Clone the keystone-linux/ipc-transport repository from git.ti.com $ git clone git://git.ti.com/keystone-linux/ipc-transport.git

  2. Navigate to the PROCSDK Linux installation of pdk_3_XX_YY_ZZ/packages and source armv7setupenv.sh.

    Note

    The armv7setupenv.sh script must be modified build for the correct K2 device, and to point to the linaro toolchain and installed devkit path

    $ source armv7setupenv.sh

  3. Navigate back to the QMSS transport directory in the ipc-transport GIT repository $ cd <repo_root_path>/ipc-transport/linus/qmss

  4. Build the TransportQmss library and user-space test executables: $ make lib $ make tests

  5. The TransportQmss static and shared libraries will be copied directly into the Linux devkit’s /usr/lib folder as long as the devkit install path was setup correctly prior to running the armv7setupenv.sh script

  6. The test executables will be generated in the <base_repo_path>/ipc-transport/bin/<k2 device>/test/ folder. Only the device specified in the armv7setupenv.sh will be built.

Note

Setting the USEDYNAMIC_LIB environment variable to “yes” will generate the shared library test executables

$ export USEDYNAMIC_LIB=yes

4.4.10.5. Tests & Examples

4.4.10.5.1. SYS/BIOS DSP TransportSrio

Name

Description

Expected Results

SYS/BIOS DSP TransportSrio Benchmark Example

Example demonstrating intra-SoC, DSP to DSP, transport over SRIO use while also measuring latency and throughput performance. The example consists of multiple iterations where each iteration runs SRIO with different lane configurations. Reference example for developers
Successful completion on two DSP cores for all example iterations.

SYS/BIOS DSP TransportSrio Producer/Consumer Example

Example demonstrates inter-SoC, DSP to DSP, transport over SRIO. The example consists of multiple iterations where each iteration runs SRIO with different lane configurations.

Successful completion for all example iterations on two DSP cores per producer and consumer devices.

4.4.10.5.2. ARM Linux TransportSrio

Name

Description

Expected Results

ARM Linux TransportSrio Producer/Consumer Example

Example demonstrates inter-SoC, ARM Linux to ARM Linux, transport over SRIO. The example consists of multiple iterations where each iteration runs SRIO with different lane configurations.

Successful completion for all example iterations on two Linux processes per producer and consumer devices.

4.4.10.5.3. SYS/BIOS DSP TransportQmss

Name

Description

Expected Results

SYS/BIOS DSP TransportQmss Benchmark Example

Example demonstrating intra-SoC, DSP to DSP, transport over QMSS use while also measuring latency and throughput performance. The example consists of multiple iterations where each iteration runs QMSS with different reception mechanisms (accumulator or QPEND queue). Reference example for developers
Successful completion on two DSP cores for all example iterations.

SYS/BIOS DSP TransportQmss DSP-Side of Heterogeneous Processor Test

Tests inter-SoC, ARM Linux to DSP, transport over QMSS. Reference test for developers

Successful completion on two DSP cores and two Linux processes.

4.4.10.5.4. ARM Linux TransportQmss

Name

Description

Expected Results

ARM Linux TransportQmss Multi-Process Test

Tests Linux inter-process communication via IPC transport over QMSS. Reference test for developers
Successful completion on four Linux processes.

ARM Linux TransportQmss Linux-Side of Heterogeneous Processor Test

Tests inter-SoC, ARM Linux to DSP, transport over QMSS. Reference test for developers

Successful completion on two DSP cores and two Linux processes.

4.4.10.6. Additional References

Document

Location

TransportQmss API Reference Manual

$(TI_PDK_INSTALL_DIR)/packages/ti /transport/ipc/c66/qmss/docs/doxy gen/html/index.html

TransportQmss Release Notes

$(TI_PDK_INSTALL_DIR)/packages/ti /transport/ipc/c66/qmss/docs/Rele aseNotes_TransportQmss.pdf

TransportSrio API Reference Manual

$(TI_PDK_INSTALL_DIR)/packages/ti /transport/ipc/c66/srio/docs/doxy gen/html/index.html

TransportSrio Release Notes

$(TI_PDK_INSTALL_DIR)/packages/ti /transport/ipc/c66/srio/docs/Rele aseNotes_TransportSrio.pdf

4.4.11. IPC Benchmarking

4.4.11.1. RTOS IPC Benchmark applications in SDK

Processor SDK RTOS includes a ping_benchmark_example under processor_sdk_rtos_<platform>_<version>/demos/ipc-examples/ping_benchmark_example. This example shows multiple cores running RTOS communicating with each other through the IPC MessageQ API. The example measures and displays the round trip delay numbers for the IPC. Note the main example sends multiple messages from each of the cores, with all the cores sending messages simultaneously. The example can be built with option to just R5F0 as the originator and all the other cores just respond back with reply.

4.4.11.1.1. Build

The example can be built by running the top level make from the processor_sdk_rtos_<platform>_<version> directory.

4.4.11.1.1.1. Linux Host
cd processor_sdk_rtos_<platform>_<version>
make sdk_ipc_examples_all
4.4.11.1.1.2. Windows Host

cd processor_sdk_rtos_<platform>_<version> setupenv.bat gmake sdk_ipc_examples_all

Once the build commands are executed, the binaries will be in their respective directories as follows.

AM65X:

Host A53: ./demos/ipc-examples/ping_benchmark_example/hosta53/bin/am65xx_evm/release/server_host.xa53fg
R5F-0:    ./demos/ipc-examples/ping_benchmark_example/r5f-0/bin/am65xx_evm/release/server_r5f-0.xer5f
R5F-1:    ./demos/ipc-examples/ping_benchmark_example/r5f-1/bin/am65xx_evm/release/server_r5f-1.xer5f

And also the R5F0 only as sender images are here:

Host A53: ./demos/ipc-examples/ping_benchmark_example/hosta53/bin/am65xx_evm/app_r5f0_only/release/server_host.xa53fg
R5F-0:    ./demos/ipc-examples/ping_benchmark_example/r5f-0/bin/am65xx_evm/app_r5f0_only/release/server_r5f-0.xer5f
R5F-1:    ./demos/ipc-examples/ping_benchmark_example/r5f-1/bin/am65xx_evm/app_r5f0_only/release/server_r5f-1.xer5f

Also the SBL bootable images can be created by using the following commands

Linux Host:

make sdk_ipc_examples_app
make sdk_ipc_examples_app_r5f0_only

Windows host:

::

gmake sdk_ipc_examples_app gmake sdk_ipc_examples_app_r5f0_only

The application images will be located under:

demos/ipc-examples/ping_benchmark_example/output.appimage
demos/ipc-examples/ping_benchmark_example/am65xx_evm/app_r5f0_only/output.appimage

4.4.11.1.2. Run

Pre-requisites: Bootable SD card loaded with SBL.

4.4.11.1.2.1. AM65x

Load the app from this directory into the SD card with filename “app” and load into the EVM to boot. (Note: Make sure the UART port is connected to the terminal to observe the application prints)

On running the application the benchmark data will be displayed on the UART port.

For the main application the benchmark data will be displayed on UART port0:

Overhead:34

Payload Size: 176

Proc id 0, Delta count: 3829025

Proc id 0 to Proc Id 0: Average Roundtrip time is: 19 us

Proc id 1, Delta count: 8920087

Proc id 0 to Proc Id 1: Average Roundtrip time is: 44 us

Proc id 2, Delta count: 7091251

Proc id 0 to Proc Id 2: Average Roundtrip time is: 35 us

AppHost_run: Test Passed

MainHost_appTskFxn: Test Passed

For the R5F0 app only build, the benchmark data will be displayed on UART port1:

Overhead:144

Payload Size: 176

Proc id 0, Delta count: 8460883

Proc id 1 to Proc Id 0: Average Roundtrip time is: 42 us

Proc id 1, Delta count: 1046906

Proc id 1 to Proc Id 1: Average Roundtrip time is: 5 us

Proc id 2, Delta count: 6297313

Proc id 1 to Proc Id 2: Average Roundtrip time is: 31 us

AppR5f_0_run: Test Passed

4.4.11.2. Linux Benchmark test: MessageQBench

The application utilizes the IPC 3.x stack to communicate from the main processor to the slave core via the MessageQ interface. The application measures the time taken to accomplish this. The MessageQBench binaries are included in the Linux file system and they are located in:

Linux Application: /usr/bin/MessageQBench
DSP binary: /lib/firmware/ipc/ti_platforms_[platform]_core0/messageq_single.xe66

The MessageQBench can be built following the instruction described in the below Build section or using high level build in Linux Processor SDK as described in IPC Quick Start Guide

4.4.11.2.1. Build

This section outlines the build process for the MessageQBench application.

4.4.11.2.1.1. Linux

To build the application, follow the Linux IPC Install Guide to configure and build the Linux-side applications for your device. You’ll also need to build the corresponding BIOS side of IPC as indicated in the guide.

4.4.11.2.1.2. QNX

To build the application, follow the QNX IPC Install Guide to configure and build the QNX-side test applications for your device. You’ll also need to build the corresponding BIOS side of IPC as indicated in the guide.

4.4.11.2.1.3. Android

The IPC development sources should be included in the Android Filesystem (AFS) indicated by the manifest provided in your AFS distribution. The sources are located in $(AFS_ROOT)/hardware/ti/ipc directory.

To build the IPC applications, follow the same procedure to build the entire AFS. If you want to specifically build the IPC content, you can issue the following command to build the needed IPC libraries and application from the top your AFS installation (must configure and pick the correct lunch target for your device).

$buildhost make -j messageQApp \
                     messageQBench \
                     messageQMulti \
                     nameServerApp \
                     ping_rpmsg \
                     lad_dra7xx \
                     libmmrpc

To build the corresponding SysBios side of IPC follow the IPC Install Guide.

4.4.11.2.2. Run

This section illustrates the steps to run the MessageQBench application.

4.4.11.2.2.1. Linux/Android

The built IPC libraries and applications must be copied (installed) onto the devices filesystem. The LAD Daemon must be running and application’s corresponding slave-side binary must be loaded via the remoteproc kernel module. The slave-side binary (messageq_single.x<suffix>), is located in the $(IPC_ROOT)/packages/ti/ipc/tests/bin/<device> directory.


Detailed information regarding this can be found in the Installing Tests and IPC Daemons and Drivers section of the IPC Install Guide.

Note

Android users - The Android binaries (lad_dra7xx and messageQBench) will be located in the $(AFS_ROOT)/out/target/product/<device>/system/binary directory. There are also dependent libraries (libtiipc.so, libtiipcutils.so, and libtiipcutils_lad.so) in the $(AFS_ROOT)/out/target/product/<device>/system/lib directory. These files need to be copied to your device filesystem for execution.


Once the LAD daemon is running and the slave core binary has been loaded. You can execute the following to run the application:
target# /usr/bin/MessageQBench

The application will exchange 1000 messages with a payload of 8 by default with an average round trip time per message. The following is the usage parameters for the application.

Usage: ./bin/MessageQBench [<numLoops>] [<payloadSize>] [<ProcId>]
       Defaults: numLoops: 1000; payloadSize: 8, ProcId: 1
4.4.11.2.2.2. QNX

The built IPC libraries and test applications must be copied (installed) onto the devices filesystem. The IPC resource manager must be running and the application’s corresponding slave-side binary must be loaded using the resource manager. The slave-side binary (messageq_single.x<suffix>), is located in the $(IPC_ROOT)/packages/ti/ipc/tests/bin/<device> directory.


Detailed information regarding this can be found in the Installing Tests in QNX, IPC resource manager and Running Test Applications sections of the IPC Install Guide.

Once the IPC resource manager is running and the slave core binary has been loaded. You can execute the following to run the application:

target#  tests/MessageQBench 1000 8 <slave core procId>

The application will exchange 1000 messages with a payload of 8 with the core identified by ‘procId’ and show an average round trip time per message. The following shows the usage parameters for the application.

Syntax:
  MessageQBench [<numLoops>] [<payloadSize>] [<procId>]

4.4.11.3. IPC Performance

The performance numbers in this section are gathered using Processor SDK 5.2 release.

4.4.11.3.1. IPC Average Round Trip Time

The average round trip time is measured using MessageQBench which calculates the time for sending 1000 messages from the main processor to slave core and divided by the number of messages sent (1000).

Average Round Trip Time (ARTT) for K2H/E/G and OMAP-L138 EVMs

PLSDK 5.2

K2H

K2E

K2G

OMAP-L138

CPU Speed

1.2GHz

1.4GHz

1GHz

456MHz

ARTT (us)

115

68

81

904

ARTT @ 1GHz (us)

121

94

81

N/A

Table: IPC Average Round Trip Time (ARTT)

Note

The MessageQBench is a user space application which needs to call into kernel space and involves linux context switch, process priority, and multiple copies during the delivery of a message. Each message may show different latency deviated from the average round trip time.

4.4.11.3.2. Throughput

Throughput benchmark uses a max transmit window of 130 outstanding messages to get the maximum throughput. Beyond the max transit window of 130, there won’t be enough resource to allocate additional messages before the resource is returned by the outstanding messages. In this case, the benchmark application will fail to run. Different payload sizes from 8 bytes to max of 448 bytes are measured for each direction, and the numbers apply to either direction. The throughput are measured with ARM/DSP running at 1 GHz.

Throughput for K2H/E/G and OMAP-L138 EVMs

Payload (Bytes)

K2H (MB/s)

K2E (MB/s)

K2G (MB/s)

OMAP-L138 (MB/s)

8

0.234

0.442

0.265

0.065

16

0.362

0.681

0.333

0.099

32

0.599

1.078

0.548

0.163

64

1.073

1.928

1.101

0.286

128

1.833

3.57

2.116

0.524

256

3.288

6.693

5.855

0.988

448(Max)

5.038

11.056

18.967

1.676

Table: IPC Throughput

4.4.11.3.3. IPC Throughput CPU Utilization

The CPU utilization is measured using max payload size of 448 bytes with transmit window of 130 messages.

K2H (1.2GHz)

K2E (1.4GHz)

K2H (1GHz)

K2E (1GHz)

K2G

OMAP-L138

CPU Utilization

42%

47%

44%

49%

89.5%

100%

Table: IPC Throughput CPU Utilization

4.4.12. IPC 3.x

4.4.12.1. Introduction

This page contains details about the IPC 3.x product, TI’s solution for interprocessor communication between cores on homogenous and heterogeneous devices.

IPC 3.x is an evolution of the IPC product, so it helps to understand the scope of previous generations.

  • The IPC product defines several interfaces to facilitate multiprocessor communication.

  • The IPC 1.x product includes implementations of those interfaces for the SYS/BIOS RTOS. It supports communicating between cores running SYS/BIOS, as well to HLOS processors running SysLink 2.x.

  • The SysLink 2.x provides services to control slave processors (e.g. load, start, stop). It also provides an implementation of the IPC interfaces for High Level OSs (HLOS) like Linux and QNX. SysLink 2.x supports communicating with slave processors running SYS/BIOS and IPC 1.x.

IPC 3.x merges the IPC 1.x and SysLink 2.x products, creating a single product that defines multiprocessor communication APIs and provides implementations for several OS’s, including SYS/BIOS and HLOS’s.

Note

The SysLink name is being retired. The SysLink 2.x product will not be supported on existing devices, and development has stopped and support for new devices will not be added.

4.4.12.2. Changes

The key changes between IPC 1.x/SysLink 2.x and IPC 3.x is the HLOS implementation. This table summarizes the IPC 1.x/SysLink 2.x supported APIs against those provided in IPC 3.x.

Feature

IPC 1.x/SysLink 2.x

IPC 3.x

Slave loading

ProcMgr

Slaves are loaded on demand, currently without a user API

Low-level primitives

Notify, Heap*MP, Gate*MP, SharedRegion, NameServer

Available for BIOS-to-BIOS communication, only GateMP available on HLOS

Messaging

MessageQ

MessageQ

Higher level data passing

RingIO, FrameQ

None, though IPC provides primitives to enable higher level frameworks

4.4.12.2.1. BIOS

For BIOS-to-BIOS communication, the same features available in IPC 1.x are available in IPC 3.x.

4.4.12.2.2. Linux

On Linux, IPC 3.x is built upon services available (and evolving!) in the mainline Linux kernel (3.4+). These core services include remoteproc and rpmsg.

Above those Linux services, a few key services from the IPC API (e.g. MessageQ) are provided in user mode.

4.4.12.2.3. QNX

On QNX, IPC 3.x provides feature parity to Linux. The QNX OS doesn’t inherently provide primitives like Linux’s ‘remoteproc’ and ‘rpmsg’, so IPC 3.x also includes a loader and rpmsg-compatible communication infrastructure. This rpmsg-compatible MessageQ implementation enables the same BIOS-side image to communicate with either Linux or QNX on the HLOS.

4.4.12.3. Development

IPC 3.x development is being managed at https://git.ti.com/ipc.

4.4.12.4. Resource Custom Table

4.4.12.4.1. Introduction

The IPC 3.x product introduced support for loading target images which include a Linux-defined resource table. In IPC 3.00.01, IPC added the ability for users to override the default resource table with their own. This article describes the mechanics involved to do that.

Note

Although the Linux community defined the resource table, the IPC port to QNX also uses the same resource table.

Note

This is an expert technique. Most users do not need to modify the default resource table.

4.4.12.4.2. Config

To indicate you want to provide your own resource table, you need to set the Resource.customTable config parameter true.

/* Override the default resource table with my own */
var Resource = xdc.useModule('ti.ipc.remoteproc.Resource');
Resource.customTable = true;

When Resource.customtable is set to true, IPC will no longer generate a default table, and the user will be able to supply their own table using a specially-named C struct, ti_ipc_remoteproc_ResourceTable.

4.4.12.4.3. C Code

The user-supplied resource table is a C structure named ti_ipc_remoteproc_ResourceTable that needs to be linked into the target executable. The platform-specific default resource tables are provided in IPC’s packages/ti/ip/remoteproc/rsc_table_*.h, and are the recommended place to start from.

After finding the default resource table for your platform, you can copy the entirety of it into your own C file, compile it, and link it into your executable.

Note

When configuring in a TYPE_TRACE element (a trace buffer) you must explicitly declare the symbol as an extern (as below). The trace buffer is generated during config, and unfortunately there is no header to #include that declares the name of this symbol. Also note the size of the trace buffer declared in the resource table must match the size you’ve specified in your .cfg script.

For reference, here is an Vayu compatible resource table derived from packages/ti/ipc/remoteproc/rsc_table_vayu_ipu.h( Disclaimer: This is a reference design only, and it is subject to enhancements, improvements and other changes without notifications).

#include "rsc_types.h"

/* IPU Memory Map */
#define L4_DRA7XX_BASE          0x4A000000

/* L4_CFG & L4_WKUP */
#define L4_PERIPHERAL_L4CFG     (L4_DRA7XX_BASE)
#define IPU_PERIPHERAL_L4CFG    0x6A000000

#define L4_PERIPHERAL_L4PER1    0x48000000
#define IPU_PERIPHERAL_L4PER1   0x68000000

#define L4_PERIPHERAL_L4PER2    0x48400000
#define IPU_PERIPHERAL_L4PER2   0x68400000

#define L4_PERIPHERAL_L4PER3    0x48800000
#define IPU_PERIPHERAL_L4PER3   0x68800000

#define L4_PERIPHERAL_L4EMU     0x54000000
#define IPU_PERIPHERAL_L4EMU    0x74000000

#define L3_PERIPHERAL_PRUSS     0x4B200000
#define IPU_PERIPHERAL_PRUSS    0x6B200000

#define L3_PERIPHERAL_DMM       0x4E000000
#define IPU_PERIPHERAL_DMM      0x6E000000

#define L3_IVAHD_CONFIG         0x5A000000
#define IPU_IVAHD_CONFIG        0x7A000000

#define L3_IVAHD_SL2            0x5B000000
#define IPU_IVAHD_SL2           0x7B000000

#define L3_TILER_MODE_0_1       0x60000000
#define IPU_TILER_MODE_0_1      0xA0000000

#define L3_TILER_MODE_2         0x70000000
#define IPU_TILER_MODE_2        0xB0000000

#define L3_TILER_MODE_3         0x78000000
#define IPU_TILER_MODE_3        0xB8000000

#define L3_OCMC_RAM       0x40300000
#define IPU_OCMC_RAM      0x60300000

#define L3_EMIF_SDRAM     0xA0000000
#define IPU_EMIF_SDRAM        0x10000000

#define IPU_MEM_TEXT            0x0
#define IPU_MEM_DATA            0x80000000

#define IPU_MEM_IOBUFS          0x90000000

#define IPU_MEM_IPC_DATA        0x9F000000
#define IPU_MEM_IPC_VRING       0x60000000
#define IPU_MEM_RPMSG_VRING0    0x60000000
#define IPU_MEM_RPMSG_VRING1    0x60004000
#define IPU_MEM_VRING_BUFS0     0x60040000
#define IPU_MEM_VRING_BUFS1     0x60080000

#define IPU_MEM_IPC_VRING_SIZE  SZ_1M
#define IPU_MEM_IPC_DATA_SIZE   SZ_1M

#if defined(VAYU_IPU_1)
#define IPU_MEM_TEXT_SIZE       (SZ_1M)
#elif defined(VAYU_IPU_2)
#define IPU_MEM_TEXT_SIZE       (SZ_1M * 6)
#endif

#if defined(VAYU_IPU_1)
#define IPU_MEM_DATA_SIZE       (SZ_1M * 5)
#elif defined(VAYU_IPU_2)
#define IPU_MEM_DATA_SIZE       (SZ_1M * 48)
#endif

#define IPU_MEM_IOBUFS_SIZE     (SZ_1M * 90)

/*
 * Assign fixed RAM addresses to facilitate a fixed MMU table.
 * PHYS_MEM_IPC_VRING & PHYS_MEM_IPC_DATA MUST be together.
 */
/* See CMA BASE addresses in Linux side: arch/arm/mach-omap2/remoteproc.c */
#if defined(VAYU_IPU_1)
#define PHYS_MEM_IPC_VRING      0x9D000000
#elif defined (VAYU_IPU_2)
#define PHYS_MEM_IPC_VRING      0x95800000
#endif

#define PHYS_MEM_IOBUFS         0xBA300000

/*
 * Sizes of the virtqueues (expressed in number of buffers supported,
 * and must be power of 2)
 */
#define IPU_RPMSG_VQ0_SIZE      256
#define IPU_RPMSG_VQ1_SIZE      256

/* flip up bits whose indices represent features we support */
#define RPMSG_IPU_C0_FEATURES   1

struct my_resource_table {
    struct resource_table base;

    UInt32 offset[21];  /* Should match 'num' in actual definition */

    /* rpmsg vdev entry */
    struct fw_rsc_vdev rpmsg_vdev;
    struct fw_rsc_vdev_vring rpmsg_vring0;
    struct fw_rsc_vdev_vring rpmsg_vring1;

    /* text carveout entry */
    struct fw_rsc_carveout text_cout;

    /* data carveout entry */
    struct fw_rsc_carveout data_cout;

    /* ipcdata carveout entry */
    struct fw_rsc_carveout ipcdata_cout;

    /* trace entry */
    struct fw_rsc_trace trace;

    /* devmem entry */
    struct fw_rsc_devmem devmem0;

    /* devmem entry */
    struct fw_rsc_devmem devmem1;

    /* devmem entry */
    struct fw_rsc_devmem devmem2;

    /* devmem entry */
    struct fw_rsc_devmem devmem3;

    /* devmem entry */
    struct fw_rsc_devmem devmem4;

    /* devmem entry */
    struct fw_rsc_devmem devmem5;

    /* devmem entry */
    struct fw_rsc_devmem devmem6;

    /* devmem entry */
    struct fw_rsc_devmem devmem7;

    /* devmem entry */
    struct fw_rsc_devmem devmem8;

    /* devmem entry */
    struct fw_rsc_devmem devmem9;

    /* devmem entry */
    struct fw_rsc_devmem devmem10;

    /* devmem entry */
    struct fw_rsc_devmem devmem11;

    /* devmem entry */
    struct fw_rsc_devmem devmem12;

    /* devmem entry */
    struct fw_rsc_devmem devmem13;

    /* devmem entry */
    struct fw_rsc_devmem devmem14;

    /* devmem entry */
    struct fw_rsc_devmem devmem15;
};
extern char ti_trace_SysMin_Module_State_0_outbuf__A;
#define TRACEBUFADDR (UInt32)&ti_trace_SysMin_Module_State_0_outbuf__A

#pragma DATA_SECTION(ti_ipc_remoteproc_ResourceTable, ".resource_table")
#pragma DATA_ALIGN(ti_ipc_remoteproc_ResourceTable, 4096)

struct my_resource_table ti_ipc_remoteproc_ResourceTable = {
    1,      /* we're the first version that implements this */
    21,     /* number of entries in the table */
    0, 0,   /* reserved, must be zero */
    /* offsets to entries */
    {
        offsetof(struct my_resource_table, rpmsg_vdev),
        offsetof(struct my_resource_table, text_cout),
        offsetof(struct my_resource_table, data_cout),
        offsetof(struct my_resource_table, ipcdata_cout),
        offsetof(struct my_resource_table, trace),
        offsetof(struct my_resource_table, devmem0),
        offsetof(struct my_resource_table, devmem1),
        offsetof(struct my_resource_table, devmem2),
        offsetof(struct my_resource_table, devmem3),
        offsetof(struct my_resource_table, devmem4),
        offsetof(struct my_resource_table, devmem5),
        offsetof(struct my_resource_table, devmem6),
        offsetof(struct my_resource_table, devmem7),
        offsetof(struct my_resource_table, devmem8),
        offsetof(struct my_resource_table, devmem9),
        offsetof(struct my_resource_table, devmem10),
        offsetof(struct my_resource_table, devmem11),
        offsetof(struct my_resource_table, devmem12),
        offsetof(struct my_resource_table, devmem13),
        offsetof(struct my_resource_table, devmem14),
        offsetof(struct my_resource_table, devmem15),
    },

    /* rpmsg vdev entry */
    {
        TYPE_VDEV, VIRTIO_ID_RPMSG, 0,
        RPMSG_IPU_C0_FEATURES, 0, 0, 0, 2, { 0, 0 },
        /* no config data */
    },
    /* the two vrings */
    { IPU_MEM_RPMSG_VRING0, 4096, IPU_RPMSG_VQ0_SIZE, 1, 0 },
    { IPU_MEM_RPMSG_VRING1, 4096, IPU_RPMSG_VQ1_SIZE, 2, 0 },

    {
        TYPE_CARVEOUT,
        IPU_MEM_TEXT, 0,
        IPU_MEM_TEXT_SIZE, 0, 0, "IPU_MEM_TEXT",
    },

    {
        TYPE_CARVEOUT,
        IPU_MEM_DATA, 0,
        IPU_MEM_DATA_SIZE, 0, 0, "IPU_MEM_DATA",
    },

    {
        TYPE_CARVEOUT,
        IPU_MEM_IPC_DATA, 0,
        IPU_MEM_IPC_DATA_SIZE, 0, 0, "IPU_MEM_IPC_DATA",
    },

    {
        TYPE_TRACE, TRACEBUFADDR, 0x8000, 0, "trace:sysm3",
    },

    {
        TYPE_DEVMEM,
        IPU_MEM_IPC_VRING, PHYS_MEM_IPC_VRING,
        IPU_MEM_IPC_VRING_SIZE, 0, 0, "IPU_MEM_IPC_VRING",
    },

    {
        TYPE_DEVMEM,
        IPU_MEM_IOBUFS, PHYS_MEM_IOBUFS,
        IPU_MEM_IOBUFS_SIZE, 0, 0, "IPU_MEM_IOBUFS",
    },

    {
        TYPE_DEVMEM,
        IPU_TILER_MODE_0_1, L3_TILER_MODE_0_1,
        SZ_256M, 0, 0, "IPU_TILER_MODE_0_1",
    },

    {
        TYPE_DEVMEM,
        IPU_TILER_MODE_2, L3_TILER_MODE_2,
        SZ_128M, 0, 0, "IPU_TILER_MODE_2",
    },

    {
        TYPE_DEVMEM,
        IPU_TILER_MODE_3, L3_TILER_MODE_3,
        SZ_128M, 0, 0, "IPU_TILER_MODE_3",
    },

    {
        TYPE_DEVMEM,
        IPU_PERIPHERAL_L4CFG, L4_PERIPHERAL_L4CFG,
        SZ_16M, 0, 0, "IPU_PERIPHERAL_L4CFG",
    },

    {
        TYPE_DEVMEM,
        IPU_PERIPHERAL_L4PER1, L4_PERIPHERAL_L4PER1,
        SZ_2M, 0, 0, "IPU_PERIPHERAL_L4PER1",
    },

    {
        TYPE_DEVMEM,
        IPU_PERIPHERAL_L4PER2, L4_PERIPHERAL_L4PER2,
        SZ_4M, 0, 0, "IPU_PERIPHERAL_L4PER2",
    },

    {
        TYPE_DEVMEM,
        IPU_PERIPHERAL_L4PER3, L4_PERIPHERAL_L4PER3,
        SZ_8M, 0, 0, "IPU_PERIPHERAL_L4PER3",
    },

    {
        TYPE_DEVMEM,
        IPU_PERIPHERAL_L4EMU, L4_PERIPHERAL_L4EMU,
        SZ_16M, 0, 0, "IPU_PERIPHERAL_L4EMU",
    },

    {
        TYPE_DEVMEM,
        IPU_PERIPHERAL_PRUSS, L3_PERIPHERAL_PRUSS,
        SZ_1M, 0, 0, "IPU_PERIPHERAL_PRUSS",
    },

    {
        TYPE_DEVMEM,
        IPU_IVAHD_CONFIG, L3_IVAHD_CONFIG,
        SZ_16M, 0, 0, "IPU_IVAHD_CONFIG",
    },

    {
        TYPE_DEVMEM,
        IPU_IVAHD_SL2, L3_IVAHD_SL2,
        SZ_16M, 0, 0, "IPU_IVAHD_SL2",
    },

    {
        TYPE_DEVMEM,
        IPU_PERIPHERAL_DMM, L3_PERIPHERAL_DMM,
        SZ_1M, 0, 0, "IPU_PERIPHERAL_DMM",
    },

    {
        TYPE_DEVMEM,
        IPU_OCMC_RAM, L3_OCMC_RAM,
        SZ_4M, 0, 0, "IPU_OCMC_RAM",
    },

    {
        TYPE_DEVMEM,
        IPU_EMIF_SDRAM, L3_EMIF_SDRAM,
        SZ_256M, 0, 0, "IPU_EMIF_SDRAM",
    },
};

You can find 3 new TYPE_DEVMEM entries added in above resource table.

  • L3_PERIPHERAL_PRUSS

  • IPU_OCMC_RAM

  • IPU_EMIF_SDRAM

All ELF section placements (as well as the VRINGS, which aren’t in an ELF section) are placed in memory allocated from the remoteproc CMA area and are mapped to the virtual address as specified in the TYPE_CARVEOUT entries.

The virtual addresses #defined are:

#define IPU_MEM_TEXT            0x0
#define IPU_MEM_DATA            0x80000000

#define IPU_MEM_IOBUFS          0x90000000

#define IPU_MEM_IPC_DATA        0x9F000000
#define IPU_MEM_IPC_VRING       0x60000000

The followings are fixed physical address to facilitate a fixed MMU table.

#if defined(VAYU_IPU_1)
#define PHYS_MEM_IPC_VRING      0x9D000000
#elif defined (VAYU_IPU_2)
#define PHYS_MEM_IPC_VRING      0x95800000
#endif

These PHYS_MEM values match exactly the physical address specified in the remoteproc CMA area in Linux DTS file:

ipu2_cma_pool: ipu2_cma@95800000 {
        compatible = "shared-dma-pool";
        reg = <0x95800000 0x3800000>;
        reusable;
        status = "okay";
};

ipu1_cma_pool: ipu1_cma@9d000000 {
        compatible = "shared-dma-pool";
        reg = <0x9d000000 0x2000000>;
        reusable;
        status = "okay";
};

The 1st entry in the resource table is:

/* rpmsg vdev entry */
{
    TYPE_VDEV, VIRTIO_ID_RPMSG, 0,
    RPMSG_IPU_C0_FEATURES, 0, 0, 0, 2, { 0, 0 },
    /* no config data */
},
/* the two vrings */
{ IPU_MEM_RPMSG_VRING0, 4096, IPU_RPMSG_VQ0_SIZE, 1, 0 },
{ IPU_MEM_RPMSG_VRING1, 4096, IPU_RPMSG_VQ1_SIZE, 2, 0 },

This tells remoteproc to allocate the vrings and vring buffers, and the remoteproc CMA area is used for this.

The 2nd-4th entries are:

{
    TYPE_CARVEOUT,
    IPU_MEM_TEXT, 0,
    IPU_MEM_TEXT_SIZE, 0, 0, "IPU_MEM_TEXT",
},
{
    TYPE_CARVEOUT,
    IPU_MEM_DATA, 0,
    IPU_MEM_DATA_SIZE, 0, 0, "IPU_MEM_DATA",
},
{
    TYPE_CARVEOUT,
    IPU_MEM_IPC_DATA, 0,
    IPU_MEM_IPC_DATA_SIZE, 0, 0, "IPU_MEM_IPC_DATA",
},

These carveouts tell remoteproc to allocate memory from its CMA area and map the allocated physical address to the virtual address specified in the carveout (the 1st field after TYPE_CARVEOUT), in the IPU’s MMU (“iommu” in Linux kernel parlance). These are for the ELF sections that are placed (mapped) in those virtual address.

The TYPE_TRACE entry tells remoteproc where the remote executable’s trace buffer is, using its C symbol.

The TYPE_DEVMEM entries are virtual <-> physical mappings. remoteproc just creates an IPU MMU mapping for the entry. The 1st TYPE_DEVMEM entry corresponds to the vrings and creates the IPU MMU mapping needed to access them from the IPU core:

{
    TYPE_DEVMEM,
    IPU_MEM_IPC_VRING, PHYS_MEM_IPC_VRING,
    IPU_MEM_IPC_VRING_SIZE, 0, 0, "IPU_MEM_IPC_VRING",
},

4.4.12.4.4. New TYPE_DEVMEM entry

To add a new TYPE_DEVMEM entry, for example, to access PRU-ICSS from IPU

    1. specify the physical address of PRU-ICSS and its virutal address.

#define L3_PERIPHERAL_PRUSS     0x4B200000
#define IPU_PERIPHERAL_PRUSS    0x6B200000
    1. increase size of offset[X] array in struct my_resource_table.

  • 3. add new struct fw_rsc_devmem devmemY entry in struct my_resource_table.

    1. increase number of entries in ti_ipc_remoteproc_ResourceTable.

  • 5. add a offsetof(struct my_resource_table, devmemY) in ti_ipc_remoteproc_ResourceTable.

    1. add actual entry in ti_ipc_remoteproc_ResourceTable.

{
    TYPE_DEVMEM,
    IPU_PERIPHERAL_PRUSS, L3_PERIPHERAL_PRUSS,
    SZ_1M, 0, 0, "IPU_PERIPHERAL_PRUSS",
},

Note, when MMU is enabled, even though it is a one-to-one mapping, you need to have an entry mapped in the MMU for that, e.g. to access PRU-ICSS from DSP, you must add the following entry in rsc_table_vayu_dsp.h

#define L3_PRU_ICSS             0x4B200000
#define DSP_PRU_ICSS            0x4B200000

The MMU pagetable can be dumped through debugfs - “cat /sys/kernel/debug/omap_iommu/<iommu_name>/pagetable”

4.4.12.5. Resource Usage

4.4.12.5.1. Overview

The main aim of this page is to capture details about the different resources used in the IPC drivers.

Note

This page is under construction

4.4.12.5.2. Interrupt map usage

4.4.12.5.2.1. AM57xx Interrupt resources

The following table captures the interrupt resources used by IPC associated with different cores for the AM57xx platform. If customer wants to use any of the interrupt resources, it is better to make sure any of the resources listed here are not used.

A15 Interrupt Mapping

Index

Addr

Value

Name

Source

Description

134

0x4A002B44

127

EVE1_IRQ_MBX0_USER3

EVE1_MAILBOX0

Eve1 Mailbox 0 user 3 interrupt

135

0x4A002B44

128

EVE2_IRQ_MBX0_USER3

EVE2_MAILBOX0

Eve2 Mailbox 0 user 3 interrupt

136

0x4A002B48

129

MAILBOX5_IRQ_USER2

MAILBOX5

Mailbox 5 user 2 interrupt

137

0x4A002B48

130

EVE3_IRQ_MBX0_USER3

EVE3_MAILBOX0

Eve 3 Mailbox 0 user 3 interrupt

138

0x4A002B4C

131

EVE4_IRQ_MBX0_USER3

EVE4_MAILBOX0

Eve4 Mailbox 0 user 3 interrupt

141

0x4A002B50

134

MAILBOX6_IRQ_USER2

MAILBOX6

Mailbox 6 user 2 interrupt

DSP1 Interrupt Mapping

Index

Addr

Value

Name

Source

Description

55

0x4A002974

24

EVE1_IRQ_MBX0_USER1

EVE1_MAILBOX0

Eve1 Mailbox 0 user 1 interrupt

56

0x4A002978

25

EVE2_IRQ_MBX0_USER1

EVE2_MAILBOX0

Eve2 Mailbox 0 user 1 interrupt

57

0x4A002978

26

MAILBOX5_IRQ_USER0

MAILBOX5

Mailbox 5 user 0 interrupt

58

0x4A00297C

27

EVE3_IRQ_MBX0_USER1

EVE3_MAILBOX0

Eve 3 Mailbox 0 user 1 interrupt

59

0x4A00297C

28

EVE4_IRQ_MBX0_USER1

EVE4_MAILBOX0i

Eve4 Mailbox 0 user 1 interrupt

60

0x4A002980

29

MAILBOX7_IRQ_USER0

MAILBOX7

Mailbox 7 user 0 interrupt

61

0x4A002980

30

MAILBOX8_IRQ_USER0

MAILBOX8

Mailbox 8 user 0 interrupt

DSP2 Interrupt Mapping

Index

Addr

Value

Name

Source

Description

55

0x4A0029F4

24

EVE1_IRQ_MBX1_USER1

EVE1_MAILBOX1

Eve1 Mailbox 1 user 1 interrupt

56

0x4A0029F8

25

EVE2_IRQ_MBX1_USER1

EVE2_MAILBOX1

Eve2 Mailbox 1 user 1 interrupt

57

0x4A0029F8

26

MAILBOX6_IRQ_USER0

MAILBOX6

Mailbox 6 user 0 interrupt

58

0x4A0029FC

27

EVE3_IRQ_MLBX1_USER1

EVE3_MAILBOX1

Eve 3 Mailbox 1 user 1 interrupt

59

0x4A0029FC

28

EVE4_IRQ_MBX1_USER1

EVE4_MAILBOX1

Eve4 Mailbox 1 user 1 interrupt

60

0x4A002A00

29

MAILBOX7_IRQ_USER1

MAILBOX7

Mailbox 7 user 1 interrupt

61

0x4A002A00

30

MAILBOX8_IRQ_USER1

MAILBOX8

Mailbox 8 user 1 interrupt

IPU1 Interrupt Mapping

Index

Addr

Value

Name

Source

Description

64

0x4A002830

42

EVE1_IRQ_MBX0_USER2

EVE1_MAILBOX0

Eve1 Mailbox 0 user 2 interrupt

65

0x4A002834

43

EVE2_IRQ_MBX0_USER2

EVE2_MAILBOX0

Eve2 Mailbox 0 user 2 interrupt

66

0x4A002834

44

MAILBOX5_IRQ_USER1

MAILBOX5

Mailbox 5 user 1 interrupt

67

0x4A002838

45

EVE3_IRQ_MBX0_USER2

EVE3_MAILBOX0

Eve 3 Mailbox 0 user 2 interrupt

68

0x4A002838

46

EVE4_IRQ_MBX0_USER2

EVE4_MAILBOX0

Eve4 Mailbox 0 user 2 interrupt

69

0x4A00283C

47

MAILBOX7_IRQ_USER2

MAILBOX7

Mailbox 7 user 2 interrupt

70

0x4A00283C

48

MAILBOX8_IRQ_USER2

MAILBOX8

Mailbox 8 user 2 interrupt

71

0x4A002840

49

EVE1_IRQ_MBX1_USER3

EVE1_MAILBOX1

Eve1 Mailbox 1 user 3 interrupt

72

0x4A002840

50

EVE2_IRQ_MBX1_USER3

EVE2_MAILBOX1

Eve2 Mailbox 1 user 3 interrupt

73

0x4A002844

51

MAILBOX5_IRQ_USER3

MAILBOX5

Mailbox 5 user 3 interrupt

74

0x4A002844

52

EVE3_IRQ_MBX1_USER3

EVE3_MAILBOX1

Eve 3 Mailbox 1 user 3 interrupt

75

0x4A002848

53

EVE4_IRQ_MBX1_USER3

EVE4_MAILBOX1

Eve4 Mailbox 1 user 3 interrupt

76

0x4A002848

54

MAILBOX8_IRQ_USER2

MAILBOX8

Mailbox 8 user 2 interrupt


IPU2 Interrupt Mapping

Index

Addr

Value

Name

Source

Description

64

0x4A0028A4

42

EVE1_IRQ_MBX1_USER2

EVE1_MAILBOX1

Eve1 Mailbox 1 user 2 interrupt

65

0x4A0028A8

43

EVE2_IRQ_MBX1_USER2

EVE2_MAILBOX1

Eve2 Mailbox 1 user 2 interrupt

66

0x4A0028A8

44

MAILBOX6_IRQ_USER1

MAILBOX6

Mailbox 6 user 1 interrupt

67

0x4A0028AC

45

EVE3_IRQ_MBX1_USER2

EVE3_MAILBOX1

Eve 3 Mailbox 1 user 2 interrupt

68

0x4A0028AC

46

EVE4_IRQ_MBX1_USER2

EVE4_MAILBOX1

Eve4 Mailbox 1 user 2 interrupt

69

0x4A0028B0

47

MAILBOX7_IRQ_USER3

MAILBOX7

Mailbox 7 user 3 interrupt

70

0x4A0028B0

48

MAILBOX8_IRQ_USER3

MAILBOX8

Mailbox 8 user 3 interrupt

71

0x4A0028B4

49

EVE1_IRQ_MBX1_USER3

EVE1_MAILBOX1

Eve1 Mailbox 1 user 3 interrupt

72

0x4A0028B4

50

EVE2_IRQ_MBX1_USER3

EVE2_MAILBOX1

Eve2 Mailbox 1 user 3 interrupt

73

0x4A0028B8

51

MAILBOX6_IRQ_USER3

MAILBOX6

Mailbox 6 user 3 interrupt

74

0x4A0028B8

52

EVE3_IRQ_MBX1_USER3

EVE3_MAILBOX1

Eve 3 Mailbox 1 user 3 interrupt

75

0x4A0028BC

53

EVE4_IRQ_MBX1_USER3

EVE4_MAILBOX1

Eve4 Mailbox 1 user 3 interrupt

76

0x4A0028BC

54

MAILBOX8_IRQ_USER3

MAILBOX8

Mailbox 8 user 3 interrupt

4.4.13. IPC 3.x vs IPC LLD

IPC 3.x APIs provide High level IPC APIs that scale across all TI platforms.

Instead of the IPC 3.x APIs, in certain cases customers can choose to use the IPC Low level Driver (IPC LLD).

In general, the IPC LLD can be used, if the customer application falls under the following categories.

  1. Non-RTOS bare metal applications (Note: IPC 3.x is closely coupled with SYSBIOS and XDC tools)

  2. Applications does not need the high level API abstraction and all the features (like GateMP etc) and would like to call the low level driver to just establish basic communication between the cores.

  3. Application needs the same RPMSG/Virtio transport mechanism to communicate with a core running linux and other cores running other operating systems.

Please refer to the following page for details on the IPC LLD: IPC LLD for AM65x/J721E

4.4.14. IPC FAQ

4.4.14.1. I have a question that’s not answered here, what now?

  • There are lots of knowledgeable experts on the TI E2E Support Forums - posts welcome!

  • Have you tried Google? IPC’s been around a while, and Google has lots of details about it.

4.4.14.2. Does IPC 3.x support SMP BIOS?

SMP BIOS is described here. It is currently only available for dual-core ARM M-class devices, sometimes called “Benelli” or “Ducati”.

Specifically where IPC 3.x is concerned, SMP BIOS is only applicable to the following cores/devices:

  • IPU on OMAP54XX

  • IPU1 (and IPU2, if present) on DRA7XX (e.g. Vayu)

The answer to this question depends on whether you are developing an HLOS-based or BIOS-based system.

HLOS-Based (e.g. Linux, QNX, Android on the Cortex-A)

BIOS-Based (i.e. BIOS on the Cortex-A)

IPC 3.x only supports SMP BIOS on the IPU(s). In an HLOS-based system, the IPU(s) are loaded and managed as slaves (not peers).

When managing the IPU as a slave, issues like power management, loading/resetting, and error recovery are key features. Because the 2 cores in a single IPU can’t easily be seen as truly independent (e.g. they share a power domain), treating them as one slave (running SMP BIOS) is simpler and more natural.

IPC 3.x supports either SMP BIOS or non-SMP BIOS. Note that specifically on DRA7XX’s IPU2, IPC 3.x only supports SMP BIOS (to reduce the number of core-to-core permutations and thus reduce the number of mailboxes required). In a BIOS-based environment, the IPU(s) are treated as peers (not slaves), and as such some of the concerns about power management, loading, error recovery are no longer an issue.

Note that the MultiProc configuration used on all cores reflects your SMP-or-not-SMP decision. When using non-SMP BIOS, you must include “IPU1-0” and “IPU1-1” (rather than “IPU1” used when running SMP BIOS) in your MultiProc configuration. See the ex11_ping example provided with the IPC product for an example that supports either “IPU1” or “IPU1-0 and IPU1-1”.

Finally, there are a few notable benefits to using SMP BIOS. Many are described in the SMP/BIOS article, but two are highlighted here:

  • Free load balancing - SMP BIOS schedules tasks to run on the less-loaded core to balance IPU usage. Without SMP BIOS, when one of the cores became heavily loaded, users had to repartition the resources; often this meant moving tasks/code from one core to the other, which can affect memory maps, build/config scripts, etc.

  • Simpler system integration - there’s one fewer core to manage the memory map and resource allocation for. Without SMP BIOS, the single unicache needed to understand AMMU and cache needs for both cores, and often confusing and error-prone integration issue.

4.4.14.3. Build Questions

4.4.14.3.1. BIOS-side Build

4.4.14.3.1.1. How do I change the Memory Map?

Standard techniques for changing the memory map of any BIOS executable generally apply (e.g. creating your own custom platform, creating an instance of an existing platform, etc). However when building an executable which is loaded by an HLOS, you also need to ensure the resource table is aligned with the memory map. The IPC Resource customTable article describes how to override the default resource table, which among other things, includes the memory map.

Obviously be sure to also ensure changes to the memory map don’t collide with memory usage on other cores.

4.4.14.3.1.2. Why are the peripherals mapped to 0x6XXX:XXXX virtual memory for IPU (Cortex-M4) test images?

The ARM Cortex-M4 memory map includes a bit-banding region of memory from 0x4000:0000 to 0x400F:FFFF and 0x4200:0000 to 0x43FF:FFFF. Here is a Cortex-M4 memory map picture from ARM:

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0553a/CHDBIJJE.html

Many Vayu components running on the IPUs, including IPC, must access peripherals physically located in this bit-banding region. As a result, these accesses must be performed indirectly using a virtual memory address, mapped using the IPU’s AMMU.

Many of the components aligned on mapping this memory using one Large AMMU page that maps 512M of physical memory beginning at 0x4000:0000 to virtual memory beginning at 0x6000:0000. Then the components (by default) access the peripherals using the 0x6XXX:XXXX address space.

  • IPC specifics:

IPC follows the convention of, by default, accessing memory physically located at 0x4XXX:XXXX using virtual memory at 0x6XXX:XXXX. You can see an example of this here - note the mailbox addresses configured here are in the 06XXX:XXXX range:

http://git.ti.com/cgit/cgit.cgi/ipc/ipcdev.git/tree/packages/ti/sdo/ipc/family/vayu/InterruptIpu.xs

In that same file, you can see that these addresses are configurable, and the default 0x6XXX:XXXX addresses are only used if other addresses haven’t already been configured by the system integrator (e.g. in a .cfg script). Users can override these default mailbox addresses using the ti.sdo.ipc.family.vayu.InterruptIpu module’s mailboxBaseAddr[] array, documented here:

https://software-dl.ti.com/dsps/dsps_public_sw/sdo_sb/targetcontent/ipc/latest/docs/cdoc/ti/sdo/ipc/family/vayu/InterruptIpu.html#metamailbox.Base.Addr

4.4.14.3.2. Linux Build

For details on the Linux build process see the IPC Install Guide for Linux.

4.4.14.3.2.1. Try Cleaning First

Sometimes the generated autotools makefiles can get confused, especially if your often switching from one platform to another, between static and dynamic libraries, etc. And sometimes simply cleaning and retrying can help.

“make clean” does a ‘light’ cleaning, leaving the autotools-generated config files so you can just retry ‘make’:

# make clean
#
# make

“make distclean” does a more aggressive clean, requiring you to re-run the config before running ‘make’:

# make distclean
#
# make -f ipc-linux.mak
# make

4.4.14.3.3. QNX Build

For details on the QNX build process see the IPC Install Guide for QNX.

4.4.14.3.3.1. How do I change the Memory Map?

For the standpoint of the QNX OS, it owns all external memory by default. When the ‘ipc’ resource manager is launched and the slave cores are loaded, the resource table in each slave executable is read and interpreted. All resource entries of type CARVEOUT are automatically allocated from QNX-owned memory. Similarly, the first DEVMEM entry always corresponds to the vring memory used by MessageQ and rpmsg_rpc, and is also automatically allocated from QNX-owned memory, regardless of which physical memory address is specified in the resource table entry. If any other DEVMEM resource entry resides in external memory, QNX would need to be told to not use that particular range. This is usually done using the ‘-r’ flag in the startup command in the ‘build’ file of the QNX IFS (refer to your QNX BSP documentation for more details):

startup-dra74x-vayu-evm -r0xBA300000,0x5A00000 -v -n852,668 -W -G

For example, the above ‘-r’ flag would reserve 0x5A00000 bytes starting at physical address 0xBA300000 from being used by the QNX OS on the DRA74x. This technique is often used for the memory range used by shmemallocator (resource table entry PHYS_MEM_IOBUFS) and/or by SharedRegions. After modifying the startup flags, rebuild the QNX IFS and use the new one to boot your board.

4.4.14.3.3.2. How do I configure the memory used by shmemallocator?

shmemallocator stands for “Shared Memory Allocator”. It is a utility that is built alongside the core IPC driver whenever the IPC product is built. The user can optionally use this utility to allocate contiguous shared memory for exchanging data between the host and the slave cores. The memory it allocates from is defined in <IPC_INSTALL_DIR>qnxsrcipc3x_devsharedmemallocatorresmgrSharedMemoryAllocator.c thru some #define statements:

#define SH_MEM_BLOCK1_START 0xBA300000
#define SH_MEM_BLOCK1_SIZE  0x5A00000

Typically, even though the code base (as of IPC 3.35) supports up to two blocks, only block 1 is used when allocating memory using the function SHM_alloc(), as shown in the IPC examples. When adjusting this block, It is important that the memory matches a corresponding DEVMEM entry (PHYS_MEM_IOBUFS) in the resource table so that this memory is mapped to the slave core’s MMU, and that the memory is reserved in the QNX IFS startup command as per “How do I change the Memory Map?”.

4.4.14.3.4. Runtime Troubleshooting

4.4.14.3.4.1. Linux spurious “msg received with no recepient”

When a Linux-loaded slave communicates to the Linux host, it sends a msg using an rpmsg “channel”. Sometimes there’s no one listening on that channel, for example if there are no host-side IPC applications running. When a host-side application calls Ipc_start(), these channels start to be monitored, but until then, no one’s listening.

When, for example, a slave does a NameServer lookup, this query is often sent to all cores in the system. If there’s no host-side application running, there’s no one listening for these msgs, and when the underlying rpmsg driver detects an arriving msg that no one’s listening for, it issues a dev_warn() call to report this, drops the message, and moves along.

These warnings are often benign, and sometimes just reflect chatter between the various slaves. Perhaps the dev_warn() should be changed to a dev_dbg().

And yes, that’s a typo - ‘recepient’ is really spelled ‘recipient’. Submit a patch and get your initials into the Linux kernel.  :)

4.4.14.3.4.2. Disabling auto-recovery

When the HLOS detects a fault on a slave (e.g. an MMU fault), it reloads and restarts the slave. Sometimes, especially when debugging slave-side errors, it’s useful to disable this feature so the state of the crashed slave isn’t reset and can be examined.

4.4.14.3.4.2.1. Linux

You can disable recovery of a given remote processor using remoteproc’s debugfs features. First, find the slave you want to disable (e.g. cat /debug/remoteproc/remoteproc0/name) and then echo “disabled” to the “recovery” file.

For example, if “remoteproc0” is the core you want to disable:

target# echo "disabled" > /debug/remoteproc/remoteproc0/recovery
4.4.14.3.4.2.2. QNX

Starting in IPC 3.22, you can simply throw the ‘-d’ option when launching the ipc resource manager to disable the recovery mechanism.

If you are using IPC 3.21 or older, there are a couple of tricks to prevent the slaves from being automatically reloaded and restarted:

  • If you have the QNX Momentics IDE installed, you can set a breakpoint on the entry of ipc_recover() in <IPC_INSTALL_DIR>/qnx/src/ipc3x_dev/ti/syslink/build/Qnx/resmgr/syslink_main.c. This function is the callback that is invoked when an MMU fault occurs to perform the reload. Halting the host at that point would prevent it from reloading the DSP, and gives you time to inspect the DSP memory. This is nice because it does not necessitate a rebuild of IPC.

  • If you do not have access to the QNX debugger, you can rebuild IPC after commenting out the lines in ipc_recover(), and use the modified IPC resource manager binary for debugging. That would prevent it from reloading the DSP.

4.4.14.4. When should Ipc_start() be called?

On a slave core, Ipc_start() should be called only if there is a need to initialize the SharedRegion module and/or to perform slave-to-slave IPC communication. In other words, if communication strictly happens between host and slave(s), and SharedRegions are not defined, then there is no need to call Ipc_start() on the slave. In fact, if SR0 (SharedRegion 0) is not defined on a slave core, do not call Ipc_start() on that slave core. The call would fail when it tries to look for SR0.

For an example that shows a call to Ipc_start() on the slave cores, refer to the ex41_forwardmsg example, which showcases both host-to-slave and slave-to-slave communication paths. Specifically, this line in Dsp1.cfg and Ipu1.cfg adds a call to Ipc_start() within BIOS_start():

BIOS.addUserStartupFunction('&IpcMgr_callIpcStart');

On the host core, Ipc_start() always needs to be called to initialize IPC. Given host-side IPC code does not use SharedRegion, the function is strictly used to perform initialization for other IPC modules.

4.4.14.5. Linux Ipc_start() Failures

4.4.14.5.1. LAD reports NameServer_setup: connect failed: 22, Invalid argument

Internally, LAD tries to connect to sockets created by the rpmsg_proto kernel driver. This connection can fail for several reasons, so this is a common “general” error code. Some examples that generate this error condition are below.

  • Failure to load the slave image. If the a slave executable fails to load, the sockets to communicate with it are not created. Make sure the slaves you need to communicate with are already loaded.

  • Failure to provide the correct KERNEL_INSTALL_DIR when building the user-side IPC libraries. Some customers reported this error when using Linux 3.9+ kernels (where the AF_MAX value in socket.h has increased). This created a mismatch in what AF_RPMSG was set to in user libs and the kernel, and the connect call failed. The solution was to require users set KERNEL_INSTALL_DIR when building the user-space libraries so IPC can interrogate the kernel version and set AF_RPMSG appropriately.

  • Failure to configure the slave image correctly. Internally, the slave-side MessageQ transport (TransportRpmsg) broadcasts its availability to the Linux host. If this broadcast doesn’t occur (e.g., b/c the slave wasn’t configured with TransportRpmsg), the socket connection will fail with error 22.

  • General slave-to-host interrupt failure. Some devices (e.g. DRA7XX) have interrupt crossbars that must be configured correctly as part of system boot (e.g. uboot). If these crossbars aren’t initialized correctly, the slave-side broadcast that TransportRpmsg is available won’t be received, the underlying socket connection won’t be available, and the first sign of failure will be LAD trying to connect to the socket.

4.4.14.5.2. LAD reports LAD_connect() failed: 4

This is a LAD_IOFAILURE = 4. The HOST OS-side application was unable to communicate with LAD due to an OS_level I/O failure.

This is typically caused by LAD directory permissions.

  • In Linux /tmp/LAD

  • In Android /data/lad/LAD

The application was attempting to create or read from LAD’s response FIFO but was unable to by the application.

LAD must be started as root(su). IPC user applications communicating with LAD, can be executed in user mode but need to ensure LAD is stared with the correct permission.

  • % lad_dra7xx -l log.txt -p 777

You can also run both LAD and user application as root(su), thus not requiring permission to be properly set when starting LAD.

4.4.14.5.3. LAD reports _GateMP_TI_dGate not found

When the -g option is thrown, the LAD log may show an error about not finding the symbol _GateMP_TI_dGate:

[22.583839] NameServer_getRemote: value for GateMP:_GateMP_TI_dGate not found.
[22.583852] GateMP_attach: failed to open default gate on procId 4
[22.583862]     status = -5
[22.583871] DONE

This is because LAD is looking for the default GateMP instance, and did not find it. On the DRA7xx, verify

  1. SharedRegion 0 is defined on DSP1. GateMP implementation on the host requires use of SharedRegion

  2. DSP1 is the owner of SharedRegion 0

  3. Ipc_start() is called on DSP1. This ensures the default GateMP instance is created.


4.4.14.6. HLOS loading failures

4.4.14.6.1. BIOS-side VirtQueue assertions

The slave loads ok, but communication fails, and in the slave trace output you find the following assert (line number and timestamp may vary):

[      0.000] [t=0x00d090ce] ti.ipc.family.vayu.VirtQueue: ERROR: "VirtQueue.c", line 296:
[      0.000] ti.ipc.family.vayu.VirtQueue: "VirtQueue.c", line 296:
[      0.000] xdc.runtime.Error.raise: terminating execution

This error often indicates a mismatch in VRING addresses between the HLOS side and the slave. Often this is because a custom resource table was provided and the VRING addresses specified don’t match the HLOS-side addresses.

4.4.14.6.2. QNX IPC driver takes a long time to load the slave executable(s) when ‘-g’ is thrown

The ‘-g’ option to the IPC driver is used to enable optional support for GateMP on the host processor. During the setup of GateMP module, it polls the slave processors for the default gate instance, which is created by the owner of SharedRegion 0. If none of the cores loaded has created the default gate, the driver will continue polling and eventually timeout after 2 seconds, which means the loading procedure may be delayed by 2 seconds as a consequence. To better suit a given application, one can adjust the polling interval and the timeout duration in the file <IPC_INSTALL_DIR>/qnx/src/ipc3x_dev/ti/syslink/ipc/hlos/knl/GateMP_daemon.c (this code excerpt may look slightly different depending on the version of IPC you are looking at, but the idea is the same):

// Timeout duration is SETUP_TIMEOUT * polling interval
#define SETUP_TIMEOUT         2

Int GateMP_setup(Int32 * sr0ProcId)
{
...
   UInt              timeout = SETUP_TIMEOUT;
...
   if (status == GateMP_S_SUCCESS) {
       /* The default gate creator is the owner of SR0 */
       while (((status = GateMP_openDefaultGate(&GateMP_module->defaultGate,
           &procId)) == GateMP_E_NOTFOUND) && (timeout > 0)) {
           sleep(1);  // polling interval
           timeout--;
       }
...
}

In the case where the owner of SharedRegion 0 is not being loaded by the IPC driver, reducing the timeout duration (SETUP_TIMEOUT) may be useful to reduce the delay that results when the driver cannot find the default gate.

In the case where the owner of SharedRegion 0 is indeed loaded by the IPC driver, using usleep() instead of sleep() to reduce the polling interval can be beneficial in cases where the initial poll happens before the slave core (owner of SR0) has even got to the point in its initialization to create the default gate. A faster polling rate would ensure that a second or a third poll is performed more quickly thereby giving a chance for the driver to move on. On the other hand, reducing the polling interval too much may cause the driver to swamp the slave cores with gate lookup requests, counterproductively slowing down the loading process. As a rule of thumb, it is recommended to keep the polling interval above 1 ms in modern platforms at the time of writing.


4.4.14.7. Disabling runtime auto-suspend

When the HLOS detects that there has been no communication with the slave for a defined amount of time and the slave is idled and in standby state, then it can initiate suspend of the slave. Sometimes, the slave may be performing some tasks that don’t require communication with the HLOS and does not wish to enter suspend. Or, it may be useful to temporarily disable auto-suspend while debugging some errors.

4.4.14.7.1. Linux

The runtime suspend feature can be enabled or disabled by writing ‘auto’ or ‘on’ to the device’s ‘control’ power field. The default is enabled. First, find the slave you want to disable (e.g. cat /debug/remoteproc/remoteproc0/name) and then echo “on” to the device’s “control” power field.

For example, if “remoteproc0” is the core you want to disable:

target# echo "on" > /sys/bus/platform/devices/58820000.ipu/power/control

4.4.14.7.2. SYS/BIOS

Alternatively, the slave can choose to deny the runtime suspend request, preventing suspend. To prevent the slave from suspending, add the IpcPower_hibernateLock() call to the slave software. When you wish to allow suspend again, call IpcPower_hibernateUnlock(). As long as the lock is held, the slave will deny all suspend requests from the HLOS.

IpcPower_hibernateLock();
...
// Perform tasks
...
IpcPower_hibernateUnlock();

In this case, it is not necessary to also disable auto-suspend from the HLOS.

4.4.14.7.3. QNX

Runtime auto-suspend is not supported in QNX.


4.4.14.8. FAQ For Keystone Devices

4.4.14.8.1. Overview

This wiki article is a collection of frequently asked questions (FAQ) on IPC on Keystone family of devices , along with some useful collateral and software reference links.


4.4.14.8.2. Guide on building and running the IPC examples of Processor SDK

This guide will give step by step instruction on how to bring up the target EVMs and how to run the run the IPC examples of processor SDK on target EVM.
Download the guide from Keystone II IPC Examples
This guide will have steps on
1. Flashing the u-boot and boot u-boot.
2. Flashing the UBIFS image ( Linux and root filesystem ) into NAND and boot Linux.
3. Building the IPC package.
4. Building and running the IPC examples ( ex02_MessageQ, ex44_compute, ex45_host and ex46_graph ) on target EVM.
5. Real-time debugging on the DSP side programs using CCS.

4.4.14.8.3. Keystone IPC Frequently Asked Questions (FAQ)

Q: Where do I look for the list of IPC API reference document?

Ans:

Please visit : http://downloads.ti.com/dsps/dsps_public_sw/sdo_sb/targetcontent/ipc/latest/docs/doxygen/html/index.html

The IPC product contains the following APIs:

•GateMP (BIOS, Linux, QNX)
•HeapBufMP (BIOS)
•HeapMemMP (BIOS)
•HeapMultiBufMP (BIOS)
•Ipc (BIOS, Linux, QNX)
•ListMP (BIOS)
•MessageQ (BIOS, Linux, QNX)
•MultiProc (BIOS, Linux, QNX)
•NameServer (BIOS, Linux, QNX)
•Notify (BIOS)
•SharedRegion (BIOS)
•IpcPower (BIOS)

Some environments also provide a “Multimedia RPC” interface. Currently this is limited to OMAP5 and DRA7XX devices running an HLOS.

•MmRpc (Linux, QNX)
•MmServiceMgr (BIOS)

Q: How to re-build the IPC package and its libraries?

Ans:

Actually, the user guide which comes along with the IPC package ( under docs folder) is sufficient to build the IPC package. However, we have extracted below steps from user guide. For building IPC package, you can use either Cygwin/Command-line utility in windows machine or Linux machine. This cannot be built through CCS.

1. Install the mcsdk_bios_3_xx_xx_xx which will install the IPC package version, ipc_3_xx_xx_xx
2. Go to the directory where the IPC package is installed.
3. Open the products.mak file and make sure the following parameters are set appropriately.
For example:- ( For ipc-linux build )
PLATFORM = TCI6638
DESTDIR = /opt/ti/ipc_3_xx_xx_xx/ipc_3_xx_xx_xx_lib
XDC_INSTALL_DIR = /opt/ti/xdctools_3_xx_xx_xx
BIOS_INSTALL_DIR = /opt/ti/bios_6_xx_xx_xx
ti.targets.elf.C66 = /opt/ti/ccsv5/tools/compiler/C6000_7.4.5
4. $cd /opt/ti/ ipc_3_xx_xx_xx
5. $make distclean
6. $make –f ipc-linux.mak config
7. $make
8. $make install

Q: Is there any simple example to demonstrate IPC methods like message Q or notify for Keystone-II ?

Ans:

Please look at the ex44_compute.zip in :~/ti/ipc_3_3x_xx_xx/examples/TCI6638_linux_elf
~/ti/ipc_3_3x_xx_xx/examples$ ls
C6472_bios_elf C6A8149_bios_elf DRA7XX_android_elf DRA7XX_linux_elf makefile TCI6638_linux_elf
TI814X_bios_elf C6678_bios_elf dosrc.bat DRA7XX_bios_elf DRA7XX_qnx_elf OMAPL138_linux_elf TDA3XX_bios_elf

Please refer the readme.txt to run and build the example according to the target used. For K2, use the cluster ID as 0 instead of 18.

  1. perl patchExec.pl 0 compute_dspN.xe66 compute_dspN_patched.xe66

  2. lad_tci6638 -r 8 -n 9 -b 0 -l log.txt


Q: For Keystone II, is there any CCS based examples to demonstrate a simple communication between ARM-DSP?

Ans:

No. We have only Image Processing Demo.

In MCSDK 3.x, the ARM core only runs Linux kernel and User Space applications. There isn’t any Linux application example using CCS and if any, it will be using Linux commands.

Q: In IPC packages, there are lot of test example(sample.c) code given in the path: “~ipc_3_3x_xx_xxpackagestiipctests”. But there is only command line option to build the whole IPC package. No option available to build the test examples individually. This is time consuming to build the whole IPC package. Customers were asking for CCS based environment to build and test as individual example for both DSP and ARM side.

Ans:

The IPC package has been developed to work on multiple platforms like Linux, Android, QNX and TI-RTOS(SYS/BIOS) so the command line build is selected which is common across all these platforms and we do not have CCS based projects for these examples.


Q: For keystone-II devices, where do I find the source code of the image processing demo and how to I re-build them? Using ARM core as a master, DSP cores as slaves.

Ans:

Tested version : “mcsdk_bios_3_00_03_15”. The Image processing demo was tested multiple times with this version and it works fine.

The image processing demo source code can be found in below path.

PATH: C:timcsdk_bios_3_0x_0x_0xdemosimage_processingipc (master, slave and common directories)

Building Slave Code: The project can be imported into CCS and can be rebuilt.

For K2E: “~mcsdk_bios_3_0x_0x_0xdemosimage_processingipcevm66ak2eslave”

For K2K: “~mcsdk_bios_3_0x_0x_0xdemosimage_processingipcevmtci6638k2kslave”

For K2L: “~mcsdk_bios_3_0x_0x_0xdemosimage_processingipcevmtci6630k2lslave”

Building Master Code:

This can be built in the linux environment using the makefile provided at

(~mcsdk_bios_3_0x_0x_0xdemosimage_processingipcevm66ak2xmastermake ) which in turn uses the makefile located at ~mcsdk_bios_3_0x_0x_0xdemosimage_processingipcmastersrc


Q: How to import the slave code of Image processing demo and how do I build it?

Ans:

Refer the screenshot below in which the correct path should be provided to pick up the project and its sources. Note that the option for “Copy projects into work space” should be unchecked.

../_images/ImportPjt.jpg

In the project explorer screen, check whether you are able to see the folders, slave –> src –> *.c files…. Right click on the project and give build.

../_images/Sourcefilepjt.jpg

Q: After building the slave code of the Image processing demo using CCS, where it needs to be replaced in the linux file system?

Ans:

Take the binary file, image_processing_evmtci66xxk2x_slave.out and replace it in the path target Linux filesystem, “/usr/share/matrix-gui-2.0/apps/demo_imageproc/bin/”

Q:  While building the ARM(master) side code of Image processing demo, I see a compilation error message about Std.h as below when I make it with or without BUILD_LOCAL=true.

Error:
*user@ubuntu:~/ti/mcsdk_bios_3_0x_0x_0x/demos/image_processing/ipc/evm66ak2x/master$* make
make[1]: Entering directory `/home/user/ti/mcsdk_bios_3_0x_0x_0x/demos/image_processing/ipc/master/src’ mcip_mem_mgmt.c:53:24: fatal error: ti/ipc/Std.h: No such file or directory #include <ti/ipc/Std.h> Compilation terminated.
Ans:
There is an error in the Makefile. Make file needs to be updated for including the appropriate search path for “Std.h”. You will find it at “../ti/ipc_3_3x_0x_0x/linux/include

Update the Makefile located at “mcsdk_bios_3_0x_0x_0xdemosimage_processingipcmastersrcmakefile” as below,

For example:
IPC_INSTALL_DIR := /opt/ti/ipc_3_xx_0x_0x
CFLAGS  := -Wall -I$(COMMON_INC) -I$(MASTER_INC) -I$(IPC_INSTALL_DIR)/linux/include -I$(IPC_INSTALL_DIR)/packages -D_GNU_SOURCE

Q: While building the ARM(master) side code of Image processing demo, I see a linker error message like below.

Error:
/usr/bin/ld: skipping incompatible /opt/ti/ipc_3_35_01_07/examples/TCI6638_linux_elf /ex44_compute_bkp/lib//libtitransportrpmsg.a when searching for -ltitransportrpmsg /usr/bin/ld: cannot find –ltitransportrpmsg
usr/bin/ld: cannot find -ltiipcutils
collect2: error: ld returned 1 exit status
make: *** [../../master/image_processing_master.out] Error 1
root@e2e:/opt/ti/mcsdk_bios_3_0x_xx_xx/demos/image_processing/ipc/master/src#* *
Ans:
For these errors, please make sure you already built the Whole IPC package and installed the libraries such as transportrpmsg in a destination directory. This destination directory should be given in the makefile to find those libraries.
For example, in the makefile located at mcsdk_bios_3_0x_xx_xxdemosimage_processingipcmastersrcmakefile
IPC_INSTALL_DIR := /opt/ti/ipc_3_3x_xx_xx

#The location where the libraries are installed after building the IPC package
SIPC_LINUX_DIR := /opt/ti/ipc_3_3x_xx_xx/IPC_Linux_libraries
CROSS_COMPILE ?= arm-linux-gnueabihf-
CC            := $(CROSS_COMPILE)gcc
CFLAGS        := -Wall -I$(COMMON_INC) -I$(MASTER_INC) -I$(IPC_INSTALL_DIR)/linux/include -I$(IPC_INSTALL_DIR)/packages -D_GNU_SOURCE
LFLAGS        := -lpthread -L$(SIPC_LINUX_DIR)/ -ltitransportrpmsg -L$(SIPC_LINUX_DIR)/ -ltiipc -L$(SIPC_LINUX_DIR)/ -ltiipcutils

Q: The Image processing demo does not work on the version of MCSDK, V3.0x.xx.x on both the K2H and K2E EVMs. The earlier version of MCSDK works on both the EVMs. Will it be fixed on next version?

Ans:

Make sure the u-boot environments is set as below to work on MCSDK 3.x.
u-boot# env default –f –a
u-boot# setenv mem_reserve 1536M
u-boot# saveenv

Q: How to build and run the qmssIpcBenchmark on C6678 EVM?

Ans:

Hardware set up:

Set the boot mode dip switch to no boot/EMIF16 mode, Connect power and emulator to C6678 EVM.

Software setup: 1. After power ON the EVM, create and launch the target configuration file(.ccxml) for CCS debugging.

  1. Group Core 0 and Core 1 in CCS.

  2. Connect to both cores via the group.

4. Load the evmc66xxl.gel to initialize the DDR. The GEL can be found in the “CCS install dir”ccsv5ccs_base_x.x.x.xxxxxemulationboardsevmc66xxlgel directory. Once loaded execute the default setup script on each core. In the CCS menu go to Scripts->EVMC6678L Init Functions->Global_Default_Setup.

5. Highlighting the Group in the CCS Debug window, load transportipcexamplesqmssIpcBenchmarkDebugqmssIpcBenchmark_c66xx.out on each core

6. Highlighting the Group in CCS Debug window, run the program in CCS on both cores simultaneously, qmssIpcBenchmark_c66xx will send messageQ messages between the cores via the QMSS transport. The messages will be used to measure the transport’s performance. The test will be complete after the throughput (msg/s) has been calculated.

../_images/Qmss_IPC.png

Q: How can I build the qmssIpcbenchmark of pdk_C6678_1_1_2_x pdk_C6678_1_1_2_x with release build configuration ?

Ans:

Actually the option, “-mo -o3 -q -k -eo.o” works for building the IPC-QMSS transport library in release mode and the option “-mo -g -q -k -eo.o” works for building the IPC-QMSS transport library in debug mode.
But the common.bld script of IPC doesnot create a “release” folder. By default it always creates the “debug” folder and dumps all the binaries.
By tweaking the common.bld, release folder can be made and hence the the IPC - qmssIpcBenchmark project can be built in release mode.
How to change the Common.bld:
1. Go to C:tiipc_3_00_xx_xxpackagestisdoipcbuildCommon.bld ( Note: Go to the IPC version you use for building the transport library. Here, it refers to IPC version : 3.00.4.29)
2. Modify
Line No:88 profiles[0] = “release”;
Line No: 99 var libPath = “lib/ipc/release/”;
3. Build with option : -mo -o3 -q -k -eo.o in config.bld of transport library located at “..tipdk_C6678_1_1_2_xpackagestitransportipcqmsstransports”
4. Now build qmssIpcBenchmark project.
Screenshot of the successful release build of qmssIpcBenchmark project.
../_images/QMSSIPC_project1.png

Q: How to re-build the IPC - QMSS transport library and generate “ti.transport.ipc.qmss.transports.ae66” ?

Ans:

After installing PDK, go to path “~tipdk_C6678_1_1_2_xpackagestitransportipcqmsstransports”

1. [Optional - Required for debug single stepping] Modify the transports config.bld file C66LE/BE.ccOpts.prefix to remove optimization and add symbolic debug

From: "-mo -o3 -q -k -eo.o"
To: "-mo -g -q -k -eo.o"

2. From a command prompt navigate to the pdkpackagestitransportipc(qmss or srio) directory 3 Configure the XDCPATH environment variable with the BIOS and IPC install locations:

set XDCPATH=c:\ti\bios_w_xx_yy_zz\packages\
set XDCPATH=%XDCPATH%;c:\ti\ipc_w_xx_yy_zz\packages\

4 Configure the XDCCGROOT environment variable with the compiler install path (Using CGT 7.2.4 installed as part of CCS as an example)

set XDCCGROOT=c:\ti\ccsv5\tools\compiler\c6000_7.2.4

5 Add the XDC Tools to your system PATH

set PATH=%PATH%;c:\ti\xdctools_w_xx_yy_zz\

6 Clean the transport

>xdc clean -PR .

7 Build the transport

>xdc -PR .

If we try this option, we should be able to build in releasemode. Note: To allow single-step debug of the IPC and BIOS source rebuild the example projects with the following command added to the example’s .cfg file BIOS.libType = BIOS.LibType_Debug; <— here.

../_images/Qmss_transports.png

4.4.15. IPC Install Guides

The IPC package now comes bundled with Processor SDK RTOS and can be built using the top-level makefile located in the Processor SDK RTOS folder. These install guides have been kept here for those using older, legacy versions of IPC.

4.4.15.1. BIOS Install Guide

4.4.15.1.1. Introduction

Inter/Intra Processor Communication (IPC) is a product designed to enable communication between processors in a multi-processor environment. Features of IPC include message passing, multi-processor gates, shared memory primitives, and more.

IPC is designed for use with processors running SYS/BIOS applications. This is typically an ARM or DSP. IPC includes support for High Level Operating Systems (HLOS) like Linux, as well as the SYS/BIOS RTOS. The breadth of IPC features supported in an HLOS environment is reduced in an effort to simplify the product.

4.4.15.1.2. Install

IPC is often distributed and installed within a larger SDK. In those cases, no installation is required.

Outside of an SDK, IPC can be downloaded here, and is released as a zip file. To install, simply extract the file.

buildhost$ unzip ipc_<version>.zip

This will extract the IPC product in a directory with its product name and version information (e.g. c:/ti/ipc_<version>)

Note

  • This document assumes the IPC install path to be the user’s home directory on a Linux host machine (/home/<user>) or the user’s main drive on a Windows host machine (C:\). The variable IPC_INSTALL_DIR will be used throughout the document. If IPC was installed at a different location, make appropriate changes to commands.

  • Some customers find value in archiving the released sources in a configuration management system. This can help in identifying any changes made to the original sources - often useful when updating to newer releases.

4.4.15.1.3. Build

The IPC product often comes with prebuilt libraries, so rebuilding them isn’t necessary. The IPC product downloads contain prebuilt libraries, and when provided with an SDK, IPC is typically rebuilt to contain only libraries appropriate for the SDK.

However, if you want to rebuild its libraries, IPC provides GNU makefile(s) at the base of the product. This section describes the steps required to rebuild the IPC libraries.

Note

GNU make version 3.81 or greater is required. The XDC tools (provided with most SDKs and CCS distributions) includes a pre-compiled version of GNU make 3.81 in $(XDC_INSTALL_DIR)/gmake.

4.4.15.1.3.1. products.mak

IPC contains a products.mak file at the root of the product that specifies the necessary paths and options to build IPC for the various OS support.

Edit products.mak and set the following variables:

Note

Please make sure the complete path is specified. (e.g) Don’t use ~ in the path specified.

  • XDC_INSTALL_DIR - Path to TI’s XDCTools installation

  • BIOS_INSTALL_DIR - Path to TI’s SYS/BIOS installation

  • ti.targets.<device target and file format> - Path to TI toolchain for the device.

    • Set only the variables to the targets your device supports to minimize build time.

  • gnu.targets.arm.<device target and file format> - Path to GNU toolchain for the device.

    • Set only the variables to the targets your device supports to minimize build time.

  • PLATFORM - (Optional) platform for which to build executables

    • Introduced in IPC 3.10. Prior releases build executables for all platforms based on that targets/toolchains set above

    • If not set, only libraries will be built (not executables)

Note

The versions used during validation can be found in the IPC Release Notes provided in the product.

4.4.15.1.3.2. ipc-bios.mak

IPC is built with a GNU makefile. After editing products.mak, issue the following command:

<buildhost> make -f ipc-bios.mak all

Based on the number of targets you’re building for, this may take some time.

4.4.15.1.4. Examples

The IPC product contains an examples/archive directory with device-specific examples. Once identifying your device, the examples can be unzipped anywhere on your build host. Typically once unzipped, the user edits the example’s individual products.mak file and simply invokes make.

Note

A common place to unzip the examples is into the IPC_INSTALL_DIR/examples/ directory. Each example’s products.mak file is smart enough to look up two directories (in this case, into IPC_INSTALL_DIR) for a master products.mak file, and if found it uses those variables. This technique enables users to set the dependency variables in one place, namely IPC_INSTALL_DIR/products.mak.

Each example contains a readme.txt with example-specific details.

4.4.15.1.5. See Also

4.4.15.2. Linux Install Guide

4.4.15.2.1. Introduction

Inter/Intra Processor Communication (IPC) is a product designed to enable communication between processors in a multi-processor environment. Features of IPC include message passing, multi-processor gates, shared memory primitives, and more.

IPC is designed for use with processors running SYS/BIOS applications. This is typically an ARM or DSP. IPC includes support for High Level Operating Systems (HLOS) like Linux, as well as the SYS/BIOS RTOS. The breadth of IPC features supported in an HLOS environment is reduced in an effort to simplify the product.

4.4.15.2.2. Install

IPC is often distributed and installed within a larger SDK. In those cases, no installation is required.

Outside of an SDK, IPC can be downloaded here, and is released as a zip file. To install, simply extract the file.

buildhost$ unzip ipc_<version>.zip

This will extract the IPC product in a directory with its product name and version information (e.g. c:/ti/ipc_<version>)

Note

  • This document assumes the IPC install path to be the user’s home directory on a Linux host machine (/home/<user>) or the user’s main drive on a Windows host machine (C:\). The variable IPC_INSTALL_DIR will be used throughout the document. If IPC was installed at a different location, make appropriate changes to commands.

  • Some customers find value in archiving the released sources in a configuration management system. This can help in identifying any changes made to the original sources - often useful when updating to newer releases.

4.4.15.2.3. Build

The IPC product often comes with prebuilt SYS/BIOS-side libraries, so rebuilding them isn’t necessary. The Linux-side user libraries may also be provided prebuilt, but customers often want to change the configuration (e.g. static, dynamic).

IPC provides GNU makefile(s) to rebuild all its libraries at the base of the product, details are below.

Note

GNU make version 3.81 or greater is required. The XDC tools (provided with most SDKs and CCS distributions) includes a pre-compiled version of GNU make 3.81 in $(XDC_INSTALL_DIR)/gmake.

4.4.15.2.3.1. products.mak

IPC contains a products.mak file at the root of the product that specifies the necessary paths and options to build IPC for the various OS support.

Edit products.mak and set the following variables:

  • Variables used by both Linux-side and BIOS-side build scripts

    • PLATFORM - (Optional) Device to build for

      • To find the supported list of platforms, run: ./configure –help

      • If not set, Linux libraries and executables for all supported platforms will be built.

      • If not set, BIOS libraries for all toolchains specified (see below) will be built, but no BIOS-side executables will be built.

        • BIOS-side builds started leveraging this variable in IPC 3.10. Prior releases built BIOS-side executables for all supported platforms based on that targets/toolchains set above (which can take a while!)

  • Variables used by Linux-side build scripts

    • TOOLCHAIN_INSTALL_DIR - Path to the devices ARM Linux cross-compiler toolchain

    • TOOLCHAIN_LONGNAME - Long name of the devices toolchain (e.g. arm-none-linux-gnueabi)

    • KERNEL_INSTALL_DIR - Location of your Linux kernel installation

      • In old releases, this variable was optional, and only needed for platforms that support the MmRpc API (e.g. OMAP5, DRA7XX). In IPC 3.00.04, 3.10.02, and 3.20+, all platforms began requiring this variable to interrogate your kernel’s version (via KERNEL_INSTALL_DIR/linux/version.h) and accommodate different kernels.

    • DRM_PREFIX - (Optional) Location of your libdrm installation, used by some MmRpc tests

      • This is only used by MmRpc tests, and therefore only should be set for platforms that support the MmRpc API

      • If set, additional MmRpc tests may be built.

    • CMEM_INSTALL_DIR - (Optional) Path to TI Linux Utils package to locate the pre-built CMEM libraries used by some MessageQ tests

      • If set, additional test applications for select platforms may be built.

  • Variables used by BIOS-side build scripts

    • XDC_INSTALL_DIR - Path to TI’s XDCTools installation (e.g. c:/ti/xdctools_<version>)

    • BIOS_INSTALL_DIR - Path to TI’s SYS/BIOS installation (e.g. c:/ti/bios_<version>)

    • ti.targets.<device target and file format> - Path to TI toolchain for the device. (e.g. c:/ti/CCS/ccsbase/tools/compiler/c6000_<version>)

      • Set only the variables to the targets your device supports to minimize build time.

Note

The specific versions of dependent components can be found in the IPC Release Notes, provided in the product.

4.4.15.2.3.2. ipc-linux.mak

The Linux-side build is provided as a GNU Autotools (Autoconf, Automake, Libtool) project. If you are familiar with Autoconf GNU projects, you can proceed with using the ./configure script directly to cross-compile the Linux user libraries and tests.

For those that require some assistance, the IPC package provides a GNU makefile (ipc-linux.mak) to configure the Linux-side build, using the options and component paths set in the products.mak file. To configure the build using these files, issue the following command:

<buildhost> make -f ipc-linux.mak config

There are few additional target goals provided in the ipc-linux.mak file for commonly used configurations. These goals include:

  • config - (Default) Configure both static and shared (dynamic) Linux IPC user libraries. Executables (e.g. lad and tests) link against the shared libraries.

  • config-static - Configure static only libraries and executables.

  • config-shared - Configure shared (dynamic) only libraries and executables.

Once the ‘config’ is complete, and the autotools-generated Makefile has been created, you can build the Linux side of IPC by issuing the following:

<buildhost> make

You can also specify a PLATFORM to (re)configure for on the command line which overrides any options set in the products.mak file as follows:

<buildhost> make -f ipc-linux.mak config PLATFORM=omapl138
<buildhost> make

Note that before reconfiguring for a new Linux toolchain or platform, the autotools-generated files should be clean(ed):

<buildhost> make distclean

4.4.15.2.3.3. ipc-bios.mak

The SYS/BIOS-side IPC is built with a GNU makefile. After editing products.mak, issue the following command:

<buildhost> make -f ipc-bios.mak all

Based on the number of targets you’re building for, this may take some time.

Note

The BIOS-side libraries often come pre-built, so in many cases, rebuilding the BIOS-side is not necessary. Some reasons you may want to rebuild:

  • Your distribution of IPC didn’t come with the necessary pre-built libraries

  • You intend to run the ‘test’ executables (which often don’t come pre-built)

  • You want to use a specific toolchain or dependency version

  • You want to tune some of the compile options

4.4.15.2.4. Run

The IPC product provides a way to install (copy) the necessary IPC executables and libraries onto the device’s target file-system to simplify the execution of the applications.

4.4.15.2.4.1. Configuring Kernel

The IPC product provides a set of Linux kernel patches that need to be applied to the different device supported kernels to add necessary kernel support. The patches are located in the linux/patches directory of the IPC installation.

4.4.15.2.4.1.1. OMAP-L138

The kernel for the OMAP-L138, can be obtained from Gitorious linux-davinci project.

The patches apply to the following commit id: 595ab716fc6e648b7dc79a58a01917ebb67b9508

The specific patches needed for this kernel can be found in the linux/patches/3.8.0 of your IPC installation.

Once the patches are applied, there are a few key config parameters needed for rpmsg and socket driver to build/work.

CONFIG_REMOTEPROC=m CONFIG_DA8XX_REMOTEPROC=m CONFIG_RPMSG=m CONFIG_VIRTIO=m It is also recommended to compile a Linux kernel with the debugfs facility

CONFIG_DEBUG_FS=y Re-build the kernel. For example:

buildhost$ make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi-  uImage

You will also need to re-build the kernel modules and install them on your target’s file system. For example:

buildhost$ make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi-  modules
buildhost$ make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- INSTALL_MOD_PATH=<target filesystem> modules_install
4.4.15.2.4.2. Kernel Boot-up Parameters

IPC requires an argument to be passed to the Linux kernel during boot up to properly run the tests. The remote processor(s) (rproc) memory location needs to be set.

  • For example,

bootargs console=ttyS2,115200n8 root=/dev/nfs nfsroot=HOST:nfs_root,nolock rw ip=dhcp rproc_mem=16M@0xC3000000

This is just an example, bootargs may vary depending on available setup Depending on the memory map used in the final system configuration, the memory to be reserved for rproc usage may differ.

4.4.15.2.4.3. Installing Tests

To assemble the IPC test executables and libraries into a directory structure suitable for running on the target’s file-system, issue the following command in the IPC install directory:

buildhost$ make install prefix=<target filesystem>/usr

Depending on you target’s filesystem directory privileges, you may be required to run **sudo make install* to properly install the files *

Note

The test executables and libraries will be installed in the location path set by the prefix variable. If you are installing directly on a host mounted Network Filesystem(NFS), make sure to specify usr at the end of the prefix variable path. As with other variables, you can override this on the command line:

buildhost$ sudo make install prefix=<target filesystem>/usr

The remote processor’s applications will be loaded via the remote_proc kernel module but they need to reside on the devices target filesystem in /lib/firmware directory. The location of the remote core application within the IPC product various based on device.

4.4.15.2.4.3.1. Slave Binaries

The slave-side test binaries, once built, are found in your IPC_INSTALL_DIR/packages/ti/ipc/tests/bin/<platform>_<core> directory.

Copy the appropriate slave-side executable onto the devices target filesystem into the /lib/firmware directory. For example, OMAP-L138 developers would do this:

buildhost$ cp IPC_INSTALL_DIR/packages/ti/ipc/tests/bin/ti_platforms_evmOMAPL138_DSP/ <target filesystem>/lib/firmware/.
4.4.15.2.4.3.2. IPC Daemons and Drivers

IPC provides system-wide services across multiple applications, and utilizes low-level system hardware (e.g. interrupts and shared memory). To facilitate these services, IPC uses a user-space daemon (LAD) and several kernel device drivers.

LAD

System-wide IPC state is managed by a user-space daemon (LAD). This daemon is specific to a given device, and is named lad_<device>. It will reside on the target’s filesystem (typically in /usr/bin/) after following the #Installing Tests section. To run LAD, execute:

target# /usr/bin/lad_<device>

This forks the LAD daemon and leaves it running in the background.

LAD takes an optional argument to indicate a filename into which log statements should be emitted. This file will be created in the /tmp/LAD/ directory. How to specify the filename varies based on your IPC release. For example, to instruct LAD to emit log statements into a ‘lad.txt’ file, start LAD like this:

  • Releases before IPC 3.21:

target# /usr/bin/lad_<device> lad.txt
  • IPC 3.21 and after:

target# /usr/bin/lad_<device> -l lad.txt

Drivers

The kernel drivers/modules added by the Linux patches must be inserted into the kernel for IPC applications to run correctly. Refer to the #Configuring Kernel section. The required modules must be configured, built and loaded onto the target’s filesystem.

Prior to loading the modules, a directory (/debug) must be created at the root of your devices filesystem. This directory will be mounted as a debugfs (debug filesystem) which the kernel modules will use to provide details about the slaves (e.g. running state, trace output, etc). If the /debug directory doesn’t exist, simply create it as follows:

target# mkdir /debug

OMAP-L138

On OMAP-L138, the kernel modules can be loaded with the following command on the target’s file-system:

target# depmod -a
target# mount -t debugfs none /debug
target# modprobe remoteproc
target# modprobe da8xx_remoteproc da8xx_fw_name=messageq_single.xe674
target# modprobe virtio_rpmsg_bus
target# modprobe rpmsg_proto

The kernel modules can be unloaded by issuing the following command on the target’s file-system:

target# umount /debug
target# rmmod rpmsg_proto
target# rmmod virtio_rpmsg_bus
target# rmmod da8xx_remoteproc
target# rmmod remoteproc

OMAP54XX

On OMAP54XX, the kernel modules can be loaded with the following command on the target’s file-system:

target# depmod -a
target# mount -t debugfs none /debug
target# modprobe remoteproc
target# modprobe omap_remoteproc
target# modprobe virtio_rpmsg_bus
target# modprobe rpmsg_proto
4.4.15.2.4.3.3. Running Test Applications

The test applications are already on the target’s filesystem in /usr/bin assuming the #Installing Tests section has been followed.

To run the test application’s, execute the following on the target’s filesystem:

target# /usr/bin/MessageQApp_<device>

OMAP-L138

The expected output on the Linux-side should be:

Using numLoops: 100; procId : 1
Entered MessageQApp_execute
Local MessageQId: 0x1
Remote queueId  [0x10000]
Exchanging 100 messages with remote processor DSP...
MessageQ_get #0 Msg = 0x15328
Exchanged 1 messages with remote processor DSP
MessageQ_get #1 Msg = 0x15328
...
...
Exchanged 99 messages with remote processor DSP
MessageQ_get #99 Msg = 0x15328
Exchanged 100 messages with remote processor DSP
Sample application successfully completed!
Leaving MessageQApp_execute

The output on the remote processor, can be obtained by running the following on the target filesystem:

target# cat /debug/remoteproc/remoteproc0/trace0

The expected output on the remote processor should be:

3 Resource entries at 0xc3100000
messageq_single.c:main: MultiProc id = 1
registering rpmsg-proto service on 61 with HOST
tsk1Fxn: created MessageQ: SLAVE_DSP; QueueID: 0x10000
Awaiting sync message from host...
[t=0x00000001:67984156] ti.ipc.rpmsg.MessageQCopy: MessageQCopy_send: no object for endpoint: 53
[t=0x00000001:67f626ed] ti.ipc.rpmsg.MessageQCopy: MessageQCopy_send: no object for endpoint: 53
Received msg from (procId:remoteQueueId): 0x0:0x1
       payload: 8 bytes; loops: 100 with printing.
Got msg #0 (40 bytes) from procId 0
Sending msg Id #0 to procId 0
Got msg #1 (40 bytes) from procId 0
Sending msg Id #1 to procId 0
...
...
Got msg #98 (40 bytes) from procId 0
Sending msg Id #98 to procId 0
Got msg #99 (40 bytes) from procId 0
Sending msg Id #99 to procId 0
Awaiting sync message from host...
[t=0x00000015:7b46c4c2] ti.ipc.rpmsg.MessageQCopy: MessageQCopy_send: no object for endpoint: 53
[t=0x00000015:7b6315fb] ti.ipc.rpmsg.MessageQCopy: MessageQCopy_send: no object for endpoint: 53

4.4.15.2.5. See Also

4.4.15.3. QNX Install Guide

4.4.15.3.1. Introduction

Inter/Intra Processor Communication (IPC) is a product designed to enable communication between processors in a multi-processor environment. Features of IPC include message passing, multi-processor gates, shared memory primitives, and more.

IPC is designed for use with processors running SYS/BIOS applications. This is typically an ARM or DSP. IPC includes support for High Level Operating Systems (HLOS) like Linux, as well as the SYS/BIOS RTOS. The breadth of IPC features supported in an HLOS environment is reduced in an effort to simplify the product.

4.4.15.3.2. Install

IPC is often distributed and installed within a larger SDK. In those cases, no installation is required.

Outside of an SDK, IPC can be downloaded here, and is released as a zip file. To install, simply extract the file.

buildhost$ unzip ipc_<version>.zip

This will extract the IPC product in a directory with its product name and version information (e.g. c:/ti/ipc_<version>)

Note

  • This document assumes the IPC install path to be the user’s home directory on a Linux host machine (/home/<user>) or the user’s main drive on a Windows host machine (C:\). The variable IPC_INSTALL_DIR will be used throughout the document. If IPC was installed at a different location, make appropriate changes to commands.

  • Some customers find value in archiving the released sources in a configuration management system. This can help in identifying any changes made to the original sources - often useful when updating to newer releases.

4.4.15.3.3. Build

The IPC product often comes with prebuilt SYS/BIOS-side libraries, so rebuilding them isn’t necessary. The QNX-side libraries/binaries may also be provided prebuilt by SDK programs, but the standalone IPC release does not currently pre-build them.

IPC provides GNU makefile(s) to rebuild all its libraries at the base of the product, details are below.

Note

GNU make version 3.81 or greater is required. The XDC tools (provided with most SDKs and CCS distributions) includes a pre-compiled version of GNU make 3.81 in $(XDC_INSTALL_DIR)/gmake.

4.4.15.3.3.1. products.mak

IPC contains a products.mak file at the root of the product that specifies the necessary paths and options to build IPC for the various OS support.

Edit products.mak and set the following variables:

  • Variables used by both QNX and BIOS

    • PLATFORM - Device to build for

      • QNX started using this variable in IPC 3.20. Prior releases required setting “DEVICE” for QNX and “PLATFORM” for BIOS. The two variables were consolidated in IPC 3.20.

      • BIOS started leveraging this variable in IPC 3.10. Prior releases built BIOS-side executables for all supported platforms based on that targets/toolchains set above (which can take a while!)

  • QNX

    • QNX_INSTALL_DIR - Path to your QNX installation directory.

    • DESTDIR - Path to which target binaries will be exported when running the ‘make install’ goal.

    • DEVICE - (only required for releases prior to IPC 3.20) Device to build for

  • SYS/BIOS

    • XDC_INSTALL_DIR - Path to TI’s XDCTools installation

    • BIOS_INSTALL_DIR - Path to TI’s SYS/BIOS installation

    • ti.targets.<device target and file format> - Path to TI toolchain for the device.

      • Set only the variables to the targets your device supports to minimize build time.

    • gnu.targets.arm.<device target and file format> - Path to GNU toolchain for the device.

      • Set only the variables to the targets your device supports to minimize build time.

Note

The versions used during validation can be found in the IPC Release Notes provided in the product.

ipc-qnx.mak

The QNX-side build is performed using QNX makefiles. To build using the components paths set in the products.mak file, issue the following command:

<buildhost> make -f ipc-qnx.mak all
4.4.15.3.3.2. ipc-bios.mak

The SYS/BIOS-side IPC is built with a GNU makefile. After editing products.mak, issue the following command:

<buildhost> make -f ipc-bios.mak all

Based on the number of targets you’re building for, this may take some time.

Note for Windows users: If you are building with a Windows host machine and it has the QNX tools installed, you will instead need to run the following in a separate command prompt window (cmd.exe) to build the SYS/BIOS side outside of the QNX build environment:

<buildhost> set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem
<buildhost> <XDC_INSTALL_DIR>\gmake -f ipc-bios.mak all

where <XDC_INSTALL_DIR> should be replaced with the installation directory of your XDC tools, same as the path you have used in products.mak.

4.4.15.3.4. Run

The IPC product provides a way to install (copy) the necessary IPC executables and libraries onto the device’s target file-system to simplify the execution of the applications. The details can vary across OS’s, so this description has been separated into OS-specific sections.

4.4.15.3.4.1. Configuring the BSP

Some of the provided IPC tests that use a utility called SharedMemoryAllocator require a carveout to be created in the QNX-owned memory. To reserve this memory, you must make the following change in the file <QNX BSP installation directory>srchardwarestartupboards<board name>build in the QNX BSP. E.g.

startup-omap5432uevm -r 0xBA300000,0x5A00000 -vvvvv -P2 -W

Save the file, then rebuild the QNX OS image (ifs-*.bin) and replace your existing one with the new one.

4.4.15.3.4.2. Installing Tests in QNX

To assemble the IPC resource manager, shared libraries and test executables into a directory structure suitable for running on the device’s file-system, issue the following command in the IPC_INSTALL_DIR directory:

buildhost$ make -f ipc-qnx.mak install

This will install the binaries into the directory specified by DESTDIR in products.mak. It this assumed that DESTDIR is a directory visible to the target filesystem. If not, you should copy its contents to such a location (e.g. onto an SD card that can be accessed by the EVM).

When building in Windows, some users might get build messages that report a version mismatch in cygwin:

C:/QNX650/host/win32/x86/usr/bin/make -j 1 -Cle.v7 -fMakefile install
      1 [main] ? (5984) C:\QNX650\host\win32\x86\usr\photon\bin\find.exe: *** fa
tal error - system shared memory version mismatch detected - 0x8A88009C/0x2D1E009C.
This problem is probably due to using incompatible versions of the cygwin DLL.
Search for cygwin1.dll using the Windows Start->Find/Search facility
and delete all but the most recent version.  The most recent version *should*
reside in x:\cygwin\bin, where 'x' is the drive on which you have
installed the cygwin distribution.  Rebooting is also suggested if you
are unable to find another cygwin DLL.

Based on what we observed the binaries are still exported correctly despite the messages. If you do want to eliminate them, you should replace the file cygwin1.dll in <QNX_INSTALL_DIR>hostwin32x86usrphotonbin with the newest cygwin1.dll you can find on your host machine (do a search on your PC’s filesystem in Windows).

Some of the tests rely on corresponding remote core applications to be run on the slave processor(s). The remote processor’s applications are loaded when launching the resource manager. See section #IPC_resource_manager for details on launching the resource manager.

The location of the remote core applications within the IPC product varies based on device.

4.4.15.3.4.2.1. Installing remote core applications

Remote core applications can be found in <IPC_INSTALL_DIR>/packages/ti/ipc/tests/bin/ti_platform_<your platform name>_* directories.

For example, you can copy the messageq_single.xem4 for OMAP54xx uEVM’s IPU onto the device’s target filesystem into the bin directory as follows:

buildhost$ copy <IPC_INSTALL_DIR>/packages/ti/ipc/tests/bin/ti_platform_omap54xx_ipu/messageq_single.xem4 <DESTDIR>/armle-v7/bin

‘ti_platform_omap54xx_ipu’ indicates the platform is ‘omap54xx’ and the remote core name is ‘IPU’. You only need to copy the binaries relevant to your platform.

4.4.15.3.4.3. IPC resource manager

Much of the functionality of IPC is provided by the resource manager. It can be launched as follows:

target# cd <target directory corresponding to DESTDIR>/armle-v7/bin
target# export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:<target directory corresponding to DESTDIR>/armle-v7/usr/lib
target# ipc <CORE1> <remote executable 1> <CORE2> <remote executable 2> ...

<CORE> should correspond to the name of the remote core on which you want the remote executable to be loaded. As a rule of thumb, it is the capitalized version of the core name specified by the name of the directory from which the executable was copied in the #Installing remote core applications section. For example, if the executable is copied from “ti_platform_omap54xx_**ipu**/test_omx_ipu_omap5.xem4”, then you should load it as follows:

target# ipc IPU test_omx_ipu_omap5.xem4

The resource manager will register devices in the pathname space for communicating with the IPC. Communication with the IPC is only possible once the needed devices are registered. The following devices are registered by default when the IPC resource manager is launched:

Device

Description

/dev/tiipc

Provides the “ti-ipc” protocol. Needed by MessageQ APIs.

/dev/ipc

Provides the HWSpinLock functionality. Needed by GateMP APIs.

Additionally, more devices may be registered by the remote core firmware if using the “rpmsg-rpc” protocol. In that case, the name that appears in the pathname space is specified by the remote core firmware.

Later, when you are done running applications that use IPC and no longer need the resource manager, it can be terminated as follows:

target# cd <target directory corresponding to DESTDIR>/armle-v7/bin
target# slay ipc
4.4.15.3.4.4. Running Test Applications

The QNX-side of the test applications are already on the target’s filesystem in <target directory corresponding to DESTDIR>/armle-v7/bin and <target directory corresponding to DESTDIR>/armle-v7/bin/tests, assuming the #Installing Tests in QNX and #IPC resource manager sections have been followed and that the resource manager has loaded the remote core(s) with the executable corresponding to the test you’d like to run.

To find out the syntax to use for running the test (say MessageQApp), run

target# cd <target directory corresponding to DESTDIR>/armle-v7/bin/tests
target# use MessageQApp

To run a test application, execute it on the target’s filesystem:

target# cd <target directory corresponding to DESTDIR>/armle-v7/bin/tests
target# ./MessageQApp 10

Here is a list of the main tests that are available in the IPC product:

  • MessageQApp: Test that creates a single thread that sends messages from host to remote core using MessageQ

    • messageq_single.x* need to be loaded by the resource manager

  • MessageQMulti: Test that creates multiple threads which send messages from host to remote core using MessageQ

    • messageq_multi.x* need to be loaded by the resource manager

  • mmrpc_test: Test that exercises MMRPC

    • test_omx_ipu_<platform>.x* need to be loaded by the resource manager

    • Aside from the IPC resource manager, this test also needs the shmemallocator resource manager to be launched beforehand:

target# cd <target directory corresponding to DESTDIR>/armle-v7/bin
target# shmemallocator
target# cd tests
target# mmrpc_test 1

Expected output

To give you an idea, the expected output for MessageQApp on the QNX-side should look similar to this:

Using numLoops: 10; procId : 1
Entered MessageQApp_execute
Local MessageQId: 0x1
Remote queueId  [0x10000]
Exchanging 10 messages with remote processor IPU...
MessageQ_get #0 Msg = 0x11c9f0
Exchanged 1 messages with remote processor IPU
MessageQ_get #1 Msg = 0x11c9f0
Exchanged 2 messages with remote processor IPU
MessageQ_get #2 Msg = 0x11c9f0
...
...
Exchanged 9 messages with remote processor IPU
MessageQ_get #9 Msg = 0x11c9f0
Exchanged 10 messages with remote processor IPU
Sample application successfully completed!
Leaving MessageQApp_execute

The output on the remote processor can be obtained by running the following on the target filesystem:

target# cat /dev/ipc-trace/IPU

The expected output on the remote processor should look similar to this:

[0][      0.000] 16 Resource entries at 0x3000
[0][      0.000] messageq_single.c:main: MultiProc id = 1
[0][      0.000] [t=0x006c565d] ti.ipc.transports.TransportVirtioSetup: TransportVirtio
Setup_attach: remoteProcId: 0
[0][      0.000] registering rpmsg-proto:rpmsg-proto service on 61 with HOST
[0][      0.000] [t=0x0072625b] ti.ipc.rpmsg.MessageQCopy: MessageQCopy_create: endPt c
reated: 61
[0][      0.000] [t=0x0073e8d9] ti.ipc.rpmsg.MessageQCopy: callback_availBufReady: virt
Queue_toHost kicked
[0][      0.000] [t=0x00753771] ti.ipc.rpmsg.MessageQCopy: callback_availBufReady: virt
Queue_fromHost kicked
[0][      0.000] [t=0x0076cb49] ti.ipc.rpmsg.MessageQCopy: MessageQCopy_swiFxn:
[0][      0.000]        Received msg: from: 0x5a, to: 0x35, dataLen: 72
[0][      0.000] [t=0x007872e9] ti.ipc.rpmsg.MessageQCopy: MessageQCopy_send: no object
for endpoint: 53
[0][      0.000] tsk1Fxn: created MessageQ: SLAVE_CORE0; QueueID: 0x10000
[0][      0.000] Awaiting sync message from host...
[0][     51.992] [t=0x0c475268] ti.ipc.rpmsg.MessageQCopy: callback_availBufReady: virt
Queue_fromHost kicked
[0][     51.992] [t=0x0c48eb28] ti.ipc.rpmsg.MessageQCopy: MessageQCopy_swiFxn:
[0][     51.993]        Received msg: from: 0x400, to: 0x3d, dataLen: 176
[0][     51.993] [t=0x0c4ad220] ti.ipc.rpmsg.MessageQCopy: MessageQCopy_send: calling c
allback with data len: 176, from: 1024
[0][     51.993]
[0][     52.995] [t=0x0c873ded] ti.ipc.rpmsg.MessageQCopy: callback_availBufReady: virt
Queue_fromHost kicked
[0][     52.996] [t=0x0c88b029] ti.ipc.rpmsg.MessageQCopy: MessageQCopy_swiFxn:
[0][     52.996]        Received msg: from: 0x406, to: 0x3d, dataLen: 40
[0][     52.996] [t=0x0c8a8a87] ti.ipc.rpmsg.MessageQCopy: MessageQCopy_send: calling c
allback with data len: 40, from: 1030
[0][     52.996]
[0][     52.996] Received msg from (procId:remoteQueueId): 0x0:0x1
[0][     52.996]        payload: 8 bytes; loops: 10 with printing.
[0][     52.997] [t=0x0c8eab7e] ti.ipc.rpmsg.MessageQCopy: callback_availBufReady: virt
Queue_fromHost kicked
[0][     52.997] [t=0x0c9031bc] ti.ipc.rpmsg.MessageQCopy: MessageQCopy_swiFxn:
[0][     52.997]        Received msg: from: 0x406, to: 0x3d, dataLen: 40
[0][     52.997] [t=0x0c9208fa] ti.ipc.rpmsg.MessageQCopy: MessageQCopy_send: calling c
allback with data len: 40, from: 1030
[0][     52.997]
[0][     52.997] Got msg #0 (40 bytes) from procId 0
[0][     52.997] Sending msg Id #0 to procId 0
[0][     52.998] [t=0x0c959f33] ti.ipc.rpmsg.MessageQCopy: callback_availBufReady: virt
Queue_fromHost kicked
[0][     52.998] [t=0x0c971df7] ti.ipc.rpmsg.MessageQCopy: MessageQCopy_swiFxn:
[0][     52.998]        Received msg: from: 0x406, to: 0x3d, dataLen: 40
[0][     52.998] [t=0x0c98f3e7] ti.ipc.rpmsg.MessageQCopy: MessageQCopy_send: calling c
allback with data len: 40, from: 1030
[0][     52.998]
[0][     52.999] Got msg #1 (40 bytes) from procId 0
[0][     52.999] Sending msg Id #1 to procId 0
[0][     52.999] [t=0x0c9c7a00] ti.ipc.rpmsg.MessageQCopy: callback_availBufReady: virt
Queue_fromHost kicked
[0][     52.999] [t=0x0c9df7fc] ti.ipc.rpmsg.MessageQCopy: MessageQCopy_swiFxn:
[0][     52.999]        Received msg: from: 0x406, to: 0x3d, dataLen: 40
[0][     52.999] [t=0x0c9fce5a] ti.ipc.rpmsg.MessageQCopy: MessageQCopy_send: calling c
allback with data len: 40, from: 1030
[0][     52.999]
[0][     53.000] Got msg #2 (40 bytes) from procId 0
[0][     53.000] Sending msg Id #2 to procId 0
[0][     53.000] [t=0x0ca36e79] ti.ipc.rpmsg.MessageQCopy: callback_availBufReady: virt
Queue_fromHost kicked
[0][     53.000] [t=0x0ca4ea95] ti.ipc.rpmsg.MessageQCopy: MessageQCopy_swiFxn:
[0][     53.000]        Received msg: from: 0x406, to: 0x3d, dataLen: 40
[0][     53.001] [t=0x0ca6c975] ti.ipc.rpmsg.MessageQCopy: MessageQCopy_send: calling c
allback with data len: 40, from: 1030
[0][     53.001]
[0][     53.001] Got msg #3 (40 bytes) from procId 0
[0][     53.001] Sending msg Id #3 to procId 0
...
...
[0][     53.007] Got msg #8 (40 bytes) from procId 0
[0][     53.007] Sending msg Id #8 to procId 0
[0][     53.007] [t=0x0cccd3d7] ti.ipc.rpmsg.MessageQCopy: callback_availBufReady: virt
Queue_fromHost kicked
[0][     53.007] [t=0x0cce50ed] ti.ipc.rpmsg.MessageQCopy: MessageQCopy_swiFxn:
[0][     53.007]        Received msg: from: 0x406, to: 0x3d, dataLen: 40
[0][     53.007] [t=0x0cd027bd] ti.ipc.rpmsg.MessageQCopy: MessageQCopy_send: calling c
allback with data len: 40, from: 1030
[0][     53.007]
[0][     53.008] Got msg #9 (40 bytes) from procId 0
[0][     53.008] Sending msg Id #9 to procId 0
[0][     53.008] Awaiting sync message from host...
4.4.15.3.4.5. Running standalone examples

On some platforms, there are standalone examples provided to illustrate how to use specific features in IPC. These standalone examples are designed to be easily rebuilt outside of the IPC product, and represent a good starting point for development. If available, the examples are located in <IPC_INSTALL_DIR>examplesarchive<platform of your choice>.

To use the examples, unzip the example you want in a working directory of your choice. Update the products.mak file in the example’s directory with the installation locations of the various dependent components. Then build it. E.g.:

buildhost$ unzip ex02_messageq.zip
buildhost$ cd ex02_messageq
buildhost$ make clean
buildhost$ make
buildhost$ make install

This would produce the host and remote core binaries in an ‘install’ subdirectory. Tip: Alternatively, for convenience, you can also extract and rebuild all examples available for your platform at once with this series of commands:

buildhost$ cd <IPC_INSTALL_DIR>/examples
buildhost$ make extract
buildhost$ make
buildhost$ make install

Next step is to copy the content of the ‘install’ subdirectory into a location accessible by your target board (e.g. SD card). Run the example on the target using IPC by loading the remote cores like you would with the test applications, then run the example. E.g.:

target# ipc IPU ex02_messageq/debug/server_ipu.xem4 DSP ex02_messageq/debug/server_dsp.xe64T
target# cd ex02_messageq/debug/
target# app_host IPU
4.4.15.3.4.6. Advanced topics
4.4.15.3.4.6.1. Load and unload individual cores while IPC is running (IPC 3.23.01 and above)

In some applications, there may be a need to load or unload cores after the IPC resource manager is already up and running – e.g. change the DSP executable while keeping the IPU running.

In order to load and start a core with an executable, you can do the following after having launched the resource manager:

target# echo <slave executable file path> > /dev/ipc-file/<core name>
target# echo 1 > /dev/ipc-state/<core name>

The first command sets the filename of an executable to be loaded, and the second command loads and starts the core with that executable.

To stop and unload a core, use the following command:

target# echo 0 > /dev/ipc-state/<core name>

Keep in mind that this simply puts the core into reset. If there is any on-going communication between the given core and the others, it is the responsibility of the user application to clean up and terminate IPC on the slave before unloading a core, thus to avoid causing any memory leaks or communication errors.

4.4.15.3.4.6.2. Inspect the state of a slave core (IPC 3.23.01 and above)

To find out the state of a slave core (whether it is running or in reset), issue the following command:

target# cat /dev/ipc-state/<core name>
4.4.15.3.4.6.3. Tracing

When an issue arises, sometimes it is useful to see the output of internal traces from IPC. This section talks about how to view IPC trace from both the host and the slave cores on the QNX command prompt.

Host-side trace output

Trace from IPC user libraries (IPC 3.35 and above)

Trace output from the IPC user libraries is controlled using the environment variable IPC_DEBUG, when launching an application that uses IPC. E.g.:

target# IPC_DEBUG=<level> app_host

where <level> can be set to a value between 1 and 3, with 3 being the most verbose.

Trace from IPC resource manager (IPC 3.35 and above)

To show the trace output of the IPC resource manager in the QNX system log, run the following command:

target# sloginfo -m42

The verbosity of the trace can be controlled using the environment variable IPC_DEBUG_SLOG_LEVEL when launching the IPC resource manager. E.g.:

target# IPC_DEBUG_SLOG_LEVEL=<level> ipc DSP1 ex02_messageq/debug/server_dsp1.xe66

where <level> can be set to a value between 0 and 7, with 7 being the most verbose. The default level is 2.


Slave-side trace output

To show all trace output (including IPC’s) on a given slave core, simply run the following

target# cat /dev/ipc-trace/<core name>

where <core name> corresponds to the name of the slave core which trace output you are interested in (e.g. DSP1, IPU1 or IPU2 for DRA7xx)

Note that older versions of IPC may use a slightly different path that is based on the MultiProc id of the core of interest: /dev/ipc-trace<id>.

4.4.15.3.4.6.4. Building the IPC resource manager in debug mode (IPC 3.35 and above)

When debugging an issue, the user may wish to have the ability to step through the source code in the IPC resource manager. For this to happen, the IPC resource manager needs to be built in debug mode with debug symbols. Adding IPC_DEBUG=1 to the file <IPC_INSTALL_DIR>/qnx/Makefile and rebuilding IPC would do the trick:

ipc3x_dev: utils
    @cd src/ipc3x_dev; \
        make IPC_PLATFORM=$(IPC_PLATFORM) SMP=1 QNX_CFLAGS=$(QNX_CFLAGS) IPC_DEBUG=1
target# make -f ipc-qnx.mak clean
target# make -f ipc-qnx.mak all

4.4.15.3.5. See Also

4.5. OpenCL

OpenCL

4.6. Boot

4.6.1. Overview

This page provides an overview about the Secondary Bootloader support provided in Processor SDK RTOS.

The Secondary Bootloader (SBL) sets-up the PLL clocks, powers on the I/O Peripherals, initializes the DDR, loads the application image into DDR & brings the target cores for applicable SOCs out of reset. Additional details including execution boot flow is covered in more elaborate detail under individual SOC section.

4.6.2. Boot Modes

4.6.2.1. MMCSD

MMCSD bootloader is required to boot target using an SD card containing bootloader and application images. When the board is powered ON the ROM bootloader detects the MMCSD bootloader image and loads it to the internal memory.The bootloader initializes the board, copies the application image from SD card to the DDR memory and gives control to the application.

4.6.2.2. QSPI

A flash device interfaced with QSPI is flashed with QSPI bootloader and application images. When the board is powered ON the ROM bootloader detects the bootloader image from flash device and loads it to the internal memory. The Bootloader initializes the board, copies the application image from QSPI device to the DDR memory and gives control to the application.

4.6.2.3. UART

A Serial connection is used for transferring the bootloader binary from PC to target board through XMODEM protocol. The bootloader on execution prompts for application image to transfer through XMODEM. On providing the path, the application binary is transferred through serial connection to DDR memory and the control is passed to application to execute.

4.6.2.4. MCSPI/SPI

A SPI flash device flashed with MCSPI/SPI bootloader and application images is used for booting the board. When the board is powered ON the ROM bootloader detects the bootloader image from flash device and loads it to the internal memory. The Bootloader initializes the board, copies the application image from flash to the DDR memory and gives control to the application.

4.6.2.5. NAND

NAND flash with bootloader and and application images is used for booting the board. When the board is powered ON the ROM bootloader detects the bootloader image from flash device and loads it to the internal memory. The bootloader initializes the board, copies the application image from flash to the DDR memory and gives control to the application.

Note

For information on boot mode setting, see the applicable EVM Hardware User Guide

4.6.3. Platforms

4.6.3.1. AM57x

4.6.3.1.1. Overview

The Secondary Bootloader (SBL) for AM57xx device initializes the execution environment for multi-core application and this can be used to demonstrate an out-of-box experience.The section covers additional details including execution sequence, tools and additional flashing instructions.

4.6.3.1.2. Bootloader Execution Sequence

  • Power On Reset

  • ROM Bootloader (RBL)

    • Software pre-programmed in ROM memory starts executing

    • Checks Sysboot pins and choose booting device

    • If no valid bootloader found on booting device, RBL checks for next booting device.

    • Platform configuration and initialization.

    • Configures DPLL and clock settings for MPU, and boot media like I2C, MMCSD, SD/MMC, SPI, QSPI, Ethernet etc for reliable boot.

    • The sequence depends on RBL execution flow and Sysboot pins.

    • RBL gets image size and load address by checking TI Image Header appended on bootloader binary(.bin). Check binary formats.

    • Loads the binary to internal memory at the Load address fetched from TI Image Header

    • Passes control to Secondary Bootloader(SBL)

Note

Detailed description of ROM bootloader is provided in Initialization Chapter in AM57xx Technical Reference manual

  • Secondary bootloader(SBL)

    • User level secondary bootloader(SBL) begins execution from internal memory by running basic initialization routines like setting up Stack, BSS and then jumps to main() to begin Board Initialization.

    • Board Initialization is done by a call to Board_init() API.For additional details refer Processor SDK Board Support.

    • It includes setting up PLLs, enabling clocks to all interfaces and modules, performing pinmux and setting up UART console.

    • Once Board Initialization is complete, it enables clocks to the target cores like C66x/DSP, IPU, etc and brings them out of reset.

    • Parses Multicore Application image located in memory device and copies it to DDR memory based on load address for different sections.

    • Once copy is successful it transfers control to application.

  • Application then starts executing from DDR.

Note

  • RBL requires boot loader to be in a special format with a header appended to the binary image. The header shall contain the load address of the bootloader and size of the bootloader image.

  • For more information on the TI header refer TRM document

4.6.3.1.3. Directory structure

Makefiles:

  • PDK_INSTALL_PATH/ti/boot/sbl/board/<EVAL_BOARD>/build: Makefile for bootloader that provides list of source files and library and compiler options to create bootloader binary.

Source Files:

  • PDK_INSTALL_PATH/ti/boot/sbl/board/<EVAL_BOARD>: Source to SBL main function that consolidates all features

  • PDK_INSTALL_PATH/ti/boot/sbl/soc: Source to SOC specific initialization used in the SBL.

  • PDK_INSTALL_PATH/ti/boot/sbl/src: Source to boot media specific initialization used in the SBL.


4.6.3.1.4. Tools and Binary Formats

This section lists out the various tools and scripts used by SBL for different boot modes and those required to create a bootable application image.

SBL/MLO image format: To generate the MLO, SBL uses tiImageGen tool to prepend the sbl.bin image with the TI header information. The image format has been described in detail in the Image Format Section of theAM57xx Technical Reference manual


Application image format: A bootable application image can be created by using the Am57xImageGen script provided under tools folder as part of sbl. It can be located at <PDK_INSTALL_DIR>/packages/ti/boot/sbl/tools/scripts folder.

The Am57xImageGen script uses out2rprc and multicoreImageGen format conversion tools to create the final application image. Graphical view of the multicore application image is provided below:

../_images/Multicore_app_image.png

The script creates the bootable image in 2 steps

Step 1: Conversion to RPRC format conversion

  • Firstly, application executable is converted from ELF/COFF format (.out) to custom TI Rprc binary image using out2rprc tool. This tool strips out the initialized sections from the executable file (i.e. *.out) and places them in a compact format that the SBL can understand. The output (bin) file is typically much smaller than the original executable (out) file.

  • The rprc files are intermediate files in a format that is consumed by MulticoreImageGen tool that generates the final binary.

RPRC File Header Format

Offset

Binary value

0x00000000

Magic Word(43525052)

0x00000004

Entry Point (Location)

0x00000008

Reserved Addr

0x0000000C

Section Count

0x00000010

Version

RPRC Section Header Format

Offset

Binary value

0x00000000

Section start Address

0x00000004

Reserved Addr

0x00000008

Size

0x0000000C

Reserved CRC

0x00000010

Reserved


Step 2: Multicore Image file generation

  • RPRC files for each cores is combined into a single multicore bootable application image.

Multicore boot image format

Meta Header Start

Offset

Binary value

0x00000000

Magic String (0x5254534D)

0x00000004

Number of Files

0x00000008

Device ID

0x0000000C

Reserved

Meta Header per Core

Offset

Binary value

0x00000000

Core ID

0x00000004

Image Offset

Core ID and Device ID for specific devices can be located in the file sbl_target_core_boot.h in the boot/sbl/soc/<SOC_NAME> folder

Refer section App Image Creation for more details on usage of this script and application image creation.

Flashing Tools

SBL provides a CCS based qspi flash writer utility to flash image and multicore AppImage from a SD card to onboard QSPI device. It is located at <PDK_INSTALL_DIR>/packages/ti/boot/sbl/tools/flashWriter/qspi

4.6.3.1.5. Building the SBL

Pre-requisites to Building

  • Set your environment using pdksetupenv.bat or pdksetupenv.sh. Refer to the Processor SDK RTOS Building page for information on setting up your build environment

  • The SBL has following dependencies and will need the following libraries built

    • Board

    • UART

    • I2C

    • SPI

    • CSL

    • OSAL

    • MMCSD

    • PM

Note

Refer to the makefile for the board you are using for the latest driver dependency. These libraries should come pre-built with any fresh installation of the Processor SDK RTOS but may be removed if a gmake clean is invoked

Compiling the SBL Components

To build the SBL components:

  1. cd <PDK>/packages

  2. Run pdksetupenv script

  3. make all -C ti/boot/sbl BOARD=<BOARD_NAME> SOC=<SOC_NAME> BOOTMODE=<BOOTMEDIA>

  • BOARD_NAME : idkAM572x, idkAM571x, evmAM572x, idkAM574x

  • SOC_NAME : AM572x, AM571x, AM574x

SBL can be built for all the AM57x platforms using below command

  1. cd <PDK>/packages

  2. Run pdksetupenv script

  3. cd <PDK>/packages/ti/boot/sbl

  4. make all_sbl_images SBL_PLATFORM=am57xx

This will make the SBL for a specific $BOARD and $BOOT_MEDIA. Output files will be located in: <PDK>/packages/ti/boot/sbl/binary/<BOARD>

Note

Refer <PDK>/packages/ti/boot/sbl/sbl_<DEVICE>.sh for more build options

Boot Modes

This Release of SBL supports MMCSD and QSPI Boot modes. The different boot modes supported for all the boards is tabulated in the table below.

MMCSD

QSPI

eMMC

AM572x GPEVM

YES

NO

YES

AM572x IDKEVM

YES

YES

NO

AM571x IDKEVM

YES

YES

NO

AM574x IDKEVM

YES

YES

NO


4.6.3.1.5.1. Booting Via SD Card
  1. Preparing the SD card.

  2. Booting the target.

4.6.3.1.5.1.1. Preparing the SD card
  1. To boot the target the SD card should be bootable. Follow the steps at Creating bootable SD card in windows or Creating bootable SD card in Linux.

  2. Delete the “MLO” and “app” in the bootable SD card which are created in the process of making the SD bootable.

  3. Copy the sbl binary(MLO) to the SD card.

  4. Copy the Application image(app) generated using the Script to the SD card.

4.6.3.1.5.1.2. Booting the target
  1. Insert micro SD card into the SD card slot of the board.

  2. Open a serial communication terminal like TeraTerm, MiniCom on host PC and connect to the UART console port

  3. Do a power reset of the board to boot the appliation from the SD card.

4.6.3.1.5.2. Booting Via eMMC
  1. Preparing the eMMC.

  2. Booting the target.

4.6.3.1.5.2.1. Preparing the eMMC
  1. To format the eMMC of the target board, Run the following application on the target board USB_DevMsc_mmcsd .

  2. To boot the target the eMMC should be bootable. Follow the steps same as SD card formatting as given at Creating bootable SD card in windows or Creating bootable SD card in Linux except instead of SD card, connect the target board eMMC to the host PC.

  3. Delete the “MLO” and “app” in the bootable eMMC which are created in the process of making the eMMC bootable.

  4. Copy the sbl binary(MLO) to the eMMC.

  5. Copy the Application image(app) generated using the Script to the eMMC.

4.6.3.1.5.2.2. Booting the target
  1. Set boot settings to eMMC by selecting J3, J4 and J6 jumpers on Pin 2 and 3 of AM572x EVM Board.

  2. Ensure there is no SD card inserted in the SD card slot.

  3. Open a serial communication terminal like TeraTerm, MiniCom on host PC and connect to the UART console port

  4. Do a power reset of the board to boot the appliation from the eMMC.

4.6.3.1.5.3. Booting Via QSPI

Booting from QSPI flash involves two steps-

  1. Flashing bootloader and app image to QSPI flash.

  2. Booting the target.

4.6.3.1.5.3.1. Preparing Flash Device

Use the CCS based qspi_flash_writer.out utility provided in <TI_PDK_INSTALL_DIR>/packages/ti/boot/sbl/tools/flashwriter/qspi/<Board>’ to flash the SBL image at offset 0 and application image at offset 0x80000 to the QSPI device.

QSPI device Memory Map:

Offset 0x00

SBL

Offset 0x80000

Application Multicore Image

The images can be flashed into QSPI flash by following steps given below.

  1. Copy QSPI mode SBL image <TI_PDK_INSTALL_DIR>/packages/ti/boot/sbl/binary/<BoardName>/qspi/bin/MLO and application image(app) generated using the Script into the SD card. Rename the bootloader file to ‘boot’ and application image to ‘app’ with no extensions.

  2. Copy ‘config’ file into the SD card, the config file should contain names of the image to be flashed and the offset. A sample config file can be found at <TI_PDK_INSTALL_DIR>/packages/ti/boot/sbl/tools/flashWriter/qspi/config. Do not change the name of the config file. NOTE: “config” file can be used without any modifications if bootloader and application images are renamed to “boot” and “app”. NOTE: Do not rename the bootloader to be copied to SD card as “MLO”, as MMCSD bootloader expects “MLO” and “app” to boot.

  3. Now SD card contains 3 files 1)boot 2)app 3)config files. config file contains the address of boot image as 0x0 and app image as 0x80000. Insert it into the SD card slot.

  4. Connect the board with CCS and and load the prebuilt qspi flash writer application from <TI_PDK_INSTALL_DIR>/packages/ti/boot/sbl/tools/flashWriter/qspi/bin/<BoardName>

  5. Run the QSPI flash writer application. You will see the following logs on the EVM’s UART console.

  6. After the images have been flashed to the QSPI device disconnect from CCS and do a power reset to boot from the QSPI memory.

PDK QSPI Flash Writer!!
Copying boot to QSPI Flash
Copying app to QSPI Flash
Changing read to quad mode
Read mode has been changed to Quad mode
SUCCESS!!!
Flashing completed

Note

  • The file names have to be renamed in such a way that the length of name is less than 9 characters. Any file name less than 9 characters can be used.

  • This application will flash the image at required offset without taking into consideration any overwriting to previously flashed image.

  • It is the responsibility of the user to provide proper offsets.

4.6.3.1.6. Test Application

SBL provides a test application to demonstrate booting of multicore application image on A15 and DSP cores.The multicore sample application uses mailbox for inter-processor communication. It is used to validate the multi-core boot-up use case.

Master application sends wake-up message to the DSP target cores & waits for acknowledgement message from the target cores in an infinite loop.Each target DSP core waits for wake-up message from the master core responds back with an acknowledgement message.

4.6.3.1.6.1. Application Image Creation

Application Image creation involves two steps.

  1. Generating the .outs of applications for individual cores

  2. Combining the .outs of individual cores to create a bootable multicore image

The steps to create the bootable image in Linux and Windows environment are listed below.

Note

  • Valid SOC settings are AM571x/AM572x

  • Valid BOARD settings are evmAM572x/idkAM571x/idkAM572x

4.6.3.1.6.2. Linux Environment

Command to build the test application.

Go to cd (TI_PDK_INSTALL_DIR)\packages\ti\boot\sbl

make example BOARD=<BOARD> SOC=<SOC> to build the application
make example_clean BOARD=<BOARD>

Example:

make example BOARD=idkAM572x SOC=AM572x

To create the final bootable application image use the AM57xImageGen script and follow these steps

1. Set the following environment variable in the shell.  BIN_PATH: Pointing to the path where the AppImage needs to be generated

Ex: export BIN_PATH=$(TI_PDK_INSTALL_DIR)/packages/ti/boot/sbl/binary

2. Edit the script file to point to the application elf files by setting the input application variables.

App_MPU_CPU0: Point to the path where the application .out for A15 MPU is located App_DSP1: Point to the path where the dsp core 1 application is located App_DSP2: Point to the path where the dsp core 2 application is located

export APP_MPU_CPU0=$(TI_PDK_INSTALL_DIR)/packages/ti/boot/sbl/binary/idkAM572x/example/armv7/bin/sbl_app.out

export APP_DSP1=$(TI_PDK_INSTALL_DIR)/packages/ti/boot/sbl/binary/idkAM572x/example/c66/dsp1/bin/sbl_app.xe66

export APP_DSP2=$(TI_PDK_INSTALL_DIR)/packages/ti/boot/sbl/binary/idkAM572x/example/c66/dsp2/bin/sbl_app.xe663

export APP_IPU1_CPU0=$(TI_PDK_INSTALL_DIR)/packages/ti/boot/sbl/binary/idkAM572x/example/m4/ipu1/bin/sbl_app.xem4

export APP_IPU1_CPU0=$(TI_PDK_INSTALL_DIR)/packages/ti/boot/sbl/binary/idkAM572x/example/m4/ipu2/bin/sbl_app.xem4

3. If it is not required to load an application on specific core leave the variable blank.

  1. Run the script file AM57xImageGen found under the path $(TI_PDK_INSTALL_DIR)/packages/ti/boot/sbl/tools/scripts

5. An application image by name app is created in the path pointed by BIN_PATH variable

6. Copy the Bootlaoder image(MLO) and application(app) in the SD card to boot using MMCSD boot mode.

Note

  • The AM57xImageGen.sh script depends on tools like mono to execute the out2rprc.exe.

  • The linux host environment needs to have this tool installed to execute this script.

  • Refer this link to download the mono tool

4.6.3.1.6.3. Windows environment

Command to build the test application.

Go to cd (TI_PDK_INSTALL_DIR)\packages\ti\boot\sbl

gmake example BOARD=<BOARD> SOC=<SOC> to build the application
gmake example_clean BOARD=<BOARD>

Example:

gmake example BOARD=idkAM572x SOC=AM572x

To create the final bootable application image use the AM57xImageGen script and follow these steps

  1. Set the following environment variable in windows command prompt

BIN_PATH: Pointing to the path where the AppImage needs to be generated

Ex:  set BIN_PATH=%TI_PDK_INSTALL_DIR%\packages\ti\boot\sbl\binary

2. Edit the batch file to point to the application elf files by setting the input application variables.

App_MPU_CPU0: Point to the path where the application .out for A15 MPU is located App_DSP1: Point to the path where the dsp core 1 application is located App_DSP2: Point to the path where the dsp core 2 application is located

set App_MPU_CPU0=%TI_PDK_INSTALL_DIR%\packages\ti\boot\sbl\binary\idkAM572x\example\armv7\bin\sbl_app.out

set App_DSP1=%TI_PDK_INSTALL_DIR%\packages\ti\boot\sbl\binary\idkAM572x\example\c66\dsp1\bin\sbl_app.xe66

set App_DSP2=%TI_PDK_INSTALL_DIR%\packages\ti\boot\sbl\binary\idkAM572x\example\c66\dsp2\bin\sbl_app.xe66

set App_IPU1_CPU0=%TI_PDK_INSTALL_DIR%\packages\ti\boot\sbl\binary\idkAM572x\example\m4\ipu1\bin\sbl_app.xem4

set App_IPU2_CPU0=%TI_PDK_INSTALL_DIR%\packages\ti\boot\sbl\binary\idkAM572x\example\m4\ipu2\bin\sbl_app.xem4

3. If it is not required to load an application on specific core leave the variable blank.

  1. Run the batch file AM57xImageGen found under the path $(TI_PDK_INSTALL_DIR)/packages/ti/boot/sbl/tools/scripts

5. Follow the steps 4 to 6 listed above for Linux environment. | Setup Requirements “””””””””””””””””””” For information on board specific requirements like power supply, UART console port connections refer the Hardware User guide of the respective boards.

The configurations needed to setup UART console through a serial terminal application on host PC are listed in the next section.

4.6.3.1.6.3.1. UART Console Setup

PDK SBL prints messages on the UART Serial Console running on the host. Hence, a serial terminal application (like Tera Term/HyperTerminal/minicom) should be running on the host.

The host serial port must be configured at 115200 baud, no parity, 1 stop bit and no flow control. Please ensure that the local echo setting for the terminal is turned off.

4.6.3.1.6.3.2. Loading the test application

Follow these steps to load the test application using a SD card on the target

copy the MLO to your SD card (located at $(TI_PDK_INSTALL_DIR)/packages/ti/boot/sbl/binary/[BOARD]/mmcsd)

  1. copy the example app located at path pointed to by BIN_PATH to your SD card

  2. insert your SD card into your board and power on your board

  3. open teraterm to connect to the board’s UART console

  4. press the “Hard Reset” button on your board

On Successful bootup you should see the following logs on the UART console for a AM572x based board.

../_images/Sbl_example.jpg

Note

MPU Core 0 example does a sequential check of mailbox messages sent from the other cores. On rare occasions, the check happens before the message is sent - the “<core> boot-up Successful” message might not be displayed even though the core(s) were booted successfully.

4.6.3.1.7. Application Integration

4.6.3.1.7.1. Memory Map

Table indicated below provides memory map details for SBL image in OCMC_RAM1.  For more details on pinmux and IO delay requirements refer this link Processor SDK Board Support

We recommend that users should refer to the linker command file and the map file for the boot loader to check for latest information on the memory utilization in the boot loader.

Location of linker command file: <PDK_INSTALL_PATH>/packages/ti/boot/sbl/board/<BOARD>/build

The SBL memory map is shown below

../_images/SBL_memory_map.png

Note

  • After the application boots and is running on the SOC, it is free to use the SBL_MEM region.

  • The pinmux data from the board library and MMU Table are part of the SBL_MEM region indicated in the figure above. If pinmux data needs to be placed at a specific location then users can update the SBL linker command file to add the BOARD_IO_DELAY_CODE and BOARD_IO_DELAY_DATA as described in Application Integration of board library for AM5x

4.6.3.1.8. SBL Customization

4.6.3.1.8.1. Changing boot media offsets

The location at which SBL resides on the flash is predefined by the ROM bootloader spec and so these defaults can`t be changed. However the SBL is a user defined bootloader so many of the defaults can easily be modified to meet application requirements. For example the flash offset location from which the bootloader reads the application is configured in the source files located under <PDK_INSTALL_PATH>/packages/ti/boot/sbl/src/<BOOT_MEDIA>

Examples of customization that can be changed:

  • QSPI/SPI flash offsets: These offsets are configured in sbl_qspi.c and sbl_spi.c

  • MMCSD: The name of the application is hard coded as app in function SBL_MMCBootImage in the sbl_mmcsd.c

4.6.3.1.8.2. Speeding up boot by increasing speed of the boot interface

The SBL for AM57xx devices uses LLD drivers to read and write from boot media supported. The SBL uses the default SOC configuration of the drivers and the speeds setup. For example, the SPI driver default SPI bitrate is 1 MHz (Refer <PDK_INSTALL_PATH>/packages/ti/drv/spi/src/SPI_drv.c) so if you wish to speed up boot you can update the SPI parameter in the SBL as shown below:

SPI_Params_init(&spiParams);
spiParams.bitRate = 24000000U;

The configuration of the driver is usually done in the boot/sbl/soc/<device>/sbl_soc.c file.

  • For SD/MMC: You can configure higher speed and change bus width using MMCSD_v1_HwAttrs_s or MMCSD_v0_HwAttrs_s

  • For QSPI: 2 pin and 4 pin mode, and input frequency is configured using QSPI_HwAttrs in the QSPI driver. Check driver for defaults.

Also, check to see if the CACHE and MMU settings for the ARM core are setup to enable fast boot.

Note

SYSBOOT settings for AM57xx

The SYSBOOT configuration in your hardware using Sitara devices (AM3/AM4/AM5) can play a big role in the time required to boot successfully. On these devices the boot pins configure a boot sequence for the ROM bootloader to check for valid boot image so if you have a preferred boot mode designers are required to use SYSBOOT setup such that the preferred boot media is first in the boot sequence. If the preferred boot media occurs later boot sequence, the boot is likely to add the time required by RBL to check other boot media for an valid image. For example if QSPI is the preferred boot media on your AM57xx hardware then you should have system configure SYSBOOT to boot of QSPI first using SYSBOOT setting for QSPI_1 or QSPI4 for Memory preferred booting or Production booting (Refer: Initialization chapter in TRM).Incorrect SYSBOOT configuration can causes long delays especially if peripheral boot is configured to be one of the preferred boot modes in the boot order


4.6.3.1.8.3. Reducing size of SBL and application

Another way to optimize boot times is to reduce the size of the binary that needs to be loaded by the bootloader by building the app with optimization for code size using -Os (GNU GCC) and for -O<level> when using TI compilers.

Other than compiler based optimizations developers can actively shutdown non-essential modules and features to reduce code size. For example if UART logging is not required or DDR memory is not connected in the system, the initialization functions can be removed to reduce code size.

4.6.3.1.9. Usage Notes

4.6.3.1.9.1. SBL AVS and ABB setup

AVS and ABB configuration is mandated for normal operation of AM57xx devices. All Processor SDK RTOS releases v3.3 and later contain SBL that sets up AVS and ABB configuration features using PM LLD APIs The complete details of PMIC configuration and AVS and ABB configuration required by the chip for different OPP has been implemented in the file:

<PDK_INSTALL_PATH>/packages/ti/boot/sbl/board/src/sbl_avs_config.c If you are using the same PMIC as GP EVM or IDK platform then you can reuse the settings as is in SBL for your custom platform

4.6.3.1.9.2. Configuring entry point for SBL

The two key files that help setup the entry point in the SBL build are “sbl/soc/<SOC_NAME>/sbl_init.S” and the linker command file “sbl/soc/<SOC_NAME>/linker.cmd”. The global symbol Entry is used to provide the entry point to the SBL. The Base address of the memory section SBL_MEM is then used by the tiimage and GP Header tool to provide RBL the guidance to find the entry point to pass control. After MLO is created check the TI image format file(MLO or _ti.bin) or the GP Header file to confirm that the entry point matches the location of Entry symbol in the sbl.map

Note

The object file created by sbl_init.S should always be the first object file in the link order for the symbol Entry to be placed at the BASE address of the memory section SBL_MEM

4.6.3.1.9.3. Debugging application boot

Steps to debug application boot using Processor SDK RTOS bootloader are discussed in the article Common steps to debug application boot

4.6.4. Bootloader Debugging

4.6.4.1. Common Steps to debug application boot

Bootloading an application is a system level issue which is impacted by hardware as well as software setup. There are some common steps that users are expected to check while debugging boot related issues. This section discusses common issues to checkout when debugging application boot.


4.6.4.1.1. Hardware Debug Steps

Input clocks and power sequencing

  • Ensure that correct power sequencing is occurring on your board. Power sequencing specifications can be found in the data manual.

  • Check the system clock and ensure this is outputting the expected frequency and the signal is swinging rail to rail (should be a 1.8V signal). If you have an external oscillator, it should be outputting a square wave at the desired frequency. If you are using a crystal , you should have a sine wave at the desired frequency at XTALIN. XTALOUT should be similar (may be distorted a little).

  • Check the power on reset signal PORZ. This signal should stay low throughout the power sequencing and go high when the power AND high frequency clock are stable.

  • If the device has an OBSCLK or CLKOUT then ensure that this pin provides the correct output that matches your software configurations.

Boot pins:

Best way to confirm what the device ROM bootloader reads from the pins is to read the register where the Boot pins are latched into the device:

  • For AM335x/AM437x: Check the Control_status register

  • For AM57xs: Check CTRL_CORE_BOOTSTRAP register

  • For C66x/K2X devices: Check the DEVSTAT register

  • For OMAPL13x/C674x devices: Check BOOTCFG register

Boot media connectivity

Users are required to test their boot interface by running boot loader independent diagnostic tests that confirm that the SOC can read and write from the boot interface. Processor SDK RTOS provides Diagnostic utilities for K2G/AMXX devices and provides POST utility for keystone devices with which the TI EVM was tested.

TI Technical documentation like Technical reference manual (AMXX devices), Bootloader USer guide (for C66x/K2X devices) and Bootloader Application notes for OMAPLxx/C674x list default behavior or the ROM bootloader and limitations and constraints for each boot mode. Application developers need to account for this while designing their system to ensure smooth bring up process.

Signalling on boot media and reset pins

If software and other hardware debug steps have not helped identify the issue, you may need to hook up a scope or protocol analyzer to check the clocks and the data transfers from the boot media to check if there is any issues that can be identified. Another commonly known issue that impacts boot is noise on the power lines which may cause the SOC to reset or cause a hang on the boot core. Provide these scope shots to TI on E2E forums so TI engineers can confirm that there are no issues.

4.6.4.1.2. Software Debug Steps

SBL and app Entry points

If you don`t see your bootloader executing post boot, a good sanity check is for you to look at the map file for the bootloader and ensure that the entry point matches with the location in the TI or GPHeader in the boot image. this can be done by looking at the entry point in the MLO/_ti.bin/GPheader and ensure that it matches with the location of symbol Entry in the .out/map file for the bootloader.

SBL and App memory map

The SBL execute from device onchip memory so if your application is also using Onchip memory in addition to DDR memory, developers need to ensure that the code sections in the application do not overlap with memory sections that are used by the SBL. This can cause the SBL to hang during the application boot process and go into an abort state.

To check what memory region is used by the SBL please locate the map file created from building the bootloader.

Flash Programming verification

It is essential to understand that the flash programmer that you use has not just the ability to write the image to flash but should also be able to verify that the image written to flash matches the image that you have created on your host machine. you can use TI flash programming utilities as reference and ensure that the production flashing tools use a method to erase, write and then verify the boot image that is written to the flash.


DDR timings and configuration /SBL

The Processor SDK bootloader ships with DDR clock and timing settings that apply to the external memory devices that have been used in TI evaluation platforms. When running the code on custom platforms, users may be required to change these setting to match the timings required by custom design. We highly recommend that users create a GEL script similar to one provided by TI and test the memory interface with the new settings before using them in the secondary bootloader to setup external memory. **Processor SDK Diagnostics** provides a mem_test that can be used to test read and writes to the entire DDR address space for confirming the SOC EMIF settings.

Emulator based debugging of boot

The processor SDK RTOS bootloader is like any other application that can be loaded over the emulator and debugged. Steps to connect an emulator to the EVM have been described in the Hardware Setup Guides that is linked to the Processor SDK RTOS documentation.

Loading the bootloader over emulator can be very useful step in debugging the system boot. Before generating the final binary for bootloader, the build generates the .out file for the bootloader which is then formatted in a boot format that the ROM bootloader(RBL) can interpret. This .out can be loaded over the emulator similar to any other application. If you load the debug version of the bootloader, you can single step through the code that initializes the SOC and also part of the SBL code that loads the app from the boot media.

Note

SBL runs from onchip memory so users can load the SBL.out even without using the GEL file but using the GEL sometimes helps with putting the core in a clean state so usage of the GEL is optional

Alternate Approach:

Add a spinlock in SBL main

In some occasions the RBL may load the SBL but there may be some issue that you need to debug in the SBL. In this case you can add a spinlock loop at the start of main in SBL and allow the first stage boot to complete normally and then connect to the core when you connect the core will be held in the spinlock condition waiting for you to connect.

Example:

volatile int exitSpinLock = 0;
void Debug_spinLock(void)
{
    while (exitSpinLock == 0) { }
}

You will then have to “release” the core by toggling the variable which the while loop is blocking on. You can either do this manually by halting CCS, examining the variable in the watch window and then changing the value before continuing execution on the core, or you can use a GEL script.

Use an IO input to stall the core until user input is received:

printf("Hit enter to continue:");
getchar();

System state at boot failure

A useful data point for TI to debug boot related issues is to isolate commonly known initialization and to understand how far the bootloader has executed correctly before it runs into any issues. Users are required to capture ARM/DSP clocks, Program counter value, Entry points detected, pinmux configuration and confirm DDR initialization and target core states. This helps minimize the number of variables in the system boot and helps us zero in on the most likely cause for the boot failure.

4.6.5. UART AppLoader

4.6.5.1. Overview

UART AppLoader is a standalone application used to download application image over UART from the host PC. Host PC uses the serial console utility to transfer application image to apploader.

The Apploader application loads and executes the application binary to be tested, over UART on the target platform.

4.6.5.2. Supported Platforms

SOC

PLATFORM

Verified Boot Modes

AM335x

AM335x GP EVM

MMCSD

AM335x ICEv2

MMCSD

AMIC110 ICE

SPI

AM437x

AM437x EVM

MMCSD

AM437x IDK

MMCSD

AM571x

AM571x IDK

MMCSD

AM572x

AM572x EVM

MMCSD

AM572x IDK

MMCSD

AM574x

AM574x IDK

MMCSD

K2G

K2G GP EVM

MMCSD

K2G ICE

MMCSD

OMAPL137

OMAPL137 EVM

SPI

OMAPL138

OMAPL138 LCDK

MMCSD

4.6.5.3. Apploader Load Address

SOC

PLATFORM

DDR Size

DDR Start Address

Apploader Load Address

AM335x

AM335x GP EVM

1GB

0x80010000

0xBFE00000

AM335x ICEv2

256MB

0x80010000

0x8FD00000

AMIC110 ICE

512MB

0x80010000

0x9FE00000

AM437x

AM437x EVM

2GB

0x80000000

0xFFE00000

AM437x IDK

1GB

0x80000000

0xBFE00000

AM571x

AM571x IDK

1GB

0x80000000

0xBFDFFFFF

AM572x

AM572x EVM

2GB

0x80000000

0xFFDFFFFF

AM572x IDK

2GB

0x80000000

0xFFDFFFFF

AM574x

AM574x IDK

2GB

0x80000000

0xFFDFFFFF

K2G

K2G GP EVM

2GB

0x80000000

0xFFD00000

K2G ICE

512MB

0x80000000

0x9FE00000

OMAPL137

OMAPL137 EVM

64MB

0xC3000000

0xC6E00000

OMAPL138

OMAPL138 LCDK

128MB

0xC0000000

0xC7E00000

4.6.5.4. Pre-Requisites

  1. Download and install the Processor-SDK RTOS package. See the software product page for your device to get the latest version of this software:

  1. The Uart Apploader binaries are found under <pdk_install_path>/packages/ti/board/utils/uartAppLoader/bin/<board_name>/sd/apploader_Test

Serial Connection

Launch the serial console utility with the following configurations:

  • Baud Rate: 115200

  • Data : 8 bits

  • Parity : None

  • Stop : 1 bit

  • Flow Control : None

Note

Any Serial Console utility that supports xmodem protocol can be used. Verified utility is Teraterm

Note

TI Binaries does not work with Apploader on AM335x/AM437x platforms. Make sure to use non TI binaries for testing on these platforms.

4.6.5.5. AppLoader Usage

  1. Set the sysboot pins to the relevant boot mode(SPI / MMCSD).

  2. Make sure UART connection is established between Target platform and Host PC.

  3. Power cycle the board.

  4. Check the below output on the serial console. The application starts sending character ‘C’

**** PDK SBL ****
SBL Revision: 01.00.09.00 (May 10 2018 - 15:48:57)
Begin parsing user application

SD Boot - file open completed successfully
Jumping to user application...
********* PDK UART Apploader ********
version 0.1 (May 14 2018 - 21:40:11)

Please transfer file with XMODEM protocol...
CCCC

Note

The above log is for reference purpose and content might change based on latest software available in processor SDK release

  1. Now transfer the application binary to be tested over UART using serial console utility.

Note

For example in Teraterm, use the following option to transfer the application binary. File -> Transfer -> XMODEM -> Send

  1. Upon success, the apploader loads and executes the application.

  2. Following is the sample output (using UART Diagnostic application), on the serial console.

Xmodem received 46848 bytes

Copying application image from UART to RAM is done
Begin parsing user application
Jumping to user application...

*********************************************
*                 UART Test                 *
*********************************************

Testing UART print to console at 115.2k baud rate
Press 'y' to verify pass: y
Received: y

Test PASSED!

Note

For SPI Boot Mode: 1. Program Apploader to SPI flash using Uniflash and boot the device from SPI flash. 2. Set the sysboot pins to SPI Boot mode

Note

For MMCSD Boot Mode: 1. Copy MLO to SD card. 2. Rename apploader_Test to app and copy to SD card. 3. Set the sysboot pins to MMCSD Boot mode

4.6.5.6. Rebuilding AppLoader

Apploader pre-built binaries are currently not available for OMAPL137/L138 platforms. Binaries needs to be manually built for these platforms.

Refer Rebuilding Board Utils section to build Apploader.

4.7. MPM

Introduction

The Multiple Proc Manager (MPM) Package distributed with Processor SDK RTOS installer has multiple software modules.
Broadly the modules can be grouped into two categories:
  • Multi Proc Manager Linux module

  • Modules that facilitate Interprocess communication between the multiple cores.

Multi Proc Manager Linux module

The Multi Proc Manager Linux module is used to load and run DSP images from ARM Linux user space. The download and run operations can be exercised by using user space API calls. MPM also provides a MPM Client Application which can be used to load and run DSP through command line.

The Multi Proc Manager package also includes couple of software modules that facilitates Inter processor communication between ARM running linux and DSP running RTOS. These components provide APIs in both ARM linux as well as on DSP RTOS.

MPM Mailbox module

Mailbox is used for exchanging control messages between the host and individual DSP cores.

MPM Sync module

Sync Module implements support for Multicore Barriers and Locks.

4.8. OpenMP

Introduction

As of April 2016, this article is out of date. Please visit here.

This page is intended to be the starting point for all information about OpenMP on C6000. However, at this writing, that is not the case. The information in this article is correct if you are using C667x devices which only contain C66x CorePac DSPs, and not any ARM cores. If you are using a 66AK2H device, then please see MCSDK HPC 3.x OpenMP. OpenMP is not supported on any other C6000 devices.

Still here? After reading this article, you will be able to execute your own OpenMP code on C6000 under control of Code Composer Studio.

Presumptions

  • You are experienced at programming OpenMP applications on hosted systems like Windows or Linux PC’s.

  • You have never programmed anything on an embedded system like those that use C6000 devices.

  • You have never used any TI development tools such as Code Composer Studio (CCS).

Acronyms Used in This Article

  • CCS - Code Composer Studio. Software development environment for creating code to execute on TI devices.

  • SYS/BIOS - Or BIOS for short. Scalable real-time kernel which runs on the DSP.

  • MCSDK - Multicore Software Development Kit. Bundles all the software supplied by TI which runs on the DSP.

Conceptual Overview

This section discusses, at a high level, how different pieces of software from TI work together to form the OpenMP solution.

The OpenMP source code, at the level of OpenMP pragmas and function calls, is the same. Your code must be organized into a project, as that term is defined by CCS. A central element of this project is the SYS/BIOS configuration. SYS/BIOS (or BIOS for short) is a scalable real-time kernel produced by TI. BIOS implements the multi-threading features of OpenMP. An initial BIOS configuration (*.cfg) is supplied. Changing this BIOS configuration may never be required. The build is done within CCS. At the start, system execution is also done within CCS. Besides BIOS, many other software components execute on the DSP to comprise the OpenMP solution. All of these components are delivered together under the name Multicore Software Development Kit (MCSDK).

Installation

Getting the MCSDK installed calls for you to install and configure everything else, so this section focuses on the MCSDK.

Start with the general download page for all flavors of the MCSDK. Follow the link for SYS/BIOS MCSDK for C66x. Get MCSDK version 2.1 or later. That page includes a link to a Getting Started Guide. That is where you will find directions on hardware setup, installing CCS, and so on. Follow all the steps in the Getting Started Guide, up to Running the Demonstration Application. (Actually, go ahead and run the Demo if you want. But it isn’t required.)

Confidence Test

If you are familiar with CCS, you may skip over this section. But, installing and configuring all that software may leave you wondering if you did it all correctly. In this section, simplify things by pretending your system only has one core. This allows you to check whether you have installed most of the software correctly, while ignoring many of the complicated details.

Please check out this set of Getting Started Guides just for CCS. You only need to go through one of them. Choose the one that seems best for you. These guides provide a gentle introduction to those who are new to CCS.

When you load and run your first program, CCS needs to know which core to run it on. You see a dialog box similar this one:

../_images/Select_core.JPG

Check the box only for core 0.

First OpenMP Project

This section walks through a simple hello world example project. Four threads run concurrently. Each thread prints hello world and the thread ID.

Version and HW Information

This section was developed with CCS version 5.2.0.00069, MCSDK version 2.1.0.3, and executed on an C6678 EVM. If your environment differs, you may have to make a few adjustments.

Project Setup

In this section you create a new CCS project. This project is not written from scratch, but uses example source files that come with CCS.

Put CCS in the Edit Perspective. Select: Window | Open Perspective | Other | CCS Edit. To start this new project select: Project | New CCS Project. Enter a Project Name. For Family select C6000. For Variant, ignore the first drop-down box, and in the second choose Generic C66xx Device. In the box just under Project templates and examples type in hello. Under OMP Examples you will see some Hello world example projects. Select the best one for your HW platform. In this screen shot, the selection is for C6678. Select Next.

../_images/Omp_h1_project_setup.jpg

The next dialog shows the RTSC Configuration Settings. If only one version of the MCSDK is installed, then nothing more is required. If more than one version of the MCSDK is installed, review the version numbers of the selected packages and insure they are from the desired MCSDK. Do not forget the drop-down box at the top for the version of the XDCtools.

Click Finish. CCS creates the project, complete with source code, and adds an entry to the Project Explorer window.

Project Modifications

One modification to the example project is required.

In the Project Explorer window, expand the set of files available under the new project. Open the file omp_config.cfg by double-clicking on it. The edit window has two tabs on the bottom left. Select the Source tab. Find the source line which begins var OpenMP … Change the code as follows …

var OpenMP = xdc.useModule('ti.omp.utils.OpenMP');    // no change
OpenMP.setNumProcessors(4);                           // no change
OpenMP.autoDnldCore = false;                          // add this line

The new line disables the feature called auto-download. A side effect of auto-download is that printf works only on core 0.

Save the change to the configuration file by entering control+S or selecting File | Save.

Build and Load

The target configuration that should launch when this OpenMP project is debugged is the same one used for previous single core projects. To check on this detail, choose View | Target Configurations. Find that target configuration and insure it is the default.

In the Project Explorer window, insure the new project is selected.

Select the Debug icon.

../_images/Omp_h2_debug_icon.JPG

Next a dialog will come up which asks which CPU cores to load the program on. Select cores 0-3, then click OK. It will appear similar to this …

../_images/Omp_h3_select_cores.JPG

The following actions take place automatically:

  • CCS changes to the Debug perspective

  • The project is built

  • The target configuration is launched

  • The target connection is made

  • The program is loaded on cores 0-3

  • The system begins execution and runs to the start of main on core 0

Execute and See Output

Now you are ready to execute. In the Debug window, click the Resume execution icon.

../_images/Omp_h4_resume_icon.JPG

The console window will show output similar to, but not exactly the same as, the following …

[C66xx_0] Hello World from thread = 0
[C66xx_0] Number of threads = 4
[C66xx_1] Hello World from thread = 1
[C66xx_2] Hello World from thread = 2
[C66xx_3] Hello World from thread = 3

Because the threads are executing on the cores concurrently, there is no control over the order in which the output appears. But you should see all of these lines.

Tips on System Startup

If things do not go smoothly, please see the article SystemAnalyzerTutorial7. That article is about running a tutorial for a tool named Unified Instrumentation Architecture (UIA). UIA is for analyzing system performance and behavior. This tutorial contains several tips on how to use CCS to run OpenMP programs. It is relevant to the versions of the MCSDK components listed near the beginning.

Second OpenMP Project

Now try another project like hello world, but a bit more complicated. It will serve as the basis for your future OpenMP projects.

Project Setup

Start it the same way as the hello world project. But give it a different name, look for matrix among the examples, and choose the OpenMP matrix vector multiplication example for your system.

../_images/Omp_h5_matvec_setup.JPG

Build and Execute

All the remaining steps are the same as with the hello world project.

Upon executing, output in the console window should appear similar to …

[C66xx_0]
[C66xx_0] Starting values of matrix A and vector b:
[C66xx_0]   A[0]= 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0   b[0]= 1.0
[C66xx_0]   A[1]= 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0   b[1]= 2.0
[C66xx_0]   A[2]= 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0   b[2]= 3.0
[C66xx_0]   A[3]= 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0   b[3]= 4.0
[C66xx_0]   A[4]= 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0   b[4]= 5.0
[C66xx_0]   A[5]= 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0   b[5]= 6.0
[C66xx_0]   A[6]= 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0   b[6]= 7.0
[C66xx_0]   A[7]= 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0   b[7]= 8.0
[C66xx_0]   A[8]= 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0   b[8]= 9.0
[C66xx_0]   A[9]= 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0   b[9]= 10.0
[C66xx_0]
[C66xx_0] Results by thread/row:
[C66xx_0]   thread 0 did row 0   c[0]=55.00 Running total= 55.00
[C66xx_3]   thread 3 did row 9   c[9]=550.00    Running total= 605.00
[C66xx_1]   thread 1 did row 3   c[3]=220.00    Running total= 825.00
[C66xx_0]   thread 0 did row 1   c[1]=110.00    Running total= 935.00
[C66xx_1]   thread 1 did row 4   c[4]=275.00    Running total= 1210.00
[C66xx_2]   thread 2 did row 6   c[6]=385.00    Running total= 1595.00
[C66xx_0]   thread 0 did row 2   c[2]=165.00    Running total= 1760.00
[C66xx_1]   thread 1 did row 5   c[5]=330.00    Running total= 2090.00
[C66xx_2]   thread 2 did row 7   c[7]=440.00    Running total= 2530.00
[C66xx_2]   thread 2 did row 8   c[8]=495.00    Running total= 3025.00
[C66xx_0]
[C66xx_0] Matrix-vector total - sum of all c[] = 3025.00
[C66xx_0]

The parts before and after Results by thread/row should match, particularly the final result of 3025.00. The thread N lines will vary, but there should be one line for each row 0-9.

Start Your OpenMP Project

Build your OpenMP project out of the matrix multiply project. In the Project Explorer window, select the matrix multiply project, right-click and choose Copy. Right-click again and choose Paste. Give the new project a name. It starts as a copy of the matrix multiply project in every respect, except the name. Remove files, add files, and make the project your own. Modifications to the omp_config.cfg file are not required for base functionality.

Debugging Tips

Suppose you want to immediately execute one of those example projects again. You need to reset the CPU cores, reload the program, and run again. Here is a good way to do that.

In the Debug window, select cores 0-3, right-click and choose Group Core(s).

../_images/Omp_h6_group_cores.JPG

Now reset the cores in the newly formed group. Select the group and click the CPU Reset icon near the top right of the Debug window.

CPU Reset icon

../_images/Omp_h7_cpu_reset.JPG

Then reload the program with the menu selection Run | Load | Reload Program. Now you are ready to resume execution as before. Compare with the output you got last time, and see how some lines are in a different order.

How to Obtain Support

Post your questions and suspected bugs to the compiler forum with the tag openmp. Please use the tag. Use of the tag triggers an e-mail to the current support team. Without the tag, the issue may go unnoticed for some time.

4.9. PKTLIB

Introduction

The packet library (PKTLIB) provides higher layer of abstraction for underlying CPPI hardware descriptors to application layer. Functionalities include:

  • Zero copy operation for:

    • Packet split/merge operations

    • Cloning operations

    • Headroom/Tail room addition through merge operation

  • Allocations of packet buffer and descriptors during startup time

  • Allows packet allocation by HW at Rx CPPI DMA

  • Efficient recycling of data buffers including the case of buffers being referenced by multiple CPPI descriptors

Modes of Operation

A pktlib heap can be created in on of the following two modes:

  • Private: only visible on the core where the heap was created

  • Shared: visible across multiple cores

Driver Configuration

Applications can use Pktlib_createHeap() API to create heap(s) in the system and heap properties are specified by the Pktlib_HeapCfg configuration structure. This configuration structure should be populated by the application and passed to the PKTLIB module when a heap is being created via the Pktlib_createHeap() API. Multiple heaps can exist in the system where each heaps properties are specified by the Pktlib_HeapCfg configuration structure.

For details about individual fields of this structure, see the Doxygen help by opening <PDK_INSTALL_DIR>/packages/ti/runtime/pktlib/docs/doxygen/html/index.html.

APIs

API reference for application can be found in below file:

#include <ti/runtime/pktlib/pktlib.h>

Example

Name

Description

Expected Results

pktlibUnitTest Application

Unit Test application to test all APIs
User observes the output printed over the CCS console

Additional References

Document

Location

API Reference Manual

$(TI_PDK_INSTALL_DIR)/packages/ti /runtime/pktlib/docs/doxygen/html /index.html

Release Notes

$(TI_PDK_INSTALL_DIR)/packages/ti /runtime/pktlib/docs/ReleaseNotes _pktlib.pdf

4.10. Network

4.10.2. NDK

4.10.2.1. NDK Overview

The Network Development Kit (NDK) is a platform for development and demonstration of network enabled RTOS applications on TI processors and includes demonstration software showcasing capabilities across a range of network enabled applications. The NDK serves as a rapid prototype platform for the development of network and packet processing applications, or to add network connectivity to existing applications for communications, configuration, and control. Using the components provided in the NDK, developers can quickly move from development concepts to working implementations attached to the network.

The NDK provides an IPv6 and IPv4 compliant TCP/IP stack working with the TI-RTOS Kernel real-time operating system. Its primary focus is on providing the core Layer 3 and Layer 4 stack services along with additional higher-level network applications such as HTTP server and DHCP.

The NDK itself does not include any platform or device specific software. The NDK interfaces through well-defined transport interface, Network Interface Management UNIT(NIMU) to the PDK and platform software elements needed for operation. NIMU support. NIMU provides an interface between the stack and the device drivers through which the stack can talk to multiple instances of a single or various device drivers concurrently.

Network Development Kit Summary

Component Type

Library

Install Package

NDK

Install Directory

ndk_<version>packagestindk

Project Type

Eclipse RTSC

Endian Support

Little

Library Name

For details of the libraries delivered as part of the NDK component, please refer to [1]

Library Path

$(NDK_INSTALL_DIR)packagestindk

Include Paths

NDK_INSTALL_DIR is set automatically by CCS based on the version of NDK you have checked to build with. ${NDK_INSTALL_DIR}packagestindkinc ${NDK_INSTALL_DIR}packagestindkinctools


4.10.2.2. NIMU Transport

The NDK transport component of the PDK currently provided 2 implementations for the NIMU layer as described in the sections below. The following diagram is a high level depiction of the NDK/NIMU architecture with reference to the NIMU implementations. For details for the NDK/NIMU architecture, please refer to NDK Programmer’s Reference Guide [2]

../_images/NDK_ARCH.png

4.10.2.3. NIMU for CPSW

NIMU for CPSW provides a common CPSW interface library for NDK to communicate with when network stack is being implemented in the TI’s Common Platform Ethernet Switch for ethernet packet processing. The library uses the CSL-R based API interfaces to provide NIMU interface for NDK. This package has NDK unit test examples for all supported EVMS as indicated in the table above.

Note

This module is only intended to be used with NDK. As such, users should not tie up to its API directly.

NIMU for CPSW Summary

Component Type

Library

Install Package

PDK

Install Directory

$(TI_PDK_INSTALL_DIR)/packages/ti/transport/ndk/nimu

Project Type

Eclipse RTSC

Endian Support

Little

Library Path

$(TI_PDK_INSTALL_DIR)/packages/ti/transport/ndk/nimu/lib

Reference Guides

None


4.10.2.4. NIMU for ICSS

NIMU for ICSS (PRU-ICSS is Programmable Real-Time Unit Industrial Communications Subsystem) provides a common PRU-ICSS interface library for NDK to communicate with when network stack is being implemented in the PRU-ICSS subsytem for ethernet packet processing (firmware based switch running on PRU’s which are part of the ICSS). The library used the ICSS_EMAC LLD to provide NIMU interface for NDK. This package has NDK unit test examples for all supported Devices as indicated in the table above. For details of the PRU-ICSS, please refer to ICCS-EMAC.

Note: This module is only intended to be used with NDK and requires ICSS-EMAC low level driver. As such, users should not tie up to its API directly.

NIMU for ICSS Summary

Component Type

Library

Install Package

PDK

Install Directory

$(TI_PDK_INSTALL_DIR)/packages/ti/transport/ndk/nimu_icss

Project Type

Eclipse RTSC

Endian Support

Little

Library Path

$(TI_PDK_INSTALL_DIR)/packages/ti/transport/ndk/nimu_icss/lib

Reference Guides

None


4.10.2.5. Examples

4.10.2.5.1. PING Example

All NDK examples using CPSW interface can be found at the following location:

  • $(TI_PDK_INSTALL_DIR)/packages/ti/transport/ndk/nimu/example

All NDK examples using PRU-ICSS interface can be found at the following location:

  • $(TI_PDK_INSTALL_DIR)/packages/ti/transport/ndk/nimu_icss/example

4.10.2.5.1.1. Building the NDK examples
Use pdkProjectCreate.sh for Linux environment or pdkProjectCreate.bat for Windows.
This can be found under the <PDK>/packages folder. The only modification to these scripts, if any, is to update the CCS_INSTALL_PATH variable to point to CCS location if its not in the c:/ti/ccsv6 directory . Please refer to Rebuilding PDK for details of example project creation and how to run the example projects using CCS.
4.10.2.5.1.2. NDK Example Description

For each EVM Type supported, there is a example which demonstates “ping” use case. Once the application is loaded via CCS and run, you will be able to ping the configured IP address as specificed int he examples config file. For example, the config file for NIMU for CPSW for idkAM572x, can be found in ti/transport/ndk/nimu/example/am572x/armv7/bios/nimu_idk.cfg. If you wish to re-configure the IP address of the CPSW interface you will need to modify the following configuration parameters

  • Ip.address = “new ip address”

  • Ip.mask = “new ip mask”

  • Ip.gatewayIpAddr = “new gatewayIpAddr”

If you you do change these settings, you will be required to re-build the Example Project using CCS.

Name

Description

EVM Configuration

Expected Results

SOC Supported

NIMU_BasicExample_evmXXXX_<core>exampleproject

Example demonstrates ping from external source to Gigabit Ethernet port on EVM.

icev2AM335x: Jumpers J18 and J19 need to be set properly to select CPSW or ICSS mode. Pin2 and Pin3 need to be connected for ICSS mode and Pin1 and Pin2 for CPSW mode. Update *.cfg file with static IP to test. NIMU for CPSW test requires connection of configured Ethernet port under test to external PC on same subnet.

Run ping from any other PC in the same subnet.

Ping response from the EVM verifies successful execution of example.

AM335x

AM437x

AM57x

K2G

NIMU_ICSS_BasicExample_evmXXXX_<core>Exampleproject

Example demonstrates ping from external source to PRU-ICSS Ethernet port on EVM.

icev2AM335x: Jumpers J18 and J19 need to be set properly to select CPSW or ICSS mode. Pin2 and Pin3 need to be connected for ICSS mode and Pin1 and Pin2 for CPSW mode. Update *.cfg file with static IP to test. NIMU for CPSW test requires connection of configured Ethernet port under test to external PC on same subnet.

Run ping from any other PC in the same subnet . Ping response from the EVM verifies successful execution of example.

AMIC110

AM335x

AM437x

AM57x

K2G

NIMU_Cpsw_ExampleApp

Example demonstrates ping from external source to Gigabit Ethernet port on EVM.

Update *.cfg file with static IP to test. NIMU for CPSW test requires connection of configured Ethernet port under test to external PC on same subnet.

Run ping from any other PC in the same subnet. Ping response from the EVM verifies successful execution of example.

AM65x

J721E

NIMU_IcssgExampleApp

Example demonstrates ping from external source to PRU-ICSS Ethernet port on EVM.

Update *.cfg file with static IP to test. NIMU for CPSW test requires connection of configured Ethernet port under test to external PC on same subnet.

Run ping from any other PC in the same subnet . Ping response from the EVM verifies successful execution of example.

AM65x

NIMU_BasicClientExample_evmXXXX_<core>Exampleproject

Example demonstrates creating local servers: TCP, UDP, data, null and OOB.

Same as above. Use DHCP by default.

See TI NDK user guide, section 2 Example Applications for how to test.

AM57x

NIMU_emacExampleClient_evmXXXX_<core>Exampleproject

Example demonstrates creating local servers: TCP, UDP, data, null and OOB.

Same as above. Use DHCP by default.

See TI NDK user guide, section 2 Example Applications for how to test.

C665x

C667x

OMAP-L137/8

NIMU_BasicHelloWorldExample_evmXXXX_<core>Exampleproject

Example demonstrates UDP helloworld echo test.

Same as above. Use DHCP by default.

See TI NDK user guide, section 2 Example Applications for how to test.

AM57x

NIMU_emacExampleevmXXXX_<core>Exampleproject

Example demonstrates UDP helloworld echo test.

Same as above. Use DHCP by default.

See TI NDK user guide, section 2 Example Applications for how to test.

C665x

C667x

K2H/K/E/L

OMAP-L137/8

NIMU_DualMacExample_evmXXXX_<core>Exampleproject

Example demonstrates using two EMAC ports with different subnets.

Same as above. Use DHCP by default.

Run ping from any other PC in the same subnet. Ping response from the EVM verifies successful execution of example.

AM57x

NIMU_FtpExample_evmXXXX_<core>Exampleproject

Example demonstrates FTP server with put and get.

Same as above. Use DHCP by default.

From host PC make a FTP connection to EVM: ftp <EVM IP address>. User:user Password:password Test put and get command and show the throughput.

AM335x

AM437x

AM57x

K2G

NIMU_ICSSFtpExample_evmXXXX_<core>Exampleproject

Example demonstrates FTP server with put and get.

Same as above. Use static IP by default.

From host PC make a FTP connection to EVM: ftp <EVM IP address>. User:user Password:password Test put and get command and show the throughput.

AM335x

AM437x

AM57x

K2G

NIMU_FtpCpsw_ExampleApp

Example demonstrates FTP server with put and get.

Same as above. Use static IP by default.

From host PC make a FTP connection to EVM: ftp <EVM IP address>. User:user Password:password Test put and get command and show the throughput.

AM65x

J7

NIMU_FtpIcssg_ExampleApp

Example demonstrates FTP server with put and get.

Same as above. Use static IP by default.

From host PC make a FTP connection to EVM: ftp <EVM IP address>. User:user Password:password Test put and get command and show the throughput.

AM65x

Note

Not all the test examples are supported on all the platforms. The NDK code is hardware agnostic, the NIMU driver however depends on the specific SOC. The NDK code can be ported to different platforms. Some Windows host test applications are available under ndk_3_xx_xx_xxpackagestindkwinapps.

4.10.2.5.1.3. Running NDK example on ARM core of Keystone II devices

Before running the NDK example on ARM core of Keystone II devices(K2H/L/E/G), the following steps need to be performed.

  • Increase the NS_BootTask stack from 2048 to 4096 in netctrl.c:

TaskCreate( NS_BootTask, "ConfigBoot", OS_TASKPRINORM, 4096,(UINT32)hCfg, 0, 0 );
  • Rebuild the NDK

  • Rebuild NIMU driver


4.10.2.7. FAQ

4.10.2.7.1. How to check which versions of NIMU driver is for my SOC?

There are several versions of NIMU driver for different SOCs. Please check packagestitransportndknimubuildmakefile.mk

  • V0: C6657

  • V1: C6678

  • V2: K2H, K2K

  • V3: K2L, K2E

  • V4: AM572x, AM571x, AM437x, AM335x

  • V5: K2G

  • V6: OMAP-L137/138

  • V7: AM65x, J7


4.10.2.7.2. Is there any multicast streams limitation using the NDK?

In the NDK, the limit is defined by a macro at ti/ndk/stack/igmp/igmp.c: #define IGMP_MAX_GROUP 32 It is then used to create an array of IGMP records: static IGMP_REC igmp[IGMP_MAX_GROUP]; The IGMP_MAX_GROUP value can be increased, then rebuild the NDK stack.

4.10.2.7.3. How to share a TCP/IP socket across tasks?

The basic building block of NDK stack code internally is an object handle. Internally to the stack, both sockets and pipes are addressed by object handles. However, at the application level, sockets and pipes are treated as file descriptors. To share a SOCKET, i.e. a file descriptor, a task must first allocate a file descriptor table by calling the function fdOpenSession(), then use the function fdShare() to share the file descriptor among multiple tasks. As described in NDK API reference guide, fdShare() is useful in a case where Task A opens a session and calls recv() in a loop on a socket. Task B has a loop that calls send() on the same socket. The call to send() from Task B will fail and then fdError() will return -1 if you do not call fdOpenSession() and then fdShare() from the second Task after the first Task has opened the socket. For an example that calls fdShare(), see the contest.c file in the <NDK_INSTALL_DIR>/packages/ti/ndk/tools/console directory.

4.10.2.7.4. How to tune TCP buffer size for an optimal throughput?

The default TCP buffer size, CFGITEM_IP_SOCKTCPTXBUF/CFGITEM_IP_SOCKTCPRXBUF is 8192 defined in packages/ti/ndk/inc/stack/inc/resif.h of NDK package and can be re-configured in RTOS config file,e.g. Tcp.transmitBufSize = 16384; Tcp.receiveBufSize = 65536; NDK also provides a global TCP statistics counter structure NDK_tcps (ti/ndk/inc/stack/inc/tcpif.h) that can be analyzed in CCS View Expressions window, similaly, there is a global IP statistics counter structure NDK_ips (ti/ndk/inc/stack/inc/ipif.h).

4.10.2.7.5. Why a pipe creation fails and fdError() returns -1?

Although sockets can be used for inter-task communications, it is not the most efficient method. The stack provides a second data communications model called pipes, which allow for local connection oriented communications. As a pipe is a full duplex connection oriented file descriptor, fdOpenSession() needs to be called, which opens a file descriptor session on a task thread so that the task can begin using file descriptor and other stream IO functions.

4.10.2.7.6. How do I change the PBM buffer?

You can configure the PBM buffer used by the NDK by opening the .cfg file with XGCONF, then clicking the Buffers button. This page lets you configure the buffer size, number of buffers and locations of the NDK Packet Buffer Manager (PBM) and the Memory Manager Buffer. Or, you can edit ndk_3_xx_xx_xxpackagestindkstackpbmpbm_data.c (PKT_NUM_FRAMEBUF and PKT_SIZE_FRAMEBUF) and rebuild the NDK.

4.10.2.7.7. Do you have any raw packet example?

A raw socket is used to receive raw packets. This means packets received at the Ethernet layer will directly pass to the raw socket. Stating it precisely, a raw socket bypasses the normal TCP/IP processing and sends the packets to the specific user application. Please check the TI Network Developer’s Kit (NDK) API Reference Guide http://www-s.ti.com/sc/techlit/spru524.pdf A.15 Raw Ethernet Module for details. Code example available in NIMU_emacClientExample_evmXXXX_<CPU>ExampleProject

4.10.2.7.8. How do I enable the jumbo packet support?

Jumbo frames have packet sizes larger than 1500 bytes. Jumbo frame support can be built into an application by linking with libraries compiled for Jumbo frame support. Two parts of changes are needed for NDK/NIMU example: 1. NDK libraries: The NDK libraries would have to be recompiled with the following pre-processor definition added: _INCLUDE_JUMBOFRAME_SUPPORT. 2. NIMU library: the CPSW switch has to be configured to support jumbo packet size and rebuilt. The coding is SOC specific, V0, V4, V5, V6 and V7 NIMU drivers use structure EMAC_OpenConfigInfo and pass the maximum packet size into max_pkt_size field. V1, V2 and V3 NIMU drivers use structure NETIF_DEVICE and pass the maximum packet size into mtu field. Finally, please rebuild the test application with updated NDK and NIMU libraries. A reference example for C6678 is available at: https://processors.wiki.ti.com/index.php/Enabling_Jumbo_Packet_Support_for_C6678. Note the work was implemented on earlier BIOS MCSDK package which was obsolete.

4.10.2.7.9. Keystone PA or NDK example doesn’t work in other boot mode than no-boot mode using CCS/JTAG?

When running the program with CCS/JTAG, the default GEL file initializes the SOC. Same initialization, like turning on all the relevant power domains, and configuring the SGMII, Serdes, may not be implemented in other boot modes. First check GEL file under ccs_baseemulationboards<boards>gel, function Global_Default_Setup() function to add the missing initializations into the application. Next, look for any boot mode dependent code in the application. For example, passPowerUp() is called in no_boot mode to turn on PA, this has to be executed in your application when booting from other boot modes as well.

4.10.2.8. Additional Documentation References

Document

Location

NDK Programmer’s Reference Guide

http://www-s.ti.com/sc/techlit/spru524.pdf

NDK User’s Guide

http://www-s.ti.com/sc/techlit/spru523.pdf

Network Developers Kit FAQ

Network Developers Kit FAQ

NDK Support Package Ethernet Driver Design Guide

http://www-s.ti.com/sc/techlit/sprufp2.pdf

Rebuilding_the_NDK_Core Rebuilding NDK Core

https://processors.wiki.ti.com/index.php/ Rebuilding_the_NDK_Core


Introduction

The NWAL (Network Adaptation Layer) driver provides a well-defined set of APIs which could be used for applications interfacing with NetCP (Network Coprocessor) module in Keystone family of SOCs (C66x, K2x).

Driver Features

  • Initialization of NetCP low level driver resources

  • Initialization of Packet DMA related resources associated with NetCP

  • Classification of incoming packets based on L2: MAC header fields

  • Classification of incoming packets based on L3: IPv4/IPv6 header fields

  • Routing of packets to host based on L4: UDP/TCP/GTP-U

  • Unidirectional IPSec SA creation and deletion

  • In band offload of IPSec encryption/decryption for the outgoing packets

  • Access to SA data mode acceleration for data plane applications.

  • Supports offload of the following features to NETCP Hardware during transmission of packets:

    • IPv4 checksum/L4:TCP/UDP checksum/IPSec Encryption

    • Redirection of packets through a specific MAC port

    • Software Insertion of L2/L3/L4 header

  • Upon reception of packet, module provides additional meta data details including:

    • Status of IP checksum/UDP/TCP checksum results

    • Offset to L2/L3/L4 protocol offsets. Appropriate layer offset will be valid only if classification or routing is enabled at NETCP

    • Ingress MAC port information

Modes of Operation

Following modes of operations are supported by NWAL when sending control configuration request to configure NetCP firmware. BLOCKING Mode: In this mode, status of API request used to configure NetCP is returned back in API call context.

Non-BLOCKING Mode: In this mode, application can invoke a separate poll routine to retrieve status response result.


Driver Configuration

The driver configures the NWAL subsystem using the nwalGlobCfg_t structure.

This structure must be initialized before the nwal_create() function API is called and cannot be changed afterwards. For details regarding individual fields of this structure, see the Doxygen help by opening PDK_INSTALL_DIR/packages/ti/drv/nwal/docs/doxygen/html/index.html.

APIs

API reference for application can be found in below file:

#include <ti/drv/nwal/nwal.h>

Example

Name

Description

Expected Results

nwalUnitTest Application

Unit Test application to test all APIs
User observes the output printed over the CCS console

Additional References

Document

Location

API Reference Manual

$(TI_PDK_INSTALL_DIR)/packages/ti/drv/nwal/docs/doxygen/html/index.html

Release Notes

$(TI_PDK_INSTALL_DIR)/packages/ti/drv/nwal/docs/ReleaseNotes_NWAL_LLD.pdf

4.11. Transport

4.11.1. TimeSync (PTP)

4.11.1.1. Overview

4.11.1.1.1. Terms and Abbreviations

Acronym

Description

PTP

Precision Time Protocol

P2P

Peer to Peer

E2E

End to End

TC

Transparent Clock

OC

Ordinary Clock

BC

Boundary Clock

Controller

PTP controller

Device

PTP Device

BMCA

Best Controller Clock Algorithm

RCF

Synchronization/(Frequency Compensation) Factor

IEC 61558

PTP profile for network control systems

4.11.1.1.2. Introduction

This document details the user guide aspects of PTP/1588 v2 controller/device implementation on PRU-ICSS.

Dual EMAC firmware implementation supports P2P and the implementation is unique to Emac. The controller capability added to the TimeSync driver is currently limitted to sending Sync and Announce frames. The BMCA/Management part of PTP controller is not implemented.

TimeSync (PTP) driver does the task of

  1. Running BMCA

  2. Calculating RCF and Sync packet interval.

  3. Performing clock synchronization

  4. Calculating peer and line delay

  5. Sending Delay Request and Delay Response frames (includes Pdelay Req and Pdelay Res)

Driver Configuration

Board Specific Configuration

All board specific configurations like enabling clock and pin-mux are required before calling any driver APIs. By default Board_Init() API available under board module supports all initialization sequence for TI supported EVMs. In addition it initializes UART instance for Console/STDIO. Refer Processor SDK RTOS Board Support for additional details.

TimeSync Configuration Structure

The TimeSync driver needs to be initialized/configured with initial parameters during the driver initialization.

icss_timeSync_init.c binds driver with TimeSync parameters.

TimeSync_drvInit() API triggers all static configuration information available through parameters. Once initialization is complete the driver is ready for use.

Please refer to TimeSync_ParamsHandle structure defined under ‘<pdk>/packages/ti/transport/timeSync/include/icss_timeSyncApi.h’

4.11.1.1.3. APIs

API reference for Application:

#include <ti/transport/timeSync/icss_timeSync.h>

Below sequence indicates API calling sequence for a simple use case of timeSync controller operation

...
Board_init(boardCfg);
timeSync_example_configureInterrupts(EMAC_PORT_NUM, &emacCfg);
ICSS_EmacSocSetInitCfg(instance, emacBaseAddr);
ICSS_EmacInit();
/* Assumption: firmware is downloaded and ICSS_EMAC driver is initialized */
TimeSync_drvInit(timeSyncHandle);
TimeSync_drvEnable(timeSyncHandle);
TimeSync_enableController();
While(1)
{
    Delay();
}

Below sequence indicates API calling sequence for a simple use case of timeSync device operation

...
Board_init(boardCfg);
timeSync_example_configureInterrupts(EMAC_PORT_NUM, &emacCfg);
ICSS_EmacSocSetInitCfg(instance, emacBaseAddr);
ICSS_EmacInit();
/* Assumption: firmware is downloaded and ICSS_EMAC driver is initialized */
TimeSync_drvInit(timeSyncHandle);
TimeSync_drvEnable(timeSyncHandle);
TimeSync_disableController();
While(1)
{
    Delay();
}

NOTE :

  1. The TimeSync Tx callback interrupt is mapped to the ISR TimeSync_txTSIsr(), where the two PRU interrupts configured to ARM interrupt txIntNum are PRU_ARM_EVENT_6 and PRU_ARM_EVENT_7.

  2. The PTP frames are received via the Real Time (RT) call back from ICSS_EMAC driver and hence RT call back needs to be implemented at the application for ICSS-EMAC driver.

4.11.1.1.4. Tasks Internally created from TimeSync_drvInit()

Below tasks would be created after Application calls TimeSync_drvInit() function. The sample osal implementation is provided under <pdk>/packages/ti/transport/timeSync/example/src/common/timeSync_example_osal.c implementing these tasks.

Handle Name

Function Name
What it does

timeSync_pDelayReqSendTask

TimeSync_PdelayReqSendTask()

Send Peer Delay Requests periodically on both ports. Delay is configurable. Only applicable for P2P configuration

timeSync_delayReqSendTask

TimeSync_delayReqSendTask()

Send Delay requests to PTP Controller. This is currently done for every Sync frame. Pends indefinitely on a semaphore.

timeSync_TxTSTaskP1

TimeSync_TxTSTask_P1()

Process Tx timestamp for Port 1. Pends on an event posted by Tx ISR indefinitely.

timeSync_TxTSTaskP2

TimeSync_TxTSTask_P2()

Process Tx timestamp for Port 2. Pends on an event posted by Tx ISR indefinitely.

timeSync_syncTxTask

TimeSync_SyncTxTask()

Sends Sync frames on both ports in Controller mode. Pends indefinitely on a semaphore posted by the DM Timer ISR

timeSync_announceTxTask

TimeSync_AnnounceTxTask()

Sends Announce frames on both ports periodically

timeSync_NRT_Task

TimeSync_NRT_Task()

NRT stands for non real time. Processes Peer delay frames in the background and calculate peer delay.

timeSync_backgroundTask

TimeSync_BackgroundTask()

Checks for Sync timeout and performs offset stabilization.

4.11.1.1.5. Building timeSync Examples/Unit Test

4.11.1.1.6. Sample Examples

Name

Description

Expected Results

timeSync_BasicExample

Simple timeSync example demonstrating sync between Controller and Device

Clock Sync results at Device

4.11.1.2. Additional References

Document

Location

API Reference Manual

$(TI_PDK_INSTALL_DIR)/packages/ti /transport/timeSync/docs/doxygen/html/index .html

4.12. Tools

../_images/E2e.jpg

For technical support please post your questions at http://e2e.ti.com.