NOTICE: The Processors Wiki will End-of-Life in December of 2020. It is recommended to download any files or other content you may need that are hosted on processors.wiki.ti.com. The site is now set to read only.
Using Functional CSL with DM642
Functional Layer CSL (C6000)
The Chip Support Library (CSL) is a collection of low-level software useful for programming peripherals in TI DSPs. The CSL gives users easier control and access to a device's peripheral registers.
There are two main versions of CSL. There is CSL 2.x which supports device families such as the c55x, c621x, c671x, and c641x. The CSL 2.x APIs were over-hauled in the CSL 3.x release in 2005. The initial devices supported with CSL 3.x were the c672x family and later the c6455. Moving forward, TI's "high performance" DSPs such as the c6474 (announced October 2008) will continue to provide CSL 3.x. All other TI devices, e.g. OMAP, Davinci, DM643x, c642x, etc. will have complete drivers instead.
The CSL provides functions to perform simple low-level operations and bit manipulations. PSP drivers on the other hand not only manipulate the hardware but also interact with the OS as well. The PSP drivers take care of higher level details such as critical sections, queues, using a mutex/semaphore for shared peripherals, etc.
In terms of software architecture, the CSL can be broken down into 2 main levels. The top level APIs are often called the "functional layer". The lower level code on which the functional layer is built is called "register layer" in CSL 3.x, and it is called "hardware abstraction layer" (HAL) in CSL 2.x.
This particular topic is focused on CSL 2.x Functional Layer (FCSL). It covers some of the concepts prevalent in CSL 2.x using I2C programming as an example.
Initializing a Peripheral
Open a Handle
The first step when using the Functional CSL is to open a device handle. This handle represents the hardware in software. When calling any of the CSL API functions the specified handle informs the function of which specific peripheral to control. To open a handle for a I2C port a user would add the following to their source file:
<syntaxhighlight lang="c">I2C_Handle hI2CA; hI2CA=I2C_open(I2C_DEV0, I2C_OPEN_RESET); </syntaxhighlight>
This handle, hI2CA, is now associated with I2C port 0. Alternatively, if the user wishes to create a handle for I2C port 1 the would use the parameter I2C_DEV1. Each handle is assigned to only one instance of one peripheral, so multiple handles are required to control multiple peripherals.
Configuring the Peripheral
Once the I2C handle is opened it can be used by the other CSL functions to manipulate the peripheral's registers. The first step is to configure the baseline/starting register values. This is typically done by either populating a configuration structure which contains all of a peripheral's registers or by passing individual arguments for each register. The configuration structure/arguments is then passed to a PER_config() function where PER is the peripheral module name (e.g., I2C_config or I2C_configArgs). This config function takes all of the values stored in the structure and applies them to the individual registers for the peripheral defined by the device handle.
<syntaxhighlight lang="c"> unsigned int i2coar,i2cimr,i2cclkl,i2cclkh,i2ccnt, i2csar, i2cmdr, i2cpsc;
i2coar = I2C_I2COAR_RMK(I2C0_MASTER_ADDRESS); i2csar = I2C_I2CSAR_RMK(I2C0_SLAVE_ADDRESS); i2cimr = I2C_I2CIMR_RMK(I2C_I2CIMR_ICXRDY_MSK,
I2C_I2CIMR_ICRRDY_MSK, I2C_I2CIMR_ARDY_MSK, I2C_I2CIMR_NACK_MSK, I2C_I2CIMR_AL_MSK );
i2cpsc = I2C_I2CPSC_RMK(I2C_PRESCALER_VALUE); i2cclkl = I2C_I2CCLKL_RMK(I2C_I2CCLKL_ICCL_OF(I2C_ICCLKL_REG_VALUE)); i2cclkh = I2C_I2CCLKH_RMK(I2C_I2CCLKH_ICCH_OF(I2C_ICCLKH_REG_VALUE)); i2ccnt = I2C_I2CCNT_RMK(1); i2cmdr = i2cmdr = I2C_I2CMDR_RMK(I2C_I2CMDR_NACKMOD_DEFAULT,
I2C_I2CMDR_FREE_BSTOP, I2C_I2CMDR_STT_NONE, I2C_I2CMDR_STP_NONE, I2C_I2CMDR_MST_MASTER, I2C_I2CMDR_TRX_XMT, I2C_I2CMDR_XA_7BIT, I2C_I2CMDR_RM_NONE, I2C_I2CMDR_DLB_NONE, I2C_I2CMDR_IRS_NRST, I2C_I2CMDR_STB_NONE, I2C_I2CMDR_FDF_NONE, I2C_I2CMDR_BC_DEFAULT );
I2C_configArgs(hI2CA, i2coar, i2cimr, i2cclkl, i2cclkh, i2ccnt, i2csar, i2cmdr, i2cpsc); </syntaxhighlight>
Other Configuration Steps
The peripheral-specific User Guides typically include a section on how to properly initialize the peripheral. These should be consulted when writing the CSL-based config code to ensure that the appropriate steps are taken for proper functionality. Assuming the steps are properly followed the peripheral should at this point be fully configured and ready to run.
Accessing the Peripheral at Run-time
Once the peripheral is running a user can access it through a variety of methods.
The EDMA engine is commonly used to offload the servicing of a peripheral's I/O registers away from the CPU. Without EDMA, the CPU is forced to break away from its current processing task repeatedly in order to keep the data flowing. The EDMA instead offloads this responsibility from the CPU and allows for the data to be captured or transmitted without any interruption to the CPU. Once the EDMA has captured or emptied a complete buffer to/from the peripheral it then informs the CPU via an interrupt.
The CSL also offers a number of functions allowing the CPU to directly interface the peripheral's I/O. This is a less efficient than using the EDMA engine, but it can help in the early stages of coding for and testing a peripheral. For example, the I2C requires a single 'dummy' write before it will automatically begin transferring and receiving data. There are also numerous functions which help enable fast, efficient polling code when interrupts are not desired.
<syntaxhighlight lang="c"> /* Wait BB (Bus Busy) to clear */ while (I2C_bb(hI2CA));
/* Invoke the start condition */ I2C_start(hI2CA);
/* Wait until Transmit Ready bit is set */ while (I2C_xrdy(hI2CA));
/* Write the data into Data Transmit register */ I2C_writeByte(hI2CA, value);
/*Generate Stop condition */ I2C_sendStop(hI2CA); </syntaxhighlight>
If the peripheral is no longer needed the module handle can then be closed to free up the resource.
<syntaxhighlight lang="c"> /*Close Handle*/ I2C_close(hI2CA); </syntaxhighlight>
The Functional layer of the CSL is a strong tool to help quickly write peripheral access code. It enables a user to work with english-like names at a higher level rather than working directly with registers. While it does not provide immediately usable configuration/control code as a full device driver does, it does provide an easy-to-understand API for each device peripheral. The CSL can be considered one layer lower than a driver - in essence, a driver is built on top of CSL function calls.