AM64x MCU+ SDK  12.01.00
MCSPI Low Level Driver

Features Supported

  • Controller and Peripheral mode of operation
  • Per transfer selection of different channels/chip select
  • Non-blocking (Callback) transfers
  • Multi Word Acess. To use this feature, following requirement must be satisfied.
    • It is only supported in interrupt mode.
    • The channel selected must have the FIFO enabled.
    • Transmit and receive registers must write and read 32-bits respectively.
    • FIFO Trigger level must be 32 bit aligned i.e, It must be in power of 2.
    • Data Size must be 8 bits or 1 byte.
    • Total frame i.e spiTransaction.count must be in multiple of data size.

Features NOT Supported

  • For MCU domain instances, DMA mode is not supported.
  • Default TX data feature is not supported in DMA mode.
  • In DMA mode, FIFO is not enabled.

Constraint

  • Due to the design constraint maximum DMA PKTDMA_0 TX/RX channels each can be used is 3 per R5F core. So in case of MCSPI instance with DMA mode enabled can use atmost 3 CS in multi-controller mode.

DMA Transfer Size Limitation (12-bit Counter)

Maximum McSPI DMA transfer: 4,095 WORDS per transaction

The McSPI DMA transfer counter is a 12-bit field that tracks the number of words (not bytes) to transfer. A word is defined by the configured data width.

Transfer Counter Architecture

Counter field: 12-bit register (0x000 to 0xFFF = 0 to 4,095 decimal)

Important: Attempting a transfer count of 4,096 or higher causes register overflow:

  • 4,096 decimal = 0x1000 (requires 13 bits)
  • Overflow wraps to 0x000
  • Transfer fails and system may hang

Word Definition and Byte Conversion

The word size depends on the SPIDAT0 WL[3:0] field (word length in bits):

Data Width Word Size bufWidthShift Formula
1-8 bits 1 byte 0 bytes = count × 1
9-16 bits 2 bytes 1 bytes = count × 2
17-32 bits 4 bytes 2 bytes = count × 4

Maximum Transfer Sizes

Since counter max = 4,095 words:

Data Width Max Words Max Bytes
8-bit 4,095 4,095 bytes
16-bit 4,095 8,190 bytes
32-bit 4,095 16,380 bytes

Calculation Method

Total bytes = transaction->count × (1 << bufWidthShift)
OR
Total bytes = transaction->count << bufWidthShift

Example:

  • Data width: 8-bit (bufWidthShift = 0)
  • Count: 4,095 words
  • Total bytes: 4,095 << 0 = 4,095 bytes
  • Data width: 16-bit (bufWidthShift = 1)
  • Count: 4,095 words
  • Total bytes: 4,095 << 1 = 8,190 bytes
  • Data width: 8-bit (bufWidthShift = 0)
  • Count: 4,096 words
  • Total bytes: 4,096 << 0 = 4,096 bytes OVERFLOW

Workaround for Large Transfers

For transfers exceeding 4,095 words:

  1. Split into multiple transactions (each ≤ 4,095 words)
  2. Use transaction chaining if available
  3. Loop-based approach for sequential transfers

Implementation Detail:

The DMA transfer size validation is performed in all DMA API functions:

The driver validates that the transaction count does not exceed 4,095 words.

Validation formula used in driver:

uint32_t transferBytes = count << bufWidthShift;
if (transferBytes > MCSPI_DMA_MAX_TRANSFER_BYTES) // 4095
{
return MCSPI_INVALID_PARAM; // Reject transfer
}

If the limit is exceeded, the API returns MCSPI_INVALID_PARAM status.

Usage Overview

API Sequence

To use the MCSPI driver to send data over the SPI bus, the application calls the following APIs:

Initializing the MCSPI Driver

MCSPI_lld_init() must be called before any other MCSPI APIs. This function iterates till the channel count. This function uses mcspi handle to initialize each instance. Calling MCSPI_lld_init() a second time with the same handle previously passed to MCSPI_lld_init() will result in an error. You can, though, re-use the handle if the instance is closed via MCSPI_lld_deInit(). In DMA mode, MCSPI_lld_initDma() needs to be called instead of MCSPI_lld_init() to acquire and initialize mcspi instance. Please note that initializing MCSPI driver is taken care by the SysConfig generated code.

MCSPI Transfer Mode

The MCSPI driver supports three transfer modes of operation: Interrupt, Polling and DMA Mode. Interrupt and DMA mode, it supports only callback mode. Once a MCSPI driver is opened, the only way to change the operation mode is to close and re-open the MCSPI instance with the new transfer mode.

In callback mode, a MCSPI transaction functions asynchronously, which means that it does not block code execution. After a MCSPI transaction has been completed, the MCSPI driver calls a user-provided hook function. Callback mode is supported in the execution context of tasks and hardware interrupt routines.

In multichannel mode connected to multiple external devices, the MCSPI exchanges data with one MCSPI device at a time and FIFO is enabled per each channel at a time.

NOTE: The size of txBuf and RxBuf must be greater than the data size bits, if data size is not a multiple of 8 bits. Mask the data size bits in txBuf and rxBuf as the remaining bits will be discarded. For example, consider datasize = 18 bits then txBuf and rxBuf size should be uint32_t and the mask bits should be 0x3FFFF.

Important Usage Guidelines

  • The MCSPI protocol does not account for a built-in handshaking mechanism and neither does this driver. Therefore, when operating in MCSPI_MS_MODE_PERIPHERAL mode, the application must provide such a mechanism to ensure that the MCSPI peripheral is ready for the MCSPI controller. The MCSPI peripheral must call MCSPI_lld_readWrite() / MCSPI_lld_readWriteIntr() / MCSPI_lld_readWriteDma() before the controller starts transmitting. Some example application mechanisms could include:
    • Timed delays on the MCSPI controller to guarantee the MCSPI peripheral is ready for a MCSPI transaction.
    • A form of GPIO flow control from the peripheral to the MCSPI controller to notify the controller when ready.
  • In case of DMA mode, as R5F core is not Cache Coherent, Cache Writeback is required if R5F writes to the buffers. And before reading the buffers, application needs to invalidate those. Please refer MCSPI Loopback DMA LLD.

Example Usage

Include the below file to access the APIs

#include <kernel/dpl/MutexArmP.h>
#include <kernel/nortos/dpl/r5/HwiP_armv7r_vim.h>

Instance Open Example

int32_t status = MCSPI_STATUS_SUCCESS;
gMcspiHandle0 = &gMcspiObject[CONFIG_MCSPI0];
status = MCSPI_lld_init(gMcspiHandle0);

Instance Close Example

int32_t status = MCSPI_STATUS_SUCCESS;
status = MCSPI_lld_deInit(gMcspiHandle0);

Non-Blocking Transfer Example

uint32_t bufIndex;
uint32_t count;
uint32_t timeout = MCSPI_WAIT_FOREVER;
int32_t status = MCSPI_STATUS_SUCCESS;
MCSPI_ExtendedParams extendedParams;
/* Interrupt configuration and registration*/
intrNum = gConfigMcspi0InitObject[CONFIG_MCSPI0].intrNum;
gMcspiVimStsAddr = gVimBaseAddr + (0x404u + (((intrNum)>> 5) & 0xFu) * 0x20u);
gMcspiVimStsClrMask = 0x1u << ((intrNum) & 0x1Fu);
HwiP_setVecAddr(intrNum, (uintptr_t)&App_MCSPI_ISR);
HwiP_setPri(intrNum, gConfigMcspi0InitObject[CONFIG_MCSPI0].intrPriority);
HwiP_enableInt(intrNum);
if(status == MCSPI_STATUS_SUCCESS)
{
DebugP_log("\n[MCSPI] Loopback example started ...\r\n");
/* Memfill buffers */
for(bufIndex = 0U; bufIndex < APP_MCSPI_MSGSIZE; bufIndex++)
{
gMcspiTxBuffer[bufIndex] = bufIndex;
gMcspiRxBuffer[bufIndex] = 0U;
}
gMcspiObject[CONFIG_MCSPI0].transferMutex = &gMutexLockUnlock;
/* populate extended parameters */
extendedParams.channel = 0;
extendedParams.csDisable = TRUE;
extendedParams.dataSize = 32;
count = APP_MCSPI_MSGSIZE / (extendedParams.dataSize/8);
status = MCSPI_lld_readWriteIntr(gMcspiHandle0, \
gMcspiTxBuffer, \
&gMcspiRxBuffer, \
count, \
timeout, \
&extendedParams);
while(try_lock_mutex(gMcspiObject[CONFIG_MCSPI0].transferMutex) == MUTEX_ARM_LOCKED);
{
DebugP_assert(FALSE); /* MCSPI transfer failed!! */
}
else
{
/* Compare data */
for(bufIndex = 0U; bufIndex < APP_MCSPI_MSGSIZE; bufIndex++)
{
if(gMcspiTxBuffer[bufIndex] != gMcspiRxBuffer[bufIndex])
{
status = MCSPI_STATUS_FAILURE; /* Data mismatch */
DebugP_log("Data Mismatch at offset %d\r\n", bufIndex);
break;
}
}
}
}
/* Deregister IRQ */
HwiP_setVecAddr(intrNum, 0);
HwiP_setPri(intrNum, 15);
if(MCSPI_STATUS_SUCCESS == status)
{
DebugP_log("All tests have passed!!\r\n");
}
else
{
DebugP_log("Some tests have failed!!\r\n");
}

Non-Blocking Example transfer callback

void MCSPI_lld_transferCallback(void *args, uint32_t transferStatus)
{
unlock_mutex(gMcspiObject[CONFIG_MCSPI0].transferMutex);
}

Non-Blocking Example ISR CALL callback

static __attribute__((__section__(".text.hwi"), noinline, naked, target("arm"), aligned(4))) void App_MCSPI_ISR(void)
{
ISR_CALL_LEVEL_NONFLOAT_REENTRANT(MCSPI_lld_controllerIsr, \
gMcspiHandle0, \
intrNum, \
gMcspiVimStsAddr, \
gMcspiVimStsClrMask,
gVimBaseAddr);
}

API

APIs for MCSPI LLD

MCSPI_WAIT_FOREVER
#define MCSPI_WAIT_FOREVER
Value to use when needing a timeout of infinity or wait forver until resource is available.
Definition: mcspi_lld.h:122
SystemP.h
MCSPI_lld_controllerIsr
void MCSPI_lld_controllerIsr(void *args)
This is the McSPI Controller ISR and can be used as IRQ handler in Controller mode.
MCSPI_STATUS_SUCCESS
#define MCSPI_STATUS_SUCCESS
Return status when the API execution was successful.
Definition: mcspi_lld.h:82
MCSPI_lld_readWriteIntr
int32_t MCSPI_lld_readWriteIntr(MCSPILLD_Handle hMcspi, void *txBuf, void *rxBuf, uint32_t count, uint32_t timeout, const MCSPI_ExtendedParams *extendedParams)
This API reads writes data from the McSPI instance in Interrupt mode.
SemaphoreP.h
MCSPI_TRANSFER_COMPLETED
#define MCSPI_TRANSFER_COMPLETED
Definition: mcspi_lld.h:188
DebugP_log
#define DebugP_log(format,...)
Function to log a string to the enabled console.
Definition: DebugP.h:235
count
uint16_t count
Definition: tisci_pm_device.h:1
MCSPI_lld_init
int32_t MCSPI_lld_init(MCSPILLD_Handle hMcspi)
This API Initializes the McSPI instance.
HwiP.h
HwiP_enableInt
void HwiP_enableInt(uint32_t intNum)
Enable a specific interrupt.
MCSPI_ExtendedParams
Data structure used with MCSPI_lld_read(), MCSPI_lld_readIntr(), MCSPI_lld_readDma(),...
Definition: mcspi_lld.h:554
mcspi_lld.h
MCSPI LLD Driver API/interface file.
MCSPI_INVALID_PARAM
#define MCSPI_INVALID_PARAM
Return status when the API execution failed due invalid parameters.
Definition: mcspi_lld.h:97
MCSPI_lld_deInit
int32_t MCSPI_lld_deInit(MCSPILLD_Handle hMcspi)
This API De-Initializes the McSPI instance.
__attribute__
struct tisci_boardcfg_sa2ul_cfg __attribute__
DebugP.h
DebugP_assert
#define DebugP_assert(expression)
Function to call for assert check.
Definition: DebugP.h:187
MCSPI_STATUS_FAILURE
#define MCSPI_STATUS_FAILURE
Return status when the API execution was not successful due to a failure.
Definition: mcspi_lld.h:87
MCSPI_DMA_MAX_TRANSFER_BYTES
#define MCSPI_DMA_MAX_TRANSFER_BYTES
Definition: mcspi_lld.h:404