SPI master-slave communication in Polarfire SOC

I configured my PolarFire SoC for SPI communication with SPI1 as the slave and SPI0 as the master. The MISO, MOSI, CLK, and CS of the master are connected to the MOSI, MISO, CLK, and CS of the slave, respectively.

This setup worked perfectly in baremetal. However, in FreeRTOS, an interrupt occurs after the clock signal is sent. Specifically, after pressing any key while the clock is connected, I receive an interrupt. If the clock is not connected, no interrupt occurs, but SPI requires the clock. Could you please advise on how to handle this interrupt issue?

Here is the code I used (modified from the existing demo application code for RISC-V):

/*******************************************************************************
 * This project is used to to send command from spi0 (master) and receive
 * corresponding response from spi1 (slave).
 ******************************************************************************/

/* Standard includes. */
#include <stdio.h>
#include <string.h>
#include <unistd.h>

/* FreeRTOS kernel includes. */
#include <FreeRTOS.h>
#include <task.h>
#include <queue.h>
#include <semphr.h>

/* PolarFire HAL includes. */
#include "mpfs_hal/mss_hal.h"
#include "drivers/mss/mss_mmuart/mss_uart.h"
#include "drivers/mss/mss_spi/mss_spi.h"

/*-------------------------MACRO DEFINITIONS----------------------------------*/
#define COMMAND_BYTE_SIZE                            1U
#define NB_OF_TURNAROUND_BYTES                       4U
#define SLAVE_NB_OF_COMMANDS                         4U
#define SLAVE_PACKET_LENGTH                          8U
/* Priorities used by the tasks. */
#define SPI_TASK_PRIORITY                       ( tskIDLE_PRIORITY + 2 )
#define UART_TASK_PRIORITY                      ( tskIDLE_PRIORITY + 1 )

/*-----------------------FUNCTION DECLARATION---------------------------------*/
extern void freertos_vector_table( void );

void vApplicationStackOverflowHook( TaskHandle_t pxTask, char *pcTaskName );
static void UART_Task( void *pvParameters );
static void SPI_Task( void *pvParameters );
static void spi1_slave_cmd_handler( uint8_t *, uint32_t );
static void spi0_block_rx_handler( uint8_t *, uint32_t );
static void mss_spi_overflow_handler( uint8_t mss_spi_core );
static void handle_trap1( void );
static void handle_trap2(void);
/*----------------------------------------------------------------------------*/
static const uint8_t g_separator[] ="\r\n\
        ------------------------------------------------------------------------------";
static uint8_t g_master_tx_buffer[5] = { 0x00, 0x01, 0x02, 0x03, 0xAA};
static uint8_t g_master_rx_buffer[8] = { 0u };
static uint8_t g_slave_rx_buffer[13] = { 0u };
uint8_t g_slave_tx_buffer[SLAVE_NB_OF_COMMANDS][SLAVE_PACKET_LENGTH] = {
        {0x48, 0x41, 0x50, 0x50, 0x49, 0x4E, 0x45, 0x53},
        {0x54, 0x52, 0x45, 0x41, 0x53, 0x55, 0x52, 0x45},
        {0x43, 0x4F, 0x4D, 0x50, 0x55, 0x54, 0x45, 0x52},
        {0x53, 0x4F, 0x4C, 0x49, 0x54, 0x55, 0x44, 0x45}
};
char g_ui_buf[100] = {0};

SemaphoreHandle_t xSemaphore = NULL;

/* Main function for the HART1(U54_1 processor). Application code running on
 * HART1 is placed here. */
void e51( void )
{
    /*Reset SPI0 and SPI1*/
    (void)mss_config_clk_rst(MSS_PERIPH_SPI0, (uint8_t) MPFS_HAL_FIRST_HART, PERIPHERAL_ON);
    (void)mss_config_clk_rst(MSS_PERIPH_SPI1, (uint8_t) MPFS_HAL_FIRST_HART, PERIPHERAL_ON);
    (void)mss_config_clk_rst(MSS_PERIPH_MMUART0, (uint8_t) MPFS_HAL_FIRST_HART, PERIPHERAL_ON);

    MSS_UART_init(&g_mss_uart0_lo,
                  MSS_UART_115200_BAUD,
                  MSS_UART_DATA_8_BITS | MSS_UART_NO_PARITY | MSS_UART_ONE_STOP_BIT);

    PLIC_init();
    __enable_irq();
    PLIC_SetPriority(SPI0_PLIC, 2u);
    PLIC_SetPriority(SPI1_PLIC, 2u);
    PLIC_EnableIRQ(SPI0_PLIC);
    PLIC_EnableIRQ(SPI1_PLIC);

    /* Initialize and configure SPI1 as master */
    MSS_SPI_init(&g_mss_spi0_lo);

    MSS_SPI_configure_master_mode(&g_mss_spi0_lo,
                                  MSS_SPI_SLAVE_0,
                                  MSS_SPI_MODE1,
                                  256u,
                                  MSS_SPI_BLOCK_TRANSFER_FRAME_SIZE,
                                  mss_spi_overflow_handler);

    /* Initialize and configure SPI0 as slave */
    MSS_SPI_init(&g_mss_spi1_lo);

    MSS_SPI_configure_slave_mode(&g_mss_spi1_lo,
                                 MSS_SPI_MODE1,
                                 MSS_SPI_BLOCK_TRANSFER_FRAME_SIZE,
                                 mss_spi_overflow_handler);

    MSS_SPI_set_slave_block_buffers(&g_mss_spi1_lo,
                                    &g_slave_tx_buffer[0][0],
                                    COMMAND_BYTE_SIZE + NB_OF_TURNAROUND_BYTES,
                                    g_slave_rx_buffer,
                                    sizeof(g_slave_rx_buffer),
                                    spi0_block_rx_handler);

    MSS_SPI_set_cmd_handler(&g_mss_spi1_lo,
                            spi1_slave_cmd_handler,
                            COMMAND_BYTE_SIZE);

    __asm__ volatile( "csrw mtvec, %0" :: "r"( ( uintptr_t )freertos_vector_table | 0x1 ) );

    /* Create binary semaphore */
    xSemaphore = xSemaphoreCreateBinary();

    if (xSemaphore == NULL) {
        while(1); //Failed to create semaphore
    }

    /* Create UART Task */
    xTaskCreate(UART_Task, "UART Task", configMINIMAL_STACK_SIZE, NULL, UART_TASK_PRIORITY, NULL);

    /* Create SPI Task */
    xTaskCreate(SPI_Task, "SPI Task", configMINIMAL_STACK_SIZE, NULL, SPI_TASK_PRIORITY, NULL);

    /* Start the scheduler */
    vTaskStartScheduler();

    /* If all is well, the scheduler will now be running and the following line
     * will never be reached. */
    for (;;);
}

/*----------------------------------------------------------------------------*/
void vApplicationStackOverflowHook( TaskHandle_t pxTask, char *pcTaskName )
{
    ( void ) pcTaskName;
    ( void ) pxTask;

    /* Run time stack overflow checking is performed if
     * configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2.  This hook
     * function is called if a stack overflow is detected. */
    taskDISABLE_INTERRUPTS();
    for( ;; );
}

/*----------------------------------------------------------------------------*/
static void spi0_block_rx_handler
(
    uint8_t * rx_buff,
    uint32_t rx_size
)
{
    /* SPI slave receive completion handler.
     * This function is called by the SPI slave on the completion of each SPI
     * transaction after the SPI chip select signal is de-asserted. */
}

/*----------------------------------------------------------------------------*/
static void spi1_slave_cmd_handler
(
    uint8_t * rx_buff,
    uint32_t rx_size
)
{
    /* SPI slave command handler.
     * This function is called by the SPI slave driver once the command byte is
     * received. */

    uint8_t index;
    const uint8_t * p_response;

    index = rx_buff[0];

    if (index < SLAVE_NB_OF_COMMANDS)
    {
        p_response = &g_slave_tx_buffer[index][0];
    }
    else
    {
        p_response = &g_slave_tx_buffer[0][0];
    }

    MSS_SPI_set_cmd_response(&g_mss_spi1_lo, p_response, SLAVE_PACKET_LENGTH);
}

/*----------------------------------------------------------------------------*/
static void mss_spi_overflow_handler
(
    uint8_t mss_spi_core
)
{
    /* SPI buffer overflow handler
     * This function is called by SPI driver in case of buffer overflow. */
    if (mss_spi_core)
    {
        /* reset SPI1 */
        (void)mss_config_clk_rst(MSS_PERIPH_SPI1, (uint8_t) MPFS_HAL_FIRST_HART, PERIPHERAL_OFF);

        /* Take SPI1 out of reset. */
        (void)mss_config_clk_rst(MSS_PERIPH_SPI1, (uint8_t) MPFS_HAL_FIRST_HART, PERIPHERAL_ON);
    }
    else
    {
        /* reset SPI0 */
        (void)mss_config_clk_rst(MSS_PERIPH_SPI0, (uint8_t) MPFS_HAL_FIRST_HART, PERIPHERAL_OFF);

         /* Take SPI0 out of reset. */
        (void)mss_config_clk_rst(MSS_PERIPH_SPI0, (uint8_t) MPFS_HAL_FIRST_HART, PERIPHERAL_ON);
    }
}

/*----------------------------------------------------------------------------*/
void UART_Task( void *pvParameters)
{
    char key;
    uint8_t rx_size;
    uint8_t rx_buff[1];

    for (;;) {
        /* Prompt the user */
        MSS_UART_polled_tx(&g_mss_uart0_lo, g_separator, sizeof(g_separator));
        MSS_UART_polled_tx(&g_mss_uart0_lo, (const uint8_t*)"\r\n Press any key to continue.\r\n",
                sizeof("\r\n Press any key to continue.\r\n"));
        do
        {
            rx_size = MSS_UART_get_rx(&g_mss_uart0_lo, rx_buff, sizeof(rx_buff));
        } while(0u == rx_size);

        /* give the semaphore to notify the SPI task */
        xSemaphoreGive(xSemaphore);
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

/******************************************************************************/
void SPI_Task( void *pvParameters)
{
    uint8_t cmd_idx = 0U;
    for (;;) {
        /* Wait for the semaphore from UART Task */
        if (xSemaphoreTake(xSemaphore, portMAX_DELAY) == pdTRUE) {
            /* Assert slave select. */
            MSS_SPI_set_slave_select(&g_mss_spi0_lo, MSS_SPI_SLAVE_0);

            /* Perform block transfer */
            MSS_SPI_transfer_block(&g_mss_spi0_lo,
                                   g_master_tx_buffer,
                                   COMMAND_BYTE_SIZE + NB_OF_TURNAROUND_BYTES,
                                   g_master_rx_buffer,
                                   sizeof(g_master_rx_buffer));

            /* De-assert slave select. */
            MSS_SPI_clear_slave_select(&g_mss_spi0_lo, MSS_SPI_SLAVE_0);

            MSS_UART_polled_tx_string(&g_mss_uart0_lo, (const uint8_t*)"\r\nCommand sent to spi slave: ");
            sprintf(g_ui_buf,"    %x    \r\n", cmd_idx);
            MSS_UART_polled_tx_string(&g_mss_uart0_lo, (const uint8_t*)&g_ui_buf);

            MSS_UART_polled_tx_string(&g_mss_uart0_lo, (const uint8_t*)"\r\nData received from spi slave: ");
            sprintf(g_ui_buf,"    %x     %x     %x     %x    %x     %x     %x     %x    \r\n",
                    g_master_rx_buffer[0],
                    g_master_rx_buffer[1],
                    g_master_rx_buffer[2],
                    g_master_rx_buffer[3],
                    g_master_rx_buffer[4],
                    g_master_rx_buffer[5],
                    g_master_rx_buffer[6],
                    g_master_rx_buffer[7]);
            MSS_UART_polled_tx_string(&g_mss_uart0_lo, (const uint8_t*)&g_ui_buf);

            /* Issue a different command each time to the slave.*/
            if (3u == cmd_idx)
            {
              cmd_idx = 0u;
            }
            else
            {
              ++cmd_idx;
            }
            g_master_tx_buffer[0] = cmd_idx;
        }
    }
}

/******************************************************************************/
void vAssertCalled( void )
{
    taskDISABLE_INTERRUPTS();
}

/*-----------------------------------------------------------*/
void handle_trap1( void )
{
    MSS_UART_polled_tx(&g_mss_uart0_lo, (const uint8_t*)"\r\n interrupt1.\r\n",
                    sizeof("\r\n interrupt1.\r\n "));
}
/*-----------------------------------------------------------*/
void handle_trap2( void )
{
    MSS_UART_polled_tx(&g_mss_uart0_lo, (const uint8_t*)"\r\n interrupt2.\r\n",
                    sizeof("\r\n interrupt2.\r\n "));
}
/*-----------------------------------------------------------*/

void freertos_risc_v_application_interrupt_handler( uint32_t ulMcause )
{
    char pcCause[ 20 ];
    sprintf( pcCause, "\r\n %u \r\n", ulMcause );
    MSS_UART_polled_tx_string(&g_mss_uart0_lo, (const uint8_t*)pcCause);

    for(uint32_t i=0;i<2000000;i++)
    {

    }
    handle_trap1();
}
/*-----------------------------------------------------------*/

void freertos_risc_v_application_exception_handler( uint32_t ulMcause )
{
    char pcCause[ 20 ];
    sprintf( pcCause, "%u", ulMcause );
    MSS_UART_polled_tx_string(&g_mss_uart0_lo, (const uint8_t*)pcCause);

    handle_trap2();
}
/*-----------------------------------------------------------*/

Output I am getting:

        ---------------------------------------------------------------------
 Press any key to continue.

 11 

 interrupt1.

 11 

 interrupt1.

 11 

 interrupt1.

 11 

 interrupt1.

 11 

 interrupt1.

 11 

 interrupt1.

 11 

 interrupt1.

 11 

 interrupt1.

 11 

 interrupt1.

 11 

 interrupt1.

 11

Your description of the problem is not very clear. Please help us understanding the issue -

  1. What do you mean by “worked perfectly in baremetal”?
  2. Which interrupt do you expect to get and when?
  3. What is the value of mcause and what is the callstack when interrupt fires?

Also, please update the definition of vAssertCalled to the following:

void vAssertCalled( void )
{
    taskDISABLE_INTERRUPTS();.
    for( ;; );
}
  1. We started with a code example from PolarFire that worked well without any issues. It helped us communicate between a master and a slave device flawlessly. Then, we split the code into separate tasks for handling UART and SPI functions when we moved to FreeRTOS.

  2. Initially, we didn’t use interrupts or called any hanlders in our code. But when we connected the clock between the master and slave and pressed any key, our setup stopped working unexpectedly. After some investigation, we found that a function called freertos_risc_v_application_interrupt_handler was being triggered when the SPI communication started and the clock was provided. To confirm this, we added some debug messages in that function.

  3. on console i am getting the value as 11.

Set a breakpoint in the handler function with the debugger and have a look at the callstack (the stack of functions being called until the breakpoint is reached) to see what was causing that freertos_risc_v_application_exception_handler is invoked.

What happens when you do the same in bare metal code?

As hs2@ described, confirm that it is an expected interrupt that is happening in the bare metal code too. If that is the case, you probably need to link the BSP interrupt handling code to freertos_risc_v_application_exception_handler. Try updating the definition of freertos_risc_v_application_exception_handler to the following:

void freertos_risc_v_application_interrupt_handler( uint32_t ulMcause )
{
    ( void ) ulMcause;
    trap_from_machine_mode( NULL, NULL, NULL );
}

thank you! It worked