Using the uPP DSP/BIOS Driver

From Texas Instruments Wiki
Jump to: navigation, search

Important Note - Read This First!

When this page was originally written, there was no uPP driver in the BIOS PSP for OMAP-L1 or C674x. That omission has since been addressed, and developers are encouraged to use the uPP driver from the current PSP release. The PSP can be downloaded from the following website:

This page describes the original driver that was created before the PSP added uPP support. It does not apply to the newer PSP driver.


The universal parallel port (uPP) peripheral provides a high-speed parallel data interface with a relatively simple protocol to chips such as OMAP-L138 and C6748. It provides a clean, fast alternative to other peripherals such as HPI or data-transfer-through-video-port kludges. It is also well-suited to connect directly to high-speed analog-to-digital converters (ADCs) or digital-to-analog converters (DACs). For more information on the uPP peripheral, please refer to the uPP user guide, available on the OMAP-L138 and C6748 product websites.

This topic describes an IOM-compliant driver that enables a DSP application (using DSP/BIOS) to quickly and easily make use of the uPP peripheral. This driver is DSP-specific and cannot be used directly by an ARM application. The driver exposes uPP functionality to DSP applications via the GIO standard API. It supports most features of the uPP peripheral:

  • Independent channel operation through a single GIO handle
    • uPP channels A and B
    • Transmit and receive with one or both channels
    • 2D DMA programming for each channel
      • Uses uPP internal DMA, not system EDMA
    • Transfer queueing for each channel
  • Synchronous or asynchronous driver model
  • Clock source selection for uPP peripheral
  • Allows user callback functions for uPP interrupts
  • Digital loopback (DLB) mode for debug purposes

Many of these features are demonstrated by a demo application, which is included with the driver installation package in the "test" folder.


The uPP driver for DSP/BIOS will ultimately be included with the PSP installation for all relevant devices. As a temporary mesaure, the driver is available for download separately from this website. Simply download and execute the stand-alone installer.

You will also need the following software packages:

  • DSP/BIOS v5.33.05 or higher
  • C6000 Code Generation Tools v6.1.8 or higher

Static Configuration

Like any other IOM-compliant driver, the first step to using the uPP driver is to add the necessary items to your DSP/BIOS text configuration file (TCF). The following lines tell DSP/BIOS to create the uPP device handle and run an initialization function (provided by the driver) when the application begins:

bios.UDEV.instance("UPP").fxnTableType = "IOM_Fxns";
bios.UDEV.instance("UPP").comment = "uPP IOM mini driver";
bios.UDEV.instance("UPP").initFxn = prog.extern("UPP_INIT");
bios.UDEV.instance("UPP").fxnTable = prog.extern("UPP_FXN_TABLE");
bios.UDEV.instance("UPP").params = prog.extern("UPP_DEV_CONFIG");

There are several noteworthy items in this configuration block:

  • The fxnTableType item tells DSP/BIOS that this is an IOM driver.
  • The IOM function table, UPP_FXN_TABLE, and intialization function, UPP_INIT, are both provided by the driver.
  • Two structs must be provided by your application code:
    1. UPP_DEV_CONFIG is a struct containing static configuration for the driver. It should be declared using the const modifier.

The application-specified struct is defined in the demo application's main.c source file using a default value macro, UPP_STATICCONFIG, from the upp_md.h header file. To use non-default parameters, this struct should instead be defined using a standard struct declaration:

// static init struct for uPP driver
const upp_StaticConfig UPP_DEV_CONFIG =
    upp_sync,        // synchronous operation (non-default)
    upp_pll1sysclk2  // PLL1 SYSCLK2 (non-default)

System Considerations

In addition to initializing the uPP device, three requirements must be satisfied in the application's global configuration:

  1. There must be at least one system heap for dynamic memory allocation
  2. The Event Combiner Module (ECM) must be enabled
  3. One hardware interrupt (HWI) must be assigned to handle ECM block 2, which includes the uPP interrupt event

The demo application's TCF includes the following lines to satisfy the above conditions:

bios.MEM.instance("IRAM").createHeap = 1;
bios.MEM.BIOSOBJSEG = prog.get("IRAM");
bios.MEM.MALLOCSEG = prog.get("IRAM");

bios.ECM.ENABLE = 1;
bios.HWI.instance("HWI_INT7").interruptSelectNumber = 2;

Note that the heap does not need to be placed in internal memory. Similarly, the interrupt assigned does not need to be HWI 7. Any available memory segment or HWI can be assigned these tasks.

Run-Time Initialization

When the static configuration requirements are met, the uPP driver can be opened and used at run-time using the GIO standard API. This begins with a call to GIO_open to create a driver handle.

GIO_Handle upph = GIO_create("/UPP", IOM_INOUT, &status, &upp_setup, NULL);

The arguments passed to GIO_create are summarized as follows:

  1. The uPP device name declared in the DSP/BIOS TCF. For the demo application, this is simply "UPP." A slash is pre-pended to that name to obtain the string required by the GIO API.
  2. IOM_INOUT must be used for the driver mode, since uPP consists of two data channels that operate independently.
  3. Integer pointer used to report errors during driver initialization
  4. Pointer to a struct of type upp_UserParams. This struct is used to configure the uPP peripheral.
  5. Reserved; set to NULL.

The fourth argument contains the bulk of uPP configuration and must be initialized by the application code prior to calling GIO_create. Typically, it should be initialized by copying the UPP_USERPARAMS struct, provided by the driver as a default configuration:

upp_UserParams upp_setup = UPP_USERPARAMS;

With no additional configuration, this will place the uPP driver in a single-channel transmit mode with an 8-bit data width. By modifying individual elements of upp_setup, this behavior can be modified significantly. See the description of the #upp_UserParams Struct for details. The struct and its enumerated elements are defined in the driver include file, upp_md.h.

The driver offers many configuration options, but using the default UPP_USERPARAMS struct sets most of them to "safe" values (see the driver source file upp_md_defaults.c for details). The following code illustrates how the demo application places uPP in internal loopback mode (A-to-B).

upp_setup = UPP_USERPARAMS;
upp_setup.params.channels = 2;
upp_setup.params.special_mode = upp_digital_loopback;
upp_setup.params.A.direction = upp_dir_xmit;
upp_setup.params.A.clock_div = upp_clock_div;
upp_setup.params.A.fxn_eow = (upp_CbFxn)LOCAL_upp_complete_cb;
upp_setup.params.A.fxn_dpe = (upp_CbFxn)LOCAL_upp_error_cb;
upp_setup.params.A.fxn_err = (upp_CbFxn)LOCAL_upp_error_cb;
upp_setup.params.A.fxn_uor = (upp_CbFxn)LOCAL_upp_error_cb;
upp_setup.params.B.direction = upp_dir_rcv;
upp_setup.params.B.clock_div = upp_clock_div;
upp_setup.params.B.fxn_eow = (upp_CbFxn)LOCAL_upp_complete_cb;
upp_setup.params.B.fxn_dpe = (upp_CbFxn)LOCAL_upp_error_cb;
upp_setup.params.B.fxn_err = (upp_CbFxn)LOCAL_upp_error_cb;
upp_setup.params.B.fxn_uor = (upp_CbFxn)LOCAL_upp_error_cb;

Note that upp_clock_div is a local macro in the application code, and that LOCAL_upp_complete_cb and LOCAL_upp_error_cb are local functions in the application code.

Sending and Receiving Data

After the driver handle has been created, transmitting or receiving data via uPP is as simple as calling GIO_write or GIO_read, respectively.

status = GIO_read(upph, &upp_xfer_b, NULL);
status |= GIO_write(upph, &upp_xfer_a, NULL);

The arguments passed to GIO_read and/or GIO_write are summarized as follows:

  1. The uPP driver handle obtained from GIO_create
  2. Pointer to a struct of type upp_Transfer. The driver uses this struct to program the channel's DMA descriptors.
  3. Reserved; set to NULL.
// start with default settings
upp_Transfer upp_xfer_a = UPP_TRANSFER, upp_xfer_b = UPP_TRANSFER;
// apply common settings
upp_xfer_a.windowAddr = upp_buffer_a;
upp_xfer_a.lineCount = upp_line_count;
upp_xfer_a.lineSize = upp_line_size;
upp_xfer_a.lineOffset = upp_line_offset; = upp_A;
upp_xfer_b.windowAddr = upp_buffer_b;
upp_xfer_b.lineCount = upp_line_count;
upp_xfer_b.lineSize = upp_line_size;
upp_xfer_b.lineOffset = upp_line_offset; = upp_B;

Note that most of the fields in the upp_Transfer struct correspond with the standard concepts of 2D DMA programming: the window address, line count, line size, and line offset. For more information on these concepts, refer to the uPP user guide.

The final member of the upp_Transfer struct simply tells the uPP driver to which channel to apply this transfer.

Synchronous versus Asynchronous

Part of the uPP driver's static configuration places the driver in synchronous or asynchronous mode. There is only one difference between these modes.

  • In synchronous mode, a call to GIO_write or GIO_read will not return until that transfer is complete.
  • In asynchronous mode, a call to GIO_write or GIO_read will return immediately after the DMA descriptors are programmed.

This different behavior has many implications. First, since synchronous mode blocks the application during a uPP transfer, it is impossible to queue multiple transfers on the same channel or to execute simultaneous transfers on channels A and B using a single application thread. This means that the demo application will not work in loopback mode if the driver is operated in synchronous mode; the first call to GIO_read will never return.

Since a read or write call returns immediately in asynchronous mode, it is possible to make another read or write call before the previous operation ends. When this happens, the uPP driver enqueues the second transfer. When the active transfer completes, the driver automatically starts the next transfer in the queue if one is available.

By default, the application is not notified when a transfer completes in asynchronous mode. The use of user callback functions (particularly for the EOW event) becomes an important tool for tracking the status and completion of uPP transfers. The demo application demonstrates this with separate callback functions for EOW and various error events.

Error Recovery

To recover from a uPP error condition at run time, use the GIO_control API with the upp_ioctl_sw_reset IOCTL.

GIO_control(upph, upp_ioctl_sw_reset, NULL);

This will abort the active uPP transfer and clear the first pending transfer, which is already programmed in the uPP DMA descriptors. If additional transfers have already been queued up (in the driver's internal QUEs), the driver will automatically begin the first transfer from the QUE.

In synchronous mode, the driver will return from a GIO_read or GIO_write call immediately with an error code when an error condition occurs. The upp_ioctl_sw_reset IOCTL should then be applied before attempting additional uPP transfers.

Driver APIs and Data Types

Exposed APIs

The uPP driver provides no functions that are intended to be called directly by application code. Instead, it is accessed via its IOM function table. These functions are called in response to application calls to GIO_create, GIO_submit (including its wrappers, GIO_read and GIO_write), GIO_control, and GIO_delete. The function table provided in the driver source upp_md.c maps IOM functions to various internal functions:

    IOM_UNBINDDEVNOTIMPL,  // upp_mdUnBindDev not implemented

Neither these functions nor any of the various static functions in upp_md.c are intended to be called directly by application code. The driver should be controlled via calls to GIO functions at all times.

Useful Data Types


DSP/BIOS requires this struct to configure the uPP driver during system initialization. A struct of this type must be included in your application code and referenced in the DSP/BIOS static configuration (i.e. in the TCF).

Element Type Allowed Values Description
mode upp_DrvModel upp_sync


Configures the driver in synchronous or asynchronous operation. In synchronous mode, calls to GIO_submit will not return until the uPP transfer completes. In asynchronous mode, calls to GIO_submit return immediately.
tx_clk_src upp_ClockSrc upp_pll0sysclk2




Selects the source for the uPP transmit clock. The three options correspond to PLL0 SYSCLK2, PLL1 SYSCLK2, and the UPP_2XTXCLK pin. Refer to your device's system reference guide for more information.

upp_Transfer Struct

This struct is passed (by reference) to GIO_read and GIO_write and defines the 2D DMA transaction that will control the uPP transfer. Refer to the uPP User Guide for more information about 2D DMA concepts.

Element Type Allowed Values Description
link QUE_Elem {NULL, NULL} Allows upp_Transfer struct to be placed on the driver's internal QUE objects. Should be initialized to {NULL, NULL} by the application before upp_Transfer is passed to the uPP driver.
channel upp_Channel upp_A


Specifies which channel will execute the uPP transfer.
windowAddr Ptr Starting address of the uPP data buffer. Must be aligned to multiple of 64 bits (8 bytes).
lineSize Uint16 Number of bytes per DMA line. Must be an even number and greater than zero.
lineCount Uint16 Number of lines in the DMA transfer. Must be greater than zero.
lineOffset Uint16 Offset (in bytes) between the start of each DMA line. Must be aligned to a multiple of 64 bits (8 bytes)

upp_UserParams Struct

This struct is passed (by reference) to GIO_create to configure the uPP device. Most parameters are included as part of the params element of this struct.

Element Type Allowed Values Description
uppObj upp_Obj* NULL


Optional pointer to existing upp_Obj struct allocated by application code. If passed as NULL, the driver will allocate its own struct dynamically.
params upp_Params Parameter struct to configure uPP device. This struct and its elements are described in detail below.
segID Int32 Memory segment from which the driver will allocate objects. Must be a valid segment ID usable by DSP/BIOS MEM module.

upp_Params Struct

This struct and its children (type upp_ChanParams) specify most of the configuration for the uPP device.

Element Type Allowed Values Description
channels Uint8 1


Number of active uPP channels
A upp_ChanParams Parameter struct to configure uPP channel A. This struct and its elements are described in detail below.
B upp_ChanParams Parameter struct to configure uPP channel B. This struct and its elements are described in detail below.
dma_i_thresh upp_Threshold upp_thresh_64



Read burst size for internal DMA channel I
dma_q_thresh upp_Threshold upp_thresh_64



Read burst size for internal DMA channel Q
special_mode upp_SpecialMode upp_none


Special mode selection. Allows the use of internal loopback (or DLB) mode for debug purposes


This struct configures an individual uPP channel (A or B). The uPP channels are independent and do not need to be configured using similar parameters except in DLB mode.

Element Type Allowed Values Description
direction upp_Direction upp_dir_xmit


Operating direction
clock_div Uint8 0 - 15 Clock division value. Used in transmit mode only
data_rate Uint8 1


Selects single or double data rate. Refer to uPP User Guide for more information
width Uint8 8 - 16 Data width in bits
format upp_DataFormat upp_format_ljzf



Data format. Used only when width is between 9 and 15. Refer to uPP User Guide for more information
xmit_thresh upp_Threshold upp_thresh_64



Transmit write threshold. Used in transmit mode only
drive_idle Uint8 0


Boolean: drive value from IVR when channel is idle. Used in transmit mode only
idle_value Uint16 Value to program to IVR
en_start Uint8 0


Boolean: use START signal
en_enable Uint8 0


Boolean: use ENABLE signal
en_wait Uint8 0


Boolean: use WAIT signal
inv_start Uint8 0


Boolean: invert polarity of START signal
inv_enable Uint8 0


Boolean: invert polarity of ENABLE signal
inv_wait Uint8 0


Boolean: invert polarity of WAIT signal
inv_clock Uint8 0


Boolean: invert polarity of CLOCK signal
gen_eow Uint8 0


Boolean: Generate interrupt for end of window (EOW) events
gen_eol Uint8 0


Boolean: Generate interrupt for end of line (EOL) events
gen_uor Uint8 0


Boolean: Generate interrupt for underrun/overflow (UOR) events
gen_err Uint8 0


Boolean: Generate interrupt for internal error (ERR) events
gen_dpe Uint8 0


Boolean: Generate interrupt for DMA programming error (DPE) events
fxn_eow upp_CbFxn NULL


Function pointer to user callback function for end of window (EOW) events. Not called if NULL
fxn_eol upp_CbFxn NULL


Function pointer to user callback function for end of line (EOL) events. Not called if NULL
fxn_uor upp_CbFxn NULL


Function pointer to user callback function for underrun/overflow (UOR) events. Not called if NULL
fxn_err upp_CbFxn NULL


Function pointer to user callback function for internal error (ERR) events. Not called if NULL
fxn_dpe upp_CbFxn NULL


Function pointer to user callback function for DMA programming error (DPE) events. Not called if NULL


Callback functions registered with the uPP driver should adhere to the upp_CbFxn typedef:

typedef void (* upp_CbFxn)(Ptr xferp, Uint32 val);

When called by the driver, the first argument will be a upp_Transfer handle pointing to the transfer that caused the interrupt event. The second argument will be a single bit corresponding to the interrupt event that called the function. This can be useful for using a single callback function to handle multiple interrupt events.


The upp_ioctl enum specifies valid IOCTLs that the application can call using GIO_control. Standard IOM IOCTLs may also be used.

IOCTL Description
upp_ioctl_enable Enables the uPP peripheral. Has no effect if peripheral is already enabled (i.e. after GIO_create is called)
upp_ioctl_disable Disables the uPP peripheral. Has no effect if peripheral is already enabled (i.e. before GIO_create is called)
upp_ioctl_sw_reset Toggles uPP peripheral software reset. Clears error conditions and the active and first pending transfer on both channels. If additional transfers are pending in a uPP channel's QUE, they will automatically move up to the active and (first) pending position.
upp_ioctl_apply_params Applies new parameters to driver. A pointer to a new upp_Params struct must be passed as an argument to GIO_control. To be safe, the uPP peripheral should be disabled before calling this function.
upp_ioctl_get_status Gets status of uPP driver. A pointer to a dummy struct of type upp_Obj must be passed as an argument to GIO_control. The status bits of this dummy struct will be copied from the driver's internal upp_Obj struct.
upp_ioctl_dequeue Clears QUEs for both uPP channels. Each channel's active transfer and first pending transfer will not be affected. A pointer to an external QUE_Obj must be passed as an argument to GIO_control. The transfers removed from the uPP driver's internal QUEs will be added to the external QUE. GIO_delete cannot be called unless the driver internal QUEs are empty.
upp_ioctl_clear_errors Clears error conditions from the driver internal status struct. Generally, upp_ioctl_sw_reset should be used instead to recover from error conditions.

See Also

Introduction to uPP

uPP Peripheral User Guide

DSP/BIOS Driver Developer's Guide (includes description of IOM driver model)

uPP Driver Introduction Video (requires registration on