1.9.20 Serial Quad Interface (SQI)

The SQI module is a synchronous serial interface that provides access to serial Flash memories and other serial devices. The SQI module supports Single Lane (identical to SPI), Dual Lane, and Quad Lane interface modes.

The SQI peripheral library operates in two transfer modes:

  • DMA Mode:

    • DMA mode is a higher throughput data transfer mode, where the internal SQI DMA engine off-loads the Host processor by using predefined Buffer Descriptors for data transfers

    • Each Buffer Descriptor can handle up to 256Bytes of data and multiple descriptors can be chained to support larger portions of data transfers

    • Four words cumulatively are called a Buffer Descriptor (BD), and are part of the transmit and receive structure

    • Individually, a Buffer Descriptor describes an area within the Host memory where data is waiting to be transmitted from or received into

    • Host software creates a single or linked list of Buffer Descriptors based on the buffer size and places them in the system memory

    • This is the Default mode set during SQIx_Initialize()

  • XIP Mode:

    • XIP mode is used to execute code out of serial Flash devices

    • SQIx_XIPSetup() should be called at run time to switch SQI peripheral to XIP mode

The SQI module supports two data flow modes: SPI Mode 0 and Mode 3. Each transfer mode (XIP/DMA) can use any of the data flow modes as desired by the application.

Using The Library

The SQI Peripheral Library operates only in DMA and XIP mode to interface with the SQI based Serial Flash Memories operating in Single-bit SPI, Dual SPI, and Quad SPI.

The SQI peripheral library is by default configured to interrupt mode. Use SQIx_RegisterCallback() function to receive event on transfer completion.

The SQI Peripheral library provides non-Blocking API's, which can be used to perform the following functionalities on the SQI Slave device.

  • Start a DMA Transfer

    • sqi_dma_desc_t data type needs to filled to send a transfer request to the slave device

    • The transfer request can be

      • Sending commands (ERASE, Write Enable, etc..)

      • Reading/Writing status registers

      • Writing to the Serial flash memory

      • Reading from the serial flash memory

  • Setup Execute in Place (XIP)

    • SQIx_XIPSetup() function can be used to configure SQI peripheral to XIP mode

Here is an example code to read data from SQI memory with DMA desciptor chaining

#define PAGE_SIZE                   (256U)
#define SECTOR_SIZE                 (4096U)

//Erase, Write and Read 80KBytes of memory
#define SECTORS_TO_EWR              (20U)
#define BUFFER_SIZE                 (SECTOR_SIZE * SECTORS_TO_EWR)

#define MIN_DMA_BUFFER_LEN          (CACHE_LINE_SIZE)
#define CMD_DESC_NUMBER             5
#define BUFF_DESC_NUMBER            (BUFFER_SIZE / PAGE_SIZE)
#define MEM_START_ADDRESS           (0x0U)

#define SST26VF_HS_READ             0x0B

volatile bool xfer_done = false;

uint8_t CACHE_ALIGN readData[BUFFER_SIZE];

uint8_t CACHE_ALIGN sqi_cmd_hsr[MIN_DMA_BUFFER_LEN];
uint8_t CACHE_ALIGN sqi_cmd_dummy[MIN_DMA_BUFFER_LEN];

sqi_dma_desc_t CACHE_ALIGN sqiCmdDesc[CMD_DESC_NUMBER];
sqi_dma_desc_t CACHE_ALIGN sqiBufDesc[BUFF_DESC_NUMBER];

static void APP_EventHandler(uintptr_t context)
{
    xfer_done = true;
}

void APP_Read( void *rx_data, uint32_t rx_data_length, uint32_t address )
{
    uint32_t pendingBytes   = rx_data_length;
    uint8_t *readBuffer     = (uint8_t *)rx_data;
    uint32_t numBytes       = 0;
    uint32_t i              = 0;

    xfer_done = false;

    // Construct parameters to issue SST26 high speed read command
    sqi_cmd_hsr[0] = SST26VF_HS_READ;
    sqi_cmd_hsr[1] = (0xff & (address>>16));
    sqi_cmd_hsr[2] = (0xff & (address>>8));
    sqi_cmd_hsr[3] = (0xff & (address>>0));
    sqi_cmd_hsr[4] = 0;

    // SQI Read: High Speed Read command (0x0B), 3 byte address, and 1 byte mode 
    // Chip Select remains asserted after the transfer

    sqiCmdDesc[0].bd_ctrl       = ( SQI_BDCTRL_BD_BUFLEN(5) | SQI_BDCTRL_MODE(QUAD_MODE) |
                                    SQI_BDCTRL_SPI_DEV_SEL10(0x00) | SQI_BDCTRL_DESC_EN_Msk);

    sqiCmdDesc[0].bd_bufaddr    = (uint32_t *)&sqi_cmd_hsr;
    sqiCmdDesc[0].bd_stat       = 0;
    sqiCmdDesc[0].bd_nxtptr     = (sqi_dma_desc_t *)&sqiCmdDesc[1];  

    DCACHE_CLEAN_BY_ADDR((uint32_t *)&sqiCmdDesc[0], sizeof(sqiCmdDesc[0]));

    // SQI Read: 2 byte dummy read 
    // Chip Select remains asserted after the transfer

    sqiCmdDesc[1].bd_ctrl       = ( SQI_BDCTRL_BD_BUFLEN(2) | SQI_BDCTRL_MODE(QUAD_MODE) |
                                    SQI_BDCTRL_SPI_DEV_SEL10(0x00) | SQI_BDCTRL_DESC_EN_Msk);

    sqiCmdDesc[1].bd_bufaddr    = (uint32_t *)&sqi_cmd_dummy;
    sqiCmdDesc[1].bd_stat       = 0;
    sqiCmdDesc[1].bd_nxtptr     = (sqi_dma_desc_t *)&sqiBufDesc[0]; 

    DCACHE_CLEAN_BY_ADDR((uint32_t *)sqi_cmd_hsr, sizeof(sqi_cmd_hsr));
    DCACHE_CLEAN_BY_ADDR((uint32_t *)&sqiCmdDesc[0], 2 * sizeof(sqi_dma_desc_t));

    // SQI Read: Reads the values from the memory 
    // Create Descriptor chain. Enable packet interrupt.
    // Chip Select is de-asserted after the transfer

    for (i = 0; (i < BUFF_DESC_NUMBER) && (pendingBytes > 0); i++)
    {
        if (pendingBytes > PAGE_SIZE)
        {
            numBytes = PAGE_SIZE;
        }
        else
        {
            numBytes = pendingBytes;
        }

        sqiBufDesc[i].bd_ctrl       = ( SQI_BDCTRL_BD_BUFLEN(numBytes) | SQI_BDCTRL_PKT_INT_EN_Msk |
                                        SQI_BDCTRL_MODE(QUAD_MODE) | SQI_BDCTRL_DIR_Msk |
                                        SQI_BDCTRL_SPI_DEV_SEL10(0x00) | SQI_BDCTRL_DESC_EN_Msk);

        sqiBufDesc[i].bd_bufaddr    = (uint32_t *)readBuffer;
        sqiBufDesc[i].bd_stat       = 0;
        sqiBufDesc[i].bd_nxtptr     = (sqi_dma_desc_t *)&sqiBufDesc[i+1];

        pendingBytes    -= numBytes;
        readBuffer      += numBytes;
    }

    // The last descriptor must indicate the end of the descriptor list
    sqiBufDesc[i-1].bd_ctrl         |= (SQI_BDCTRL_LIFM_Msk | SQI_BDCTRL_LAST_BD_Msk |
                                        SQI_BDCTRL_CS_ASSERT_Msk);

    sqiBufDesc[i-1].bd_nxtptr       = 0x00000000;

    DCACHE_CLEAN_BY_ADDR((uint32_t *)&sqiBufDesc[0], (i * sizeof(sqi_dma_desc_t)));

    // Initialize the root buffer descriptor
    SQI1_DMATransfer((sqi_dma_desc_t *)&sqiCmdDesc[0]);

    // Wait for transfer to complete
    while(xferDone == false);
}

void main()
{
    // Other calls ..
    SQI1_RegisterCallback(APP_EventHandler, (uintptr_t)NULL);

    DCACHE_INVALIDATE_BY_ADDR((uint32_t *)readBuffer, BUFFER_SIZE);

    APP_Read((uint32_t *)readData, BUFFER_SIZE, MEM_START_ADDRESS);
}

Library Interface

SQI peripheral library provides the following interfaces:

Functions

Name Description
SQIx_Initialize Initializes given instance of the SQI peripheral
SQIx_DMASetup Sets up SQI peripheral to DMA mode
SQIx_DMATransfer Starts a DMA transfer to the SQI slave device
SQIx_XIPSetup Sets up SQI peripheral to XIP mode
SQIx_RegisterCallback Sets the pointer to the function (and it's context) to be called when the operation is complete

Data types and constants

Name Type Description
SQI_EVENT_HANDLER Typedef Defines the data type and function signature for the SQI peripheral event handler function
SQI_LANE_MODE Enum Defines the data type to specify the type of lane
SQI_XIP_ADDR_BYTES Enum Defines the data type to specify the number of address bytes
SQI_XIP_DEV_SELECT Enum Defines the data type to specify the device to be used
sqi_dma_desc_t Struct Defines the data type for the SQI DMA based transfer