AM64x MCU+ SDK  10.01.00
OSPI Low Level Driver

The Octal Serial Peripheral Interface (OSPI) module is a kind of Serial Peripheral Interface (SPI) module which allows single, dual, quad or octal read and write access to external flash devices. The OSPI module is used to transfer data, either in a memory mapped direct mode (for example a processor wishing to execute code directly from external flash memory), or in an indirect mode where the module is set-up to silently perform some requested operation, signaling its completion via interrupts or status registers.

Features Supported

  • Support for single, dual, quad (QSPI mode) or octal I/O instructions.
  • Supports dual Quad-SPI mode for fast boot applications.
  • Memory mapped ‘direct’ mode of operation for performing flash data transfers and executing code from flash memory.
  • Programmable delays between transactions.
  • Legacy mode allowing software direct access to low level transmit and receive FIFOs, bypassing the higher layer processes.
  • An independent reference clock to decouple bus clock from SPI clock – allows slow system clocks.
  • Programmable baud rate generator to generate OSPI clocks.
  • Supports BOOT mode.
  • Handling ECC errors for flash devices with embedded correction engine.
  • Support DMA Read.
  • Interrupt mode is supported.

SysConfig Features

Note
It is strongly recommend to use SysConfig where it is available instead of using direct SW API calls. This will help simplify the SW application and also catch common mistakes early in the development cycle.
  • OSPI instance name
  • Input clock frequency to be used for OSPI module
  • Input clock divider which decides the baud-rate at which the flash will be read
  • Chip Select
  • Enabling of various features like DMA, PHY mode(not supported yet), XIP(not supported yet)
  • In advanced config, you can choose various parameters like frame format, decoder chip select, read dummy cycles etc.
  • Pinmux configurations for the OSPI instance

OSPI Phy Tuning Algorithm

The OSPI tuning algorithm works as follows:

  • Step 1 Find Golden Primary RxLow
    To find the RxDLL boundaries, we fix a valid TxDLL and search through RxDLL range, rdDelay values.
    As we are not sure of a valid TxDLL we use a window of TxDLL values to find the RxDLL boundaries.
                Rx_DLL
                  ▲
                  │   ++++++++++++++++
              127 │     ++++++++++++++
                  │   x   ++++++++++++
                  │   xx   +++++++++++
                  │   xxx   ++++++++++
                  │   xxxx   +++++++++
                  │   xxxxx   ++++++++
                  │ │ xxx│xx   +++++++
                  │ │ xxx│xxx   ++++++
                  │ │ xxx│xxxx   +++++
                  │ │ xxx│xxxxx   ++++
                  │ │ xxx│xxxxxx   +++
         Search   │ │ xxx│xxxxxxx   ++
         Rx_Low ──┼─┤►xxx│xxxxxxxx   +
                  │ │    │
                 ─┼─┼────┼------────►  Tx_DLL
                 0│ │    │           127
                    │    │
                    │    │
    
                Tx_Low   Tx_Low
                Start    End
    

  • Step 2 Find Golden Secondary RxLow
    Search for one more rxLow at different txDl, To find Secondary rxHigh we use the txDLL + Search_offset value of rxLow.

  • Step 3 Select minimum of primary rxLow and secondary rxLow Value
    • Pick minimum value of rxDLL between rxLow and sec_rxLow.
    • Pick Minimum value of rdDelay(read_delay) between rxLow and sec_rxLow.
      Primary
      Search
      Secondary
      Search
      Final Point
      Fail Fail Return Fail
      Fail Pass Return Fail
      Pass Fail Return Fail
      Pass Pass RxDll = Min(Primary, Secondary)
      RdDelay = Min(Primary, Secondary)
      TxDll = Primary

  • Step 4 Find Golden Primary Rx High
    To find rxHigh we use the txDLL values of rxLow.
    Start the rdDelay (Read delay) from maximum and decrement it.
    As these are valid values and rxHigh rdDelay is always >= rxLow rdDelay.
                Rx_DLL
                  ▲
              127 │   ▲+++++++++++++++
        Search    │   │ ++++++++++++++
       Rx_High────┼──►│   ++++++++++++
       on Fixed   │   │x   +++++++++++
        Tx_DLL    │   │xx   ++++++++++
                  │   │xxx   +++++++++
                  │   │xxxx   ++++++++
                  │   ▼xxxxx   +++++++
                  │   Xxxxxxx   ++++++
                  │   Xxxxxxxx   +++++
                  │   Xxxxxxxxx   ++++
                  │   Xxxxxxxxxx   +++
                  │   Xxxxxxxxxxx   ++
                  │   Xxxxxxxxxxxx   +
                  │
                 ─┼------------───►  Tx_DLL
                 0│                  127
    

  • Step 5 Find Golden Secondary Rx High
    To find Secondary rxHigh we use the txDLL + Search_offset value of rxLow.
    Start the rdDelay (Read delay) from maximum and decrement it.
    As these are valid values and rxHigh rdDelay is always >= rxLow rdDelay.

  • Step 6 Select maximum of primary and secondary rxHigh value
    • Compare the Primary and Secondary point.
    • Pick the point which has passing maximum rxDll.
      Primary
      Search
      Secondary
      Search
      Final Point
      Fail Fail Return Fail
      Fail Pass Return Fail
      Pass Fail Return Fail
      Pass Pass If(secondary.rxDll > primary.rxDll)
      Pick Secondary search point
      Else
      Pick Primary search point

  • Step 7 Do a backup Search in case rxLow and rx High has same read delay values
    Check a different point if the rxLow and rxHigh are on the same rdDelay.
    This avoids mistaking the metastability gap for an rxDLL boundary.
    • Find backup rx Low Find the rxDLL boundaries using the TxDLL window at the higher end .
      We start the window_end and decrement the TxDLL value until we find the valid point.
             Rx_DLL
              ▲
              │   ++++++++++++++++
          127 │   ++++++++++++++++
              │   ++++++++++++++++
              │    +++++++++++++++
              │     +++++++++│++++│
              │      ++++++++│++++│
              │   x   +++++++│++++│
              │   xx   ++++++│++++│
              │   xxx   +++++│++++│
              │   xxxx   ++++│++++│
              │   xxxxx   +++│++++│
              │   xxxxxx   ++│++++│
              │   xxxxxxx   +│++++│         Search
              │   xxxxxxxx   │++++◄───────  Rx_Low
              │              │    │
             ─┼──────────────┼────┤► Tx_DLL
             0│              │    │   127
                             │    │
                     Tx_High        Tx_High
                     Start          End
      
    • Find backup sec rxLow
      Search for one more rxLow at different txDll, we use the txDLL - Search_offset value of rxLow.
    • Select minimum of backup rxLow and backup sec rxLow
      • Pick minimum value of rxDLL between rxLow and sec_rxLow.
      • Pick Minimum value of rdDelay(read_delay) between rxLow and sec_rxLow.
    • Find backup primary rxHigh search Find rxDLL Max
      Start the rdDelay (Read delay) from maximum and decrement it.
              Rx_DLL
      127 ▲
          │   +++++++++++++++▲                Search Rx_High
          │   +++++++++++++++│◄────────────   on Fixed Tx_DLL
          │   +++++++++++++++│
          │    ++++++++++++++│
          │     +++++++++++++│
          │      ++++++++++++│
          │   x   +++++++++++▼
          │   xx   +++++++++++
          │   xxx   ++++++++++
          │   xxxx   +++++++++
          │   xxxxx   ++++++++
          │   xxxxxx   +++++++
          │   xxxxxxx   ++++++
          │   xxxxxxxx    ++++
          │
         ─┼────────────────────► Tx_DLL
         0│                       127
      
    • Find backup secondary rxHigh search
      Search for one more rxHigh at different txDll, we use the txDLL - Search_offset value of rxLow.
    • Select maximum of backup primary and secondary rxhigh
      Compare the Primary and Secondary point.
      Pick the point which has passing maximum rxDll.
      Primary
      Search
      Secondary
      Search
      Final Point
      Fail Fail Return Fail
      Fail Pass Return Fail
      Pass Fail Return Fail
      Pass Pass If(secondary.rxDll > primary.rxDll)
      Pick Secondary search point
      Else
      Pick Primary search point

  • Step 8 Find golden TxLow Look for txDLL boundaries at 1/4 of rxDLL window.
    Find txDLL Min.
                          Rx_DLL
                     127 ▲
                         │   ++++++++++++++++
              Rx_High    │     ++++++++++++++
                  ───────┼──►x   ++++++++++++
                         │   xx   +++++++++++
                         │   xxx   ++++++++++
                         │   xxxx   +++++++++
           Fix Rx_DLL    │   xxxxx   ++++++++
          1/4 between    │   xxxxxx   +++++++
          Rx_High and    │   xxxxxxx   ++++++
            Rx_Low       │   xxxxxxxx   +++++
                   ──────┼─► ◄───┬──►    ++++
                         │   xxxx│xxxxx   +++
               Rx_Low    │   xxxx│xxxxxx   ++
                   ──────┼──►xxxx│xxxxxxx   +
                         │       │
                        ─┼───────┼───────────►  Tx_DLL
                        0│       │          127
                                 │
                            Search Tx_Low
    

  • Step 9 Find golden TxHigh
    Find txDLL Max.
    Start the rdDelay (Read delay) from maximum and decrement it.
            Rx_DLL
               127 ▲
                   │   +++++++++++++++++
        Rx_High    │     +++++++++++++++
            ───────┼──►x   +++++++++++++
                   │   xx   ++++++++++++
                   │   xxx   +++++++++++
                   │   xxxx   ++++++++++
     Fix Rx_DLL    │   xxxxx   +++++++++
    1/4 between    │   xxxxxx   ++++++++
    Rx_High and    │   xxxxxxx   +++++++
       Rx_Low      │   xxxxxxxx   ++++++
             ──────┼─► xxxxxxxxx   ◄─┬─►
                   │   xxxxxxxxxx   +│++
         Rx_Low    │   xxxxxxxxxxx   │++
             ──────┼──►xxxxxxxxxxxx  │++
                   │                 │
                  ─┼─────────────────┼─►  Tx_DLL
                  0│                 │127
                                  Search Tx_Max
    

  • Step 10 Do a backup Search in case rxLow and rx High has same read delay values
    Check a different point if the txLow and txHigh are on the same rdDelay.
    This avoids mistaking the metastability gap for a txDLL boundary.
    • Find a backup primary txLow
      Look for txDLL boundaries at 3/4 of rxDLL window.
      Find txDLL Min.
               Rx_DLL
              127 ▲
                  │
         Rx_High──┼──►+++++++++++++++++
      Fix Rx_DLL  │   +++++++++++++++++
         3/4 of   │   +++++++++++++++++
        Rx_High  ─┼─► ◄───┬───►++++++++
      and Rx_Low  │     ++│++++++++++++
                  │      +│++++++++++++
                  │   x   │++++++++++++
                  │   xx  │++++++++++++
                  │   xxx │ +++++++++++
                  │   xxxx│  ++++++++++
                  │   xxxx│   +++++++++
                  │   xxxx│x   ++++++++
                  │   xxxx│xx   +++++++
          Rx_Low──┼──►xxxx│xxx   ++++++
                  │       │
                 ─┼───────┼────────────► Tx_DLL
                 0│       │               127
                     Search Tx_Min
      
    • Find a backup primary txHigh
      Find txDLL Max.
      Start the rdDelay (Read delay) from maximum and decrement it.
               Rx_DLL
                127
                  ▲
                  │
         Rx_High──┼──►+++++++++++++++++
                  │   +++++++++++++++++
       Fix Rx_DLL │   +++++++++++++++++
       3/4 of ────┼─► +++++++◄────┬───►
        Rx_High   │     ++++++++++│++++
         and      │      +++++++++│++++
        Rx_Low    │   x   ++++++++│++++
                  │   xx   +++++++│++++
                  │   xxx   ++++++│++++
                  │   xxxx   +++++│++++
                  │   xxxxx   ++++│++++
                  │   xxxxxx   +++│++++
                  │   xxxxxxx   ++│++++
          Rx_Low──┼──►xxxxxxxx   +│++++
                  │               │
                 ─┼───────────────┼────► Tx_DLL
                 0│               │       127
                               Search Tx_Max
      

  • Step 11 Find bottom left and top right corners
    These are theoretical corners. They may not actually be "good" points.
    But the longest diagonal of the shmoo will be between these corners.

  • Step 12 Find the tuning point
    • Step1 Find the equation of diagonal between topRight(rxHigh,txHigh) and bottomLeft(rxLow,txLow) points.
    • Step2 Find gapLow, last point along the slope of bottom left read delay region.
    • Step3 If top right and bottom left have same read delay, put tuning point in the middle and adjust for temperature.
    • Step4 If top right and bottom left have different read delays, find gapHigh, the starting point along the slope of top left read delay region.
    • Step5 Find len1 = gapLow - topLeft, len2 = topRight - gapHigh.
    • Step6 Choose the read Delay region with maximum length.
    • Step7 Place the Phy tuning point in the corner farthest from the gap.

Features not Supported

Example Usage

Include the below file to access the APIs

#include <stdio.h>
#include <drivers/ospi.h>
#include <string.h>
#include <kernel/dpl/MutexArmP.h>
#include <kernel/nortos/dpl/r5/HwiP_armv7r_vim.h>

Instance Open Example

int32_t status = OSPI_SYSTEM_SUCCESS;
gOspiHandle = &gOspiObject[CONFIG_OSPI0];
status = OSPI_lld_init(gOspiHandle);

Non-Blocking Example ISR Register

static __attribute__((__section__(".text.hwi"), noinline, naked, target("arm"), aligned(4))) void App_OSPI_ISR(void)
{
ISR_CALL_LEVEL_NONFLOAT_REENTRANT(OSPI_lld_isr, \
gOspiHandle, \
intrNum, \
gOSPIVimStsAddr, \
gOSPIVimStsClrMask,
intcBaseAddr);
}

Non-Blocking Transfer Example

void transfer_nonblocking(void)
{
OSPI_Transaction transaction;
uint32_t status = SystemP_SUCCESS;
gOspiHandle->interruptCallback = isrCallback;
intrNum = gOspiHandle->hOspiInit->intrNum;
intcBaseAddr = gHwiConfig.intcBaseAddr;
gOSPIVimStsAddr = intcBaseAddr + (0x404u + (((intrNum)>> 5) & 0xFu) * 0x20u);
gOSPIVimStsClrMask = 0x1u << ((intrNum) & 0x1Fu);
/* Register Interrupt */
HwiP_setPri(intrNum, 4U);
HwiP_setVecAddr(intrNum, (uintptr_t)&App_OSPI_ISR);
HwiP_enableInt(intrNum);
HwiP_setAsPulse(intrNum, FALSE);
transaction.addrOffset = APP_OSPI_FLASH_OFFSET;
transaction.buf = (void *)(gOspiTxBuf);
transaction.count = APP_OSPI_DATA_SIZE;
OSPI_lld_writeIndirect(gOspiHandle, &transaction);
if (status == SystemP_SUCCESS )
{
while(try_lock_mutex(&transferMutex) == MUTEX_ARM_LOCKED);
}
else
{
DebugP_log("OSPI Write Fail");
}
/* Mutex Lock */
transferMutex = MUTEX_ARM_LOCKED;
transaction.buf = (void *)(gOspiRxBuf);
status = OSPI_lld_readIndirect(gOspiHandle, &transaction);
if (status == SystemP_SUCCESS )
{
while(try_lock_mutex(&transferMutex) == MUTEX_ARM_LOCKED);
}
else
{
DebugP_log("OSPI Read Fail");
}
/* De-Register Interrupt */
HwiP_setVecAddr(intrNum, 0);
HwiP_disableInt(intrNum);
}

Instance Close Example

int32_t status = OSPI_SYSTEM_SUCCESS;
status = OSPI_lld_deInit(gOspiHandle);

API

APIs for OSPI LLD

ospi_lld.h
OSPI LLD Driver API/interface file.
__attribute__
struct tisci_boardcfg_sa2ul_cfg __attribute__
OSPI_lld_isr
void OSPI_lld_isr(void *args)
QSPI ISR for Read and Write Functionality.
OSPI_Transaction
Data structure used with OSPI_Transfers - OSPI_lld_readDirect, OSPI_lld_writeDirect,...
Definition: ospi_lld.h:441
HwiP_disableInt
uint32_t HwiP_disableInt(uint32_t intNum)
Disable a specific interrupt.
OSPI_lld_Transaction_init
void OSPI_lld_Transaction_init(OSPI_Transaction *trans)
Function to initialize the OSPI_Transaction structure.
OSPI_lld_init
int32_t OSPI_lld_init(OSPILLD_Handle hOspi)
This function opens a given OSPI peripheral.
OSPI_lld_writeIndirect
int32_t OSPI_lld_writeIndirect(OSPILLD_Handle handle, OSPI_Transaction *trans)
Function to perform indirect writes to the flash using INDAC controller.
DebugP_log
#define DebugP_log(format,...)
Function to log a string to the enabled console.
Definition: DebugP.h:225
OSPI_lld_readIndirect
int32_t OSPI_lld_readIndirect(OSPILLD_Handle handle, OSPI_Transaction *trans)
Function to perform indirect reads from the flash using INDAC controller.
HwiP_enableInt
void HwiP_enableInt(uint32_t intNum)
Enable a specific interrupt.
SystemP_SUCCESS
#define SystemP_SUCCESS
Return status when the API execution was successful.
Definition: SystemP.h:56
OSPI_SYSTEM_SUCCESS
#define OSPI_SYSTEM_SUCCESS
Return status when the API execution was successful.
Definition: ospi_lld.h:277
OSPI_lld_deInit
int32_t OSPI_lld_deInit(OSPILLD_Handle hOspi)
Function to close a OSPI peripheral specified by the OSPI handle.
DebugP.h
ospi.h
DebugP_assert
#define DebugP_assert(expression)
Function to call for assert check.
Definition: DebugP.h:177