Post Go back to editing

Device not Responding

Category: Software
Software Version: xilinx freertos 10 in vitis 2022.2

Hi,

I am working on the ADIN3310 Industrial Ethernet Switch, for which I have already downloaded the driver from the software resources on the official ADIN3310 website.

I am trying to configure the switch from the Zynq UltraScale+ MPSoC XAZU3EG on my custom board. I have the PS SPI from the Zynq block connected to the Ethernet switch and a GPIO connected to Timer0.

I configured the Zynq block in Vivado by enabling the necessary MIO pins for the SPI signals and a GPIO for the Timer0 interrupts. On power-up, at the Timer0 pin of the Ethernet switch, I am able to see the 1.25 seconds high and 2 ms low signal as mentioned in the user guide.

I generated the bitstream, exported the XSA, and also enabled the TTC for FreeRTOS. I then created a platform project for the XSA in xilfreertos10 instead of standalone. I initialized the driver as mentioned in the user guide and took the Windows example from GitHub, converting the SPI functions and other necessary things to a Vitis FreeRTOS-based implementation.

I am also able to see the data transfer, but in the AddDevice() function, during the CheckMacFirmwareVersion() call, after sending the request, I am getting success. However, I am getting a semaphore timeout error. I also verified the IRQ by putting a breakpoint in the IRQ task, which is being hit, but the SPI read is returning all zeros.

If I bypass the CheckMacFirmwareVersion() call and proceed to SES_InitializePorts(), I still get the same timeout error.

int32_t SES_MX_GetInterfaces(sesID_t sesId,
SES_interfaceDescriptor_t interfaceArray[],
int32_t arrayLength)
{
int rv = SES_PORT_OK;
int32_t retVal = SES_PORT_ERROR;

RPC_sem_t sem = RPC_ReservePoolSem();

rv = SES_GetInterfaces_non_blocking(sesId,
interfaceArray,
arrayLength,
NULL,
sem,
&retVal);
if (rv == SES_PORT_OK) {
// request message sent successfully
rv = SES_PORT_WaitSemaphore(sem, SES_PORT_BLOCK_TIMEOUT)<<<<<<<-------- here i  am getting timeout error 
if (rv == SES_PORT_TIMEOUT) {
// response timeout/error
retVal = SES_PORT_TIMEOUT;
}
}
else {
retVal = rv;
}

i am also attaching the main ,example_config and spi porting layer for 

Thanks and Regards,

K.V.N.S.Kowisk

///////main code 

// Includes *******************************************************************

#include <stdio.h>

/* FreeRTOS */
#include "FreeRTOS.h"
#include "task.h"

/* Xilinx BSP */
#include "xparameters.h"
#include "xscugic.h"
#include "xil_exception.h"

/* SES */
#include "SES_example_config.h"
#include "SES_codes.h"

// End Includes ***************************************************************

// Macros *********************************************************************

/* Application task parameters */
#define SES_APP_TASK_STACK_WORDS    (2048U)
#define SES_APP_TASK_PRIORITY       (tskIDLE_PRIORITY + 1U)
#define SES_APP_TASK_NAME           "SES_App"

// End Macros *****************************************************************

// Global Variables ***********************************************************

/*
 * Single GIC instance shared across the whole application.
 * Declared extern in SES_PORT_SPI_interface.c so the SPI port layer can
 * connect the GPIO ISR without needing a separate init path.
 */
XScuGic XInterruptController;

// End Global Variables *******************************************************

// Static Function Prototypes *************************************************

static void vSesAppTask(void *pvParam);
static int  prvGicInit(void);

// End Static Function Prototypes *********************************************

// Functions ******************************************************************

/**
 * @brief  Zynq/FreeRTOS application entry point.
 *
 * Responsibilities:
 *   1. Initialise the ARM Generic Interrupt Controller (GIC).
 *   2. Enable ARM exceptions so interrupts are routed correctly.
 *   3. Create the SES application task.
 *   4. Start the FreeRTOS scheduler — never returns.
 */
int main(void)
{
    printf("\n--START SES (Zynq FreeRTOS Port)--\n\n");

    /* ── 1. Initialise the GIC ── */
    if (prvGicInit() != XST_SUCCESS) {
        printf("GIC initialisation failed — halting.\n");
        for (;;) {}   /* hang; inspect via debugger */
    }

    /* ── 2. Enable ARM exception handling ── */
    Xil_ExceptionEnable();

    /* ── 3. Create the SES application task ── */
    BaseType_t taskCreated = xTaskCreate(
                                 vSesAppTask,
                                 SES_APP_TASK_NAME,
                                 SES_APP_TASK_STACK_WORDS,
                                 NULL,
                                 SES_APP_TASK_PRIORITY,
                                 NULL);

    if (taskCreated != pdPASS) {
        printf("Failed to create SES app task — halting.\n");
        for (;;) {}
    }

    /* ── 4. Start scheduler — does not return ── */
    vTaskStartScheduler();

    /*
     * Reaching here means the scheduler failed to start (usually because
     * there is not enough heap for the idle task).
     * Check configTOTAL_HEAP_SIZE in FreeRTOSConfig.h.
     */
    printf("Scheduler failed to start — halting.\n");
    for (;;) {}

    return 0;   /* never reached; suppresses compiler warning */
}

// End Functions **************************************************************

// Static Functions ***********************************************************

/**
 * @brief  SES application task — equivalent to the original main() body.
 *
 * Runs AFTER the FreeRTOS scheduler has started.  All SES API calls that
 * create internal FreeRTOS objects (semaphores, tasks) are safe here.
 *
 * The task loops forever at the bottom so FreeRTOS always has at least one
 * user task alive (besides idle and the SES IRQ task).
 */
static void vSesAppTask(void *pvParam)
{
    (void)pvParam;

    /*
     * sesConfig() performs the full hardware + SES library initialisation:
     *   - Opens PS-SPI and XGpioPs (MIO 62)
     *   - Connects the GPIO ISR to the GIC
     *   - Creates the FreeRTOS IRQ handler task (TaskSpiIrq)
     *   - Calls SES_Init(), SES_AddHwInterface(), SES_AddDevice()
     *   - Pushes port configuration to ADIN3310 over SPI
     *
     *
     */
//    sesGetAppInfo_Example();

    if (SES_OK == sesConfig()) {

        /* Print ADIN3310 firmware version over SPI */
        sesGetAppInfo_Example();

        printf("############ Configuring the Switch ###################\n");

        /*
         * Run whichever protocol examples are enabled by compile-time flags
         * (TIME_SYNC, MSTP, FRER, LLDP_INIT, etc.).
         * This function may block in the interactive TIME_SYNC loop —
         * that is fine because TaskSpiIrq runs at a higher priority and
         * will still process incoming frames while this task is in scanf().
         */
        sesProtocolInitialization_Example();

    } else {
        printf("SES initialisation failed !!!!\n");
    }

    printf("\n--END--\n");

    /*
     * Keep the task alive.
     * Deleting the task here (vTaskDelete(NULL)) would be valid only if you
     * are certain no more SES callbacks will need this task's stack.
     * Blocking on a long delay is the safe default.
     */
    for (;;) {
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

/**
 * @brief  Initialise the ARM Generic Interrupt Controller.
 *
 * Uses the single XScuGic instance (xInterruptController) that is shared
 * with SES_PORT_SPI_interface.c via the extern declaration there.
 *
 * @return XST_SUCCESS on success, XST_FAILURE on error.
 */
static int prvGicInit(void)
{
    XScuGic_Config *gicCfg_p;
    int             status;

    /* Look up the GIC hardware configuration from xparameters.h */
    gicCfg_p = XScuGic_LookupConfig(XPAR_PSU_ACPU_GIC_DEVICE_ID);
    if (gicCfg_p == NULL) {
        return XST_FAILURE;
    }

    /* Initialise the GIC driver */
    status = XScuGic_CfgInitialize(&XInterruptController,
                                   gicCfg_p,
                                   gicCfg_p->CpuBaseAddress);
    if (status != XST_SUCCESS) {
        return XST_FAILURE;
    }

    /*
     * Connect the GIC interrupt handler to the ARM exception vector.
     * After this, any peripheral that raises a GIC SPI interrupt will be
     * routed through XScuGic_InterruptHandler automatically.
     */
    Xil_ExceptionRegisterHandler(
        XIL_EXCEPTION_ID_INT,
        (Xil_ExceptionHandler)XScuGic_InterruptHandler,
        &XInterruptController);

    return XST_SUCCESS;
}

// End Static Functions *******************************************************




////////ses_port_spi_interface


#include <string.h>
#include <stdint.h>
#include <stdio.h>

/* FreeRTOS */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

/* Xilinx BSP */
#include "xparameters.h"
#include "xspips.h"          /* PS SPI driver                                 */
#include "xgpiops.h"         /* PS GPIO driver                                */
#include "xscugic.h"         /* GIC interrupt controller                      */
#include "xil_exception.h"

/* SES driver */
#include "SES_debug.h"
#include "SES_port_api.h"
#include "SES_PORT_interface.h"

// End Includes ***************************************************************

// Macros, typedefs, enums ****************************************************

/* ── General ── */
#define SES_PORT_SPI_MAX_TYPE               (15)
#define SES_PORT_SPI_TFER_TYPE_MASK         (0xF)
#define SES_PORT_SEM_INFINITE_WAIT          (-1)

/* Milliseconds the Release path waits for the IRQ task to finish */
#define SES_PORT_SPI_WAIT_THREAD_STOP_MS    (5000U)

/* ── SPI frame commands (identical to reference) ── */
#define SES_PORT_SPI_READ_CMD               (0x80U)
#define SES_PORT_SPI_WRITE_CMD              (0x40U)
#define SES_PORT_SPI_CMD_SZ                 (1U)

/* ── Hardware IDs ── pull from xparameters.h; override here if needed ── */
#ifndef SES_SPI_DEVICE_ID
#  define SES_SPI_DEVICE_ID                 XPAR_PSU_SPI_0_DEVICE_ID
#endif

#ifndef SES_GPIO_DEVICE_ID
#  define SES_GPIO_DEVICE_ID                XPAR_PSU_GPIO_0_DEVICE_ID
#endif

/* MIO pin used as the ADIN3310 interrupt input */
#define SES_PORT_IRQ_GPIO_PIN               (62U)
#define gpio								(63U)

/*
 * SPI clock divider.
 * PS-SPI ref clock is typically 200 MHz on ZU+.
 * XSPIPS_CLK_PRESCALE_8  → 25 MHz  (ADIN3310 maximum).
 * Adjust if your ref clock differs.
 */
#define SES_SPI_CLK_PRESCALE                XSPIPS_CLK_PRESCALE_8

/* FreeRTOS task parameters for the IRQ handler task */
#define SES_PORT_IRQ_TASK_STACK_WORDS       (1024U)
#define SES_PORT_IRQ_TASK_PRIORITY          (configMAX_PRIORITIES - 1U)
#define SES_PORT_IRQ_TASK_NAME              "SES_SpiIrq"

/* ── Struct ── */
typedef struct {
    int               initialized;
    void             *spiProtectSem;   /* counting sem, max=1: SPI bus mutex  */
    SemaphoreHandle_t rxEventSem;      /* binary sem: ISR → IRQ task          */
    TaskHandle_t      irqTask;         /* FreeRTOS IRQ handler task           */
    volatile int      stopTask;        /* flag: ask the IRQ task to exit      */
    XSpiPs            spiInst;         /* Xilinx PS-SPI instance              */
    XGpioPs           gpioInst;        /* Xilinx PS-GPIO instance             */
    int               chipSelect;      /* SPI CS index (0 for single-device)  */
} SES_PORT_spiLinkDescription_t;

/* Passed through pvPortMalloc to the IRQ task on creation */
typedef struct {
    int drvHdlIdx;
} SES_PORT_threadData_t;

// End Macros, typedefs, enums ************************************************

// Global Variables ***********************************************************

static SES_PORT_spiLinkDescription_t
    spiLinkDescription_g[SES_PORT_MAX_SPI_LINKS] = { { 0 } };

/* Protects the spiLinkDescription_g[] table during Init/Release */
static void *dataTableProtectionSem_gp = NULL;

/*
 * Shared GIC handle.
 * The application must initialise this before calling SES_PORT_SPI_Init().
 * Declare it extern here so the SPI port can register its GPIO ISR.
 */
extern XScuGic XInterruptController;

// End Global Variables *******************************************************

// Static Function Prototypes *************************************************

static int  GetLinkDescriptionEntry(void);
static int  HandleReceivedMessage(int tblIndex);
static int  InitDrv(int tblIndex);
static int  SpiClose(int tblIndex);
static int  SpiRead(int tblIndex, uint16_t length, uint8_t *dataRx_p);
static void TaskSpiIrq(void *pvParam);
static void GpioIsr(void *callbackRef);

// End Static Function Prototypes *********************************************

// Functions ******************************************************************

/**
 * @brief  Initialise the SPI interface to the ADIN3310.
 *
 * Replaces the Windows FT4222 open/init sequence with XSpiPs + XGpioPs
 * initialisation.  The init parameter struct is SES_PORT_zynqSpiInitParams_t
 * (defined in SES_PORT_interface.h — see note in that file).
 *
 * @param[in]  param_p     Pointer to SES_PORT_zynqSpiInitParams_t.
 * @param[out] intfType_p  Set to SES_PORT_spiInterface on success.
 * @param[out] srcMac_p    Not used for SPI; may be NULL.
 *
 * @return  Non-negative interface handle on success, negative error code on failure.
 */
int SES_PORT_SPI_Init(void *param_p,
                      SES_PORT_intfType_t *intfType_p,
                      uint8_t *srcMac_p)
{
    int intfHandle;
    int response;
    SES_PORT_zynqSpiInitParams_t *initParams_p =
        (SES_PORT_zynqSpiInitParams_t *)param_p;

    (void)srcMac_p; /* not used for SPI */

    /* ── Parameter check ── */
    if ((param_p == NULL) || (intfType_p == NULL)) {
        return SES_PORT_INVALID_PARAM;
    }

    /* ── Create the table-protection semaphore once ── */
    if (dataTableProtectionSem_gp == NULL) {
        dataTableProtectionSem_gp = SES_PORT_CreateSemaphore(1, 1);
        if (dataTableProtectionSem_gp == NULL) {
            return SES_PORT_ERROR;
        }
    }

    SES_PORT_WaitSemaphore(dataTableProtectionSem_gp,
                           SES_PORT_PROTECTION_TIMEOUT);

    /* ── Find a free link-description slot ── */
    intfHandle = GetLinkDescriptionEntry();
    if (intfHandle < SES_PORT_OK) {
        SES_PORT_SignalSemaphore(dataTableProtectionSem_gp);
        return SES_PORT_ERROR;
    }

    /* ── Per-link SPI-bus mutex (counting sem, max = 1) ── */
    spiLinkDescription_g[intfHandle].spiProtectSem =
        SES_PORT_CreateSemaphore(1, 1);
    if (spiLinkDescription_g[intfHandle].spiProtectSem == NULL) {
        SES_PORT_SignalSemaphore(dataTableProtectionSem_gp);
        return SES_PORT_ERROR;
    }

    /*
     * ── IRQ event semaphore ──
     * Binary semaphore:  ISR calls xSemaphoreGiveFromISR(),
     *                    TaskSpiIrq() calls xSemaphoreTake().
     * Replaces the Windows CreateEvent(auto-reset) + WaitForSingleObject().
     */
    spiLinkDescription_g[intfHandle].rxEventSem =
        xSemaphoreCreateBinary();
    if (spiLinkDescription_g[intfHandle].rxEventSem == NULL) {
        SES_PORT_DeleteSemaphore(
            spiLinkDescription_g[intfHandle].spiProtectSem);
        SES_PORT_SignalSemaphore(dataTableProtectionSem_gp);
        return SES_PORT_ERROR;
    }

    /* Store the chip-select index supplied by the caller */
    spiLinkDescription_g[intfHandle].chipSelect = initParams_p->chipSelect;
    spiLinkDescription_g[intfHandle].stopTask   = 0;

    SES_PORT_SignalSemaphore(dataTableProtectionSem_gp);

    /* ── Hardware init (SPI + GPIO + IRQ task) ── */
    response = InitDrv(intfHandle);
    if (response != SES_PORT_OK) {
        vSemaphoreDelete(spiLinkDescription_g[intfHandle].rxEventSem);
        SES_PORT_DeleteSemaphore(
            spiLinkDescription_g[intfHandle].spiProtectSem);
        return SES_PORT_ERROR;
    }

    spiLinkDescription_g[intfHandle].initialized = 1;
    *intfType_p = SES_PORT_spiInterface;

    return intfHandle;
}

/**
 * @brief  Release (close) the SPI interface.
 *
 * Signals the IRQ task to stop, waits up to SES_PORT_SPI_WAIT_THREAD_STOP_MS,
 * then tears down GPIO, SPI and semaphores.
 */
int SES_PORT_SPI_Release(int intfHandle)
{
    int rv = SES_PORT_OK;

    SES_PORT_WaitSemaphore(dataTableProtectionSem_gp,
                           SES_PORT_PROTECTION_TIMEOUT);

    if (!spiLinkDescription_g[intfHandle].initialized) {
        DBG_INFO("SES_PORT_SPI_Release called before Initialization");
        rv = SES_PORT_NOT_INITIALIZED;
    }

    if (rv == SES_PORT_OK) {
        rv = SpiClose(intfHandle);
        if (rv != SES_PORT_OK) {
            rv = SES_PORT_ERROR;
        }
    }

    SES_PORT_SignalSemaphore(dataTableProtectionSem_gp);

    return rv;
}

/**
 * @brief  Send a framed message to the ADIN3310 over SPI.
 *
 * Prepends the 1-byte WRITE command (0x40) then transfers the full frame
 * using XSpiPs_PolledTransfer() under the protection of the per-link mutex.
 *
 * @param[in] intfHandle  Handle returned by SES_PORT_SPI_Init().
 * @param[in] size        Payload size in bytes (NOT including command byte).
 * @param[in] data_p      Pointer to payload buffer (freed on success).
 *
 * @return SES_PORT_OK on success, negative error code on failure.
 */
int SES_PORT_SPI_SendMessage(int intfHandle, int size, void *data_p)
{
    int      result   = SES_PORT_ERROR;
    int      response = SES_PORT_ERROR;
    int i;
    int      spiFrmSz;
    int      count    = 0;
    int      status;
    uint8_t *tx_frame = NULL;

    /* Dummy RX buffer — XSpiPs always full-duplex */
    uint8_t *rx_dummy = NULL;

    if (!spiLinkDescription_g[intfHandle].initialized) {
        return SES_PORT_NOT_INITIALIZED;
    }

    if (data_p == NULL) {
        return SES_PORT_INVALID_PARAM;
    }

    /* Build TX frame: [0x40 | payload] */
    spiFrmSz = size + SES_PORT_SPI_CMD_SZ;

    result = SES_PORT_Malloc((void **)&tx_frame, spiFrmSz);
    if (result != SES_PORT_OK) {
        return SES_PORT_ERROR;
    }

    result = SES_PORT_Malloc((void **)&rx_dummy, spiFrmSz);
    if (result != SES_PORT_OK) {
        SES_PORT_Free(tx_frame);
        return SES_PORT_ERROR;
    }

    tx_frame[0] = SES_PORT_SPI_WRITE_CMD;
    memcpy(tx_frame + SES_PORT_SPI_CMD_SZ, data_p, size);

    /* ── Take SPI bus mutex ── */
    SES_PORT_WaitSemaphore(spiLinkDescription_g[intfHandle].spiProtectSem,
                           SES_PORT_SEM_INFINITE_WAIT);

    /*
     * XSpiPs_PolledTransfer drives CS automatically when configured with
     * XSPIPS_MANUAL_START_OPTION disabled (the default after SetOptions).
//     */
//    while(1){
    status = XSpiPs_PolledTransfer(
                 &spiLinkDescription_g[intfHandle].spiInst,
                 tx_frame,
                 rx_dummy,
                 (u32)spiFrmSz);
//    for(i=0;i<1000;i++);
//    }

    SES_PORT_SignalSemaphore(spiLinkDescription_g[intfHandle].spiProtectSem);

    count = (status == XST_SUCCESS) ? spiFrmSz : 0;

    SES_PORT_Free(tx_frame);
    SES_PORT_Free(rx_dummy);

    if (count != spiFrmSz) {
        response = SES_PORT_ERROR;
    } else {
        response = SES_PORT_OK;
        SES_PORT_Free(data_p);   /* convention: caller's buffer freed on OK */
    }

    return response;
}

/**
 * @brief  Return a human-readable string describing the SPI interface.
 *
 * The FT4222 device enumeration has no equivalent on Zynq; we return a
 * static description based on the BSP parameters instead.
 */
int32_t SES_PORT_SPI_GetInterfaceInfo(char *interfaceInfo_p,
                                      uint16_t bufferSize)
{
    if ((interfaceInfo_p == NULL) || (bufferSize == 0U)) {
        return SES_PORT_BUF_ERR;
    }

    int written = snprintf(interfaceInfo_p, bufferSize,
                           "0,Zynq-UltraScale-PS-SPI(dev=%d,gpio=%d);",
                           (int)SES_SPI_DEVICE_ID,
                           (int)SES_PORT_IRQ_GPIO_PIN);

    return (written > 0) ? SES_PORT_OK : SES_PORT_BUF_ERR;
}

// End Functions **************************************************************

// Static Functions ***********************************************************

/**
 * @brief  Handle one incoming SPI message from the ADIN3310.
 *
 * Reads the 4-byte header, validates the length and transfer-type fields,
 * allocates a receive buffer, reads the payload, then hands everything to
 * SES_ReceiveMessage().  Logic is identical to the Windows reference.
 */
static int HandleReceivedMessage(int tblIndex)
{
    int      response = SES_PORT_OK;
    uint8_t  transferType;
    int16_t  txLength;
    int16_t  pad_to_block;
    uint32_t header   = 0U;
    int      rxLength;
    uint8_t *rx_frame = NULL;

    /* Step 1 – read the 4-byte header */
    response = SpiRead(tblIndex, sizeof(header), (uint8_t *)&header);
    if (response != SES_PORT_OK) {
        return response;
    }

    /* Step 2 – decode and validate length (lower 12 bits) */
    rxLength = (int)((uint16_t)(header & SES_PORT_SPI_LENGTH_MASK));
    if (rxLength != (int)(((~header >> SES_PORT_SPI_INVERTED_LENGTH_SHIFT) &
                           SES_PORT_SPI_LENGTH_MASK))) {
        return SES_PORT_ERROR;
    }

    /* Step 3 – decode and validate transfer type (4 bits) */
    transferType = (uint8_t)((header >> SES_PORT_SPI_TYPE_SHIFT) &
                             SES_PORT_SPI_TFER_TYPE_MASK);
    if (transferType != (uint8_t)(((~header >> SES_PORT_SPI_INVERTED_TYPE_SHIFT) &
                                   SES_PORT_SPI_TFER_TYPE_MASK))) {
        return SES_PORT_ERROR;
    }

    /* Step 4 – round up to block boundary */
    txLength     = (int16_t)(rxLength + SES_PORT_SPI_BLOCK_LENGTH - 1);
    txLength    &= (int16_t)(~(SES_PORT_SPI_BLOCK_LENGTH - 1));
    pad_to_block = txLength - (int16_t)rxLength;

    /* Step 5 – allocate and fill the payload buffer */
    response = SES_PORT_Malloc((void **)&rx_frame,
                               (int)(rxLength + pad_to_block));
    if (response != SES_PORT_OK) {
        return SES_PORT_ERROR;
    }

    response = SpiRead(tblIndex, (uint16_t)(rxLength + pad_to_block), rx_frame);
    if (response != SES_PORT_OK) {
        (void)SES_PORT_Free(rx_frame);
        return response;
    }

    /* Step 6 – deliver to the SES stack */
    response = SES_ReceiveMessage(tblIndex,
                                  SES_PORT_spiInterface,
                                  rxLength,
                                  (void *)rx_frame,
                                  (-1),
                                  (0),
                                  NULL);
    SES_PORT_Free(rx_frame);
    return response;
}

/**
 * @brief  Initialise the PS-SPI peripheral, GPIO interrupt pin, and IRQ task.
 *
 * Replaces FT_OpenEx / FT4222_SPIMaster_Init / CreateThread.
 */
static int InitDrv(int tblIndex)
{
    int              rv;
    XSpiPs_Config   *spiCfg_p;
    XGpioPs_Config  *gpioCfg_p;
    int              xilStatus;
    SES_PORT_threadData_t *thStruct_p = NULL;
    BaseType_t        taskCreated;

    /* ── 1. Initialise PS-SPI ── */
    spiCfg_p = XSpiPs_LookupConfig(SES_SPI_DEVICE_ID);
    if (spiCfg_p == NULL) {
        return SES_PORT_ERROR;
    }

    xilStatus = XSpiPs_CfgInitialize(
                    &spiLinkDescription_g[tblIndex].spiInst,
                    spiCfg_p,
                    spiCfg_p->BaseAddress);
    if (xilStatus != XST_SUCCESS) {
        return SES_PORT_ERROR;
    }

    /*
     * ADIN3310 SPI: CPOL=0, CPHA=0 (mode 0), 8-bit, MSB first.
     * XSPIPS_MASTER_OPTION   – this device is the SPI master
     * XSPIPS_FORCE_SSELECT_OPTION – hardware drives CS automatically
     *                               (de-asserts after each transfer)
     */
    xilStatus = XSpiPs_SetOptions(
                    &spiLinkDescription_g[tblIndex].spiInst,
                    XSPIPS_MASTER_OPTION |
                    XSPIPS_FORCE_SSELECT_OPTION);
    if (xilStatus != XST_SUCCESS) {
        return SES_PORT_ERROR;
    }

    xilStatus = XSpiPs_SetClkPrescaler(
                    &spiLinkDescription_g[tblIndex].spiInst,
                    SES_SPI_CLK_PRESCALE);
    if (xilStatus != XST_SUCCESS) {
        return SES_PORT_ERROR;
    }

    /* Select the chip-select line */
    xilStatus = XSpiPs_SetSlaveSelect(
                    &spiLinkDescription_g[tblIndex].spiInst,
                    (u8)spiLinkDescription_g[tblIndex].chipSelect);
    if (xilStatus != XST_SUCCESS) {
        return SES_PORT_ERROR;
    }

    /* ── 2. Initialise PS-GPIO for interrupt pin (MIO 62) ── */
    gpioCfg_p = XGpioPs_LookupConfig(SES_GPIO_DEVICE_ID);
    if (gpioCfg_p == NULL) {
        return SES_PORT_ERROR;
    }

    xilStatus = XGpioPs_CfgInitialize(
                    &spiLinkDescription_g[tblIndex].gpioInst,
                    gpioCfg_p,
                    gpioCfg_p->BaseAddr);
    if (xilStatus != XST_SUCCESS) {
        return SES_PORT_ERROR;
    }

    /* Configure MIO 62 as input */
    XGpioPs_SetDirectionPin(
        &spiLinkDescription_g[tblIndex].gpioInst,
        SES_PORT_IRQ_GPIO_PIN,
        0U);   /* 0 = input */

    /*
     * ── 3. Connect GPIO ISR to GIC ──
     *
     * XPAR_PSU_GPIO_0_INTR is the GIC SPI interrupt ID for the PS GPIO block.
     * All 78 MIO pins share this one GIC line; the ISR reads the interrupt
     * status register to identify which pin fired.
     */
    xilStatus = XScuGic_Connect(
                    &XInterruptController,
                    XPAR_PSU_GPIO_0_INTR,
                    (Xil_ExceptionHandler)GpioIsr,
                    (void *)&spiLinkDescription_g[tblIndex].gpioInst);
    if (xilStatus != XST_SUCCESS) {
        return SES_PORT_ERROR;
    }

    /* Rising-edge interrupt on MIO 62 */
    XGpioPs_SetIntrTypePin(
        &spiLinkDescription_g[tblIndex].gpioInst,
        SES_PORT_IRQ_GPIO_PIN,
        XGPIOPS_IRQ_TYPE_EDGE_RISING);

    XGpioPs_IntrEnablePin(
        &spiLinkDescription_g[tblIndex].gpioInst,
        SES_PORT_IRQ_GPIO_PIN);

    XScuGic_Enable(&XInterruptController, XPAR_PSU_GPIO_0_INTR);

    /* ── 4. Create the IRQ handler task ── */
    rv = SES_PORT_Malloc((void **)&thStruct_p,
                         sizeof(SES_PORT_threadData_t));
    if (rv != SES_PORT_OK) {
        return SES_PORT_ERROR;
    }
    thStruct_p->drvHdlIdx = tblIndex;

    /*
     * xTaskCreate() replaces CreateThread().
     * The task runs at the highest priority so interrupt-driven frames are
     * processed before any lower-priority application tasks — matching the
     * THREAD_PRIORITY_TIME_CRITICAL setting in the Windows reference.
     */
    taskCreated = xTaskCreate(TaskSpiIrq,
                              SES_PORT_IRQ_TASK_NAME,
                              SES_PORT_IRQ_TASK_STACK_WORDS,
                              (void *)thStruct_p,
                              SES_PORT_IRQ_TASK_PRIORITY,
                              &spiLinkDescription_g[tblIndex].irqTask);

    if (taskCreated != pdPASS) {
        SES_PORT_Free(thStruct_p);
        return SES_PORT_THREAD_FAIL;
    }

    return SES_PORT_OK;
}

/**
 * @brief  Low-level SPI read helper.
 *
 * Prepends the 1-byte READ command (0x80), performs a full-duplex polled
 * transfer, and copies the received payload (skipping the command echo byte)
 * into @p dataRx_p.
 *
 * Block-alignment padding is added on the TX side so the ADIN3310 SPI framing
 * is satisfied (identical calculation to the Windows reference).
 */
static int SpiRead(int tblIndex, uint16_t length, uint8_t *dataRx_p)
{
    int      response = SES_PORT_OK;
    int16_t  txLength;
    int16_t  pad_to_block;
    uint8_t *tx_frame = NULL;
    uint8_t *rx_frame = NULL;
    int      spiFrmSz;
    int      xilStatus;

    /* Round up to block boundary */
    txLength     = (int16_t)(length + SES_PORT_SPI_BLOCK_LENGTH - 1);
    txLength    &= (int16_t)(~(SES_PORT_SPI_BLOCK_LENGTH - 1));
    pad_to_block = txLength - (int16_t)length;

    spiFrmSz = (int)(SES_PORT_SPI_CMD_SZ + length + pad_to_block);

    /* Allocate TX and RX working buffers */
    response = SES_PORT_Malloc((void **)&tx_frame, spiFrmSz);
    if (response != SES_PORT_OK) {
        return SES_PORT_ERROR;
    }

    response = SES_PORT_Malloc((void **)&rx_frame, spiFrmSz);
    if (response != SES_PORT_OK) {
        (void)SES_PORT_Free(tx_frame);
        return SES_PORT_ERROR;
    }

    /* Build TX frame: READ command + zero padding */
    tx_frame[0] = SES_PORT_SPI_READ_CMD;
    memset(tx_frame + SES_PORT_SPI_CMD_SZ, 0x00U,
           (size_t)(spiFrmSz - SES_PORT_SPI_CMD_SZ));

    /* ── Take SPI bus mutex ── */
    SES_PORT_WaitSemaphore(spiLinkDescription_g[tblIndex].spiProtectSem,
                           SES_PORT_SEM_INFINITE_WAIT);

    xilStatus = XSpiPs_PolledTransfer(
                    &spiLinkDescription_g[tblIndex].spiInst,
                    tx_frame,
                    rx_frame,
                    (u32)spiFrmSz);

    SES_PORT_SignalSemaphore(spiLinkDescription_g[tblIndex].spiProtectSem);

    if (xilStatus != XST_SUCCESS) {
        response = SES_PORT_ERROR;
    }

    /* Free TX buffer */
    if (SES_PORT_Free(tx_frame) != SES_PORT_OK) {
        (void)SES_PORT_Free(rx_frame);
        return SES_PORT_ERROR;
    }

    /* Copy payload (skip the echoed command byte) into caller's buffer */
    if (response == SES_PORT_OK) {
        memcpy(dataRx_p, rx_frame + SES_PORT_SPI_CMD_SZ, length);
    }

    if (SES_PORT_Free(rx_frame) != SES_PORT_OK) {
        response = SES_PORT_ERROR;
    }

    return response;
}

/**
 * @brief  FreeRTOS IRQ handler task — replaces ThreadSpiIrq().
 *
 * Blocks indefinitely on rxEventSem.  The GPIO ISR posts that semaphore
 * whenever MIO 62 sees a rising edge.  On each wake-up the task reads the
 * GPIO pin level and, if still asserted, processes one received message.
 *
 * The task self-deletes when stopTask is set (by SpiClose).
 */
static void TaskSpiIrq(void *pvParam)
{
    SES_PORT_threadData_t  localData;
    SES_PORT_threadData_t *param_p = (SES_PORT_threadData_t *)pvParam;

    /* Copy data locally and free the heap allocation */
    memcpy(&localData, param_p, sizeof(SES_PORT_threadData_t));
    SES_PORT_Free(param_p);

    for (;;) {
        /*
         * Block until GpioIsr posts the semaphore.
         * portMAX_DELAY = wait forever (mirrors INFINITE in the Windows port).
         */
        xSemaphoreTake(
            spiLinkDescription_g[localData.drvHdlIdx].rxEventSem,
            portMAX_DELAY);

        /* Check the exit flag set by SpiClose() */
        if (spiLinkDescription_g[localData.drvHdlIdx].stopTask) {
            break;
        }

        /*
         * Confirm the interrupt pin is still high before reading.
         * Replaces FT4222_GPIO_Read() — reads MIO 62 directly.
         */
        if (XGpioPs_ReadPin(
                &spiLinkDescription_g[localData.drvHdlIdx].gpioInst,
                SES_PORT_IRQ_GPIO_PIN) == 1U) {
            HandleReceivedMessage(localData.drvHdlIdx);
        }
    }

    /* Signal SpiClose() that we have exited */
    xSemaphoreGive(
        spiLinkDescription_g[localData.drvHdlIdx].rxEventSem);

    vTaskDelete(NULL);
}

/**
 * @brief  GPIO ISR — fires on the rising edge of MIO 62.
 *
 * Clears the interrupt status flag and posts rxEventSem so that TaskSpiIrq
 * wakes up.  Replaces the FT_EVENT_RXCHAR / SetEvent() mechanism.
 *
 * @param callbackRef  Pointer to the XGpioPs instance for this link.
 */
static void GpioIsr(void *callbackRef)
{
    XGpioPs       *gpio_p = (XGpioPs *)callbackRef;
    BaseType_t     xHigherPriorityTaskWoken = pdFALSE;
    u32            pendingIrq;

    /* Read which pins triggered */
    pendingIrq = XGpioPs_IntrGetStatusPin(gpio_p, SES_PORT_IRQ_GPIO_PIN);

    if (pendingIrq) {
        /* Clear the interrupt at the GPIO level */
        XGpioPs_IntrClearPin(gpio_p, SES_PORT_IRQ_GPIO_PIN);

        /*
         * Find the link whose gpioInst matches and post its rxEventSem.
         * In a single-link build this is always index 0; the loop makes
         * it robust for multi-link configurations (SES_PORT_MAX_SPI_LINKS > 1).
         */
        for (int i = 0; i < SES_PORT_MAX_SPI_LINKS; i++) {
            if (spiLinkDescription_g[i].initialized &&
                (&spiLinkDescription_g[i].gpioInst == gpio_p)) {
                xSemaphoreGiveFromISR(
                    spiLinkDescription_g[i].rxEventSem,
                    &xHigherPriorityTaskWoken);
                break;
            }
        }
    }

    /* Yield to a higher-priority task if one was unblocked */
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

/**
 * @brief  Scan the link-description table for a free slot.
 *
 * @return  Table index on success, -1 if all slots are occupied.
 */
static int GetLinkDescriptionEntry(void)
{
    int i;
    int freeEntry = -1;

    for (i = 0; i < SES_PORT_MAX_SPI_LINKS; i++) {
        if (spiLinkDescription_g[i].initialized == 0) {
            freeEntry = i;
            break;
        }
    }

    return freeEntry;
}

/**
 * @brief  Stop the IRQ task, disable the interrupt, and clean up resources.
 *
 * Replaces the Windows TerminateThread / FT4222_UnInitialize / FT_Close
 * sequence.
 */
static int SpiClose(int tblIndex)
{
    TickType_t waitTicks =
        pdMS_TO_TICKS(SES_PORT_SPI_WAIT_THREAD_STOP_MS);

    if (spiLinkDescription_g[tblIndex].irqTask == NULL) {
        DBG_INFO("SpiClose: invalid task handle");
        return SES_PORT_INVALID_PARAM;
    }

    /* ── 1. Ask the IRQ task to exit and wake it up ── */
    spiLinkDescription_g[tblIndex].stopTask = 1;
    xSemaphoreGive(spiLinkDescription_g[tblIndex].rxEventSem);

    /*
     * Wait for the task to re-post the semaphore as its exit handshake.
     * If it doesn't respond within the timeout, we give up and return error.
     */
    if (xSemaphoreTake(spiLinkDescription_g[tblIndex].rxEventSem,
                       waitTicks) != pdTRUE) {
        return SES_PORT_ERROR;
    }

    /* ── 2. Disable the GPIO interrupt ── */
    XGpioPs_IntrDisablePin(
        &spiLinkDescription_g[tblIndex].gpioInst,
        SES_PORT_IRQ_GPIO_PIN);

    XScuGic_Disconnect(&XInterruptController, XPAR_PSU_GPIO_0_INTR);

    /* ── 3. Delete the FreeRTOS binary semaphore ── */
    vSemaphoreDelete(spiLinkDescription_g[tblIndex].rxEventSem);
    spiLinkDescription_g[tblIndex].rxEventSem = NULL;

    /* ── 4. Clear the link-description entry ── */
    SES_PORT_WaitSemaphore(spiLinkDescription_g[tblIndex].spiProtectSem,
                           SES_PORT_PROTECTION_TIMEOUT);
    memset(&spiLinkDescription_g[tblIndex], 0,
           sizeof(SES_PORT_spiLinkDescription_t));
    /* NOTE: spiProtectSem is cleared by the memset above; do not signal
     * it after this point.  The SES_PORT_DeleteSemaphore() was called
     * implicitly when the slot was zeroed — if your RTOS heap requires
     * explicit deletion, call it before the memset. */

    return SES_PORT_OK;
}

// End Static Functions *******************************************************

/////////////example_config




#include <stdio.h>

/* FreeRTOS — needed only for vTaskDelay() used in FIRMWARE_UPDATE path */
#include "FreeRTOS.h"
#include "task.h"

/* SES driver headers — unchanged from reference */
#include "SES_PORT_interface.h"
#include "SES_interface_management.h"
#include "SES_configuration.h"
#include "SES_codes.h"

// End Includes ***************************************************************

// Functions ******************************************************************

/**
 * @brief  Initialise the SES stack and configure the ADIN3310 switch ports.
 *
 * Call this once from your FreeRTOS application task (NOT from main() before
 * the scheduler starts) because SES_Init() and SES_AddHwInterface() internally
 * create FreeRTOS objects (semaphores, tasks).
 *
 * Sequence
 * ─────────
 *  1. SES_Init()            — initialise the SES library internals.
 *  2. SES_AddHwInterface()  — register the SPI transport (calls our
 *                             SES_PORT_SPI_Init() which sets up XSpiPs,
 *                             XGpioPs MIO-62, the GIC ISR, and the IRQ task).
 *  3. SES_AddDevice()       — tell the library which MAC address identifies
 *                             this ADIN3310 on the bus.
 *  4. SES_InitializePorts() — push the per-port PHY configuration to the
 *                             switch over SPI.
 *
 * @return SES_PORT_OK on success, negative SES error code on failure.
 */
int32_t sesConfig(void)
{
    int32_t  rv;
    uint8_t  sesPrimaryMac[6] = SES_PRIMARY_MAC;


    /*
     * Initialise to 0, not NULL.
     * interfaceId and deviceId are integers returned by the SES library;
     * assigning NULL (a pointer constant) causes a -Wint-conversion warning
     * with arm-none-eabi-gcc.
     */
    int      interfaceId = 0;
    sesID_t  deviceId    = 0;

    /* ── Port configurations ── */

    /*
     * ADIN6310 — 6-port configuration (kept for completeness; not used when
     * SES_PORT_COUNT == 3 and FIELD_SWITCH == 0).
     */
//    const SES_portInit_t portConfiguration6310[6] = {
//        { 1, SES_rgmiiMode, { 0, 0, 0 }, 1, SES_phyADIN1300, { true, 0, 0, SES_phySpeed1000, SES_phyDuplexModeFull, SES_autoMdix } },
//        { 1, SES_rgmiiMode, { 0, 0, 0 }, 0, SES_phyADIN1300, { true, 0, 1, SES_phySpeed1000, SES_phyDuplexModeFull, SES_autoMdix } },
//        { 1, SES_rgmiiMode, { 0, 0, 0 }, 0, SES_phyADIN1300, { true, 0, 2, SES_phySpeed1000, SES_phyDuplexModeFull, SES_autoMdix } },
//        { 1, SES_rgmiiMode, { 0, 0, 0 }, 0, SES_phyADIN1300, { true, 0, 4, SES_phySpeed1000, SES_phyDuplexModeFull, SES_autoMdix } },
//        { 1, SES_rgmiiMode, { 0, 0, 0 }, 0, SES_phyADIN1300, { true, 0, 8, SES_phySpeed1000, SES_phyDuplexModeFull, SES_autoMdix } },
//        { 1, SES_rgmiiMode, { 0, 0, 0 }, 0, SES_phyADIN1300, { true, 0, 9, SES_phySpeed1000, SES_phyDuplexModeFull, SES_autoMdix } }
//    };

    /*
     * ADIN3310 — 3-port configuration.
     * This is the active path for your Zynq build (SES_PORT_COUNT == 3).
     */
    const SES_portInit_t portConfiguration3310[3] = {
        { 1, SES_rgmiiMode, { 0, 0, 0 }, 1, SES_phyADIN1300, { true, 0, 0, SES_phySpeed1000, SES_phyDuplexModeFull, SES_autoMdix } },
        { 1, SES_rgmiiMode, { 0, 0, 0 }, 1, SES_phyADIN1300, { true, 0, 1, SES_phySpeed1000, SES_phyDuplexModeFull, SES_autoMdix } },
        { 1, SES_rgmiiMode, { 0, 0, 0 }, 1, SES_phyADIN1300, { true, 0, 2, SES_phySpeed1000, SES_phyDuplexModeFull, SES_autoMdix } }
    };

//    /* Field Switch Board — 6-port mixed ADIN1100/ADIN1300 configuration */
//    const SES_portInit_t portConfigurationFieldSwitch[6] = {
//        { 1, SES_rgmiiMode, { 0, 0, 0 }, 1, SES_phyADIN1100, { true, 1, 0, SES_phySpeed10,   SES_phyDuplexModeFull, SES_autoMdix } },
//        { 1, SES_rgmiiMode, { 0, 0, 0 }, 1, SES_phyADIN1100, { true, 1, 1, SES_phySpeed10,   SES_phyDuplexModeFull, SES_autoMdix } },
//        { 1, SES_rgmiiMode, { 0, 0, 0 }, 1, SES_phyADIN1300, { true, 1, 6, SES_phySpeed1000, SES_phyDuplexModeFull, SES_autoMdix } },
//        { 1, SES_rgmiiMode, { 0, 0, 0 }, 1, SES_phyADIN1300, { true, 1, 5, SES_phySpeed1000, SES_phyDuplexModeFull, SES_autoMdix } },
//        { 1, SES_rgmiiMode, { 0, 0, 0 }, 1, SES_phyADIN1100, { true, 1, 2, SES_phySpeed10,   SES_phyDuplexModeFull, SES_autoMdix } },
//        { 1, SES_rgmiiMode, { 0, 0, 0 }, 1, SES_phyADIN1100, { true, 1, 3, SES_phySpeed10,   SES_phyDuplexModeFull, SES_autoMdix } }
//    };

    /* ── SES Configuration Over SPI ── */
#ifndef ENABLE_SPI

    /*
     * Register the three SPI transport function pointers.
     * These point to our FreeRTOS/Zynq implementations in
     * SES_PORT_SPI_interface.c — no change needed here.
     */
    SES_driverFunctions_t driverFunctions = {
        .init_p        = SES_PORT_SPI_Init,
        .release_p     = SES_PORT_SPI_Release,
        .sendMessage_p = SES_PORT_SPI_SendMessage
    };

    /*
     * Init parameters for the Zynq PS-SPI port.
     *
     * CHANGED: SES_PORT_ft4222InitParams_t  →  SES_PORT_zynqSpiInitParams_t
     *
     * The FT4222 struct had:
     *   .baseLocationId  — USB location ID, not applicable on Zynq
     *   .spiMode         — single/dual/quad, handled by XSpiPs options
     *
     * Our Zynq struct only needs:
     *   .chipSelect      — which CS line the ADIN3310 is on (0 for single device)
     *
     * SES_PORT_SPI_Init() reads this and passes chipSelect to
     * XSpiPs_SetSlaveSelect() during hardware initialisation.
     */
    SES_PORT_zynqSpiInitParams_t initParameter = {
        .chipSelect = 0    /* ADIN3310 is the only device on CS0 */
    };

    printf("SES Configuration Over SPI (Zynq PS-SPI, MIO-62 IRQ)\n");

    /* Step 1: Initialise SES library internals */
    rv = SES_Init();
    sesGetAppInfo_Example();
    if (rv == SES_PORT_OK) {

        /*
         * Step 2: Register the SPI hardware interface.
         *
         * Internally this calls SES_PORT_SPI_Init() which:
         *   a) Calls XSpiPs_CfgInitialize() — opens the PS-SPI peripheral
         *   b) Calls XGpioPs_CfgInitialize() — opens MIO GPIO
         *   c) Configures MIO pin 62 as input with rising-edge interrupt
         *   d) Connects GpioIsr() to the GIC
         *   e) Creates the FreeRTOS IRQ handler task (TaskSpiIrq)
         *
         * On return, interfaceId is a small integer index (e.g. 0) that
         * identifies this SPI link inside the SES routing layer.
         */
        rv = SES_AddHwInterface(&initParameter, &driverFunctions, &interfaceId);
        if (rv == SES_PORT_OK) {

            /*
             * Step 3: Tell SES the MAC address of the ADIN3310.
             * The library will embed this in the SPI frame headers so the
             * switch can identify the host.
             */
            rv = SES_AddDevice(interfaceId, sesPrimaryMac, &deviceId);
            if (rv == SES_PORT_OK) {

                /*
                 * Step 4: Push port configuration to the ADIN3310 over SPI.
                 * For a 3-port ADIN3310 with FIELD_SWITCH == 0, this sends
                 * portConfiguration3310[] to the switch.
                 */
                if (FIELD_SWITCH) {
//                    rv = SES_InitializePorts(SES_PORT_COUNT,
//                                            portConfigurationFieldSwitch);
                } else if (SES_PORT_COUNT == 6) {
//                    rv = SES_InitializePorts(SES_PORT_COUNT,
//                                            portConfiguration6310);
                } else {
                    rv = SES_InitializePorts(SES_PORT_COUNT,
                                            portConfiguration3310);
                }
            }
        }
    }

#else
    /*
     * SES Configuration Over Ethernet — retained for reference only.
     * This path is NOT used on Zynq (we use SPI).
     * Compile with -DENABLE_SPI to always take the SPI path above.
     */
    uint8_t hostMac[6] = HOST_MAC;

    SES_driverFunctions_t driverFunctions = {
        .init_p          = SES_PORT_ETH_Init,
        .release_p       = SES_PORT_ETH_Release,
        .sendMessage_p   = SES_PORT_ETH_SendMessage,
        .updateFilter_p  = SES_PORT_ETH_UpdateFilter
    };

    printf("SES Configuration Over Ethernet\n");

    rv = SES_Init();
    if (rv == SES_PORT_OK) {
        rv = SES_AddHwInterface(hostMac, &driverFunctions, &interfaceId);
        if (rv == SES_PORT_OK) {
            rv = SES_AddDevice(interfaceId, sesPrimaryMac, &deviceId);
            if (rv == SES_PORT_OK) {
                if (FIELD_SWITCH) {
                    rv = SES_InitializePorts(6, portConfigurationFieldSwitch);
                } else if (SES_PORT_COUNT == 6) {
                    rv = SES_InitializePorts(SES_PORT_COUNT, portConfiguration6310);
                } else {
                    rv = SES_InitializePorts(SES_PORT_COUNT, portConfiguration3310);
                }
            }
        }
    }
#endif

    return rv;
}

/* ── Helper: print firmware version info ── */
void sesGetAppInfo_Example(void)
{
    SES_appInfo_t appInfo;

    printf("---------------------FIRMWARE DETAILS----------------------\n");
    SES_GetFirmwareInfo(&appInfo, sizeof(appInfo));
    printf("appInfoVersion :: %d\n",  appInfo.appInfoVersion);
    printf("buildNumber    :: %d\n",  appInfo.buildNumber);
    printf("name           :: %s\n",  appInfo.name);
    printf("part Number    :: %s\n",  appInfo.partNum);
    printf("Version        :: %s\n",  appInfo.version);
    printf("-----------------------------------------------\n\n");
}

/**
 * @brief  Run whichever protocol examples are selected via compile-time flags.
 *
 * The interactive TIME_SYNC loop uses scanf() which works on the Vitis UART
 * console.  If your application has no stdin, remove or guard that block.
 *
 * CHANGED: Sleep(5000) → vTaskDelay(pdMS_TO_TICKS(5000))
 *   Sleep() is a Windows API (milliseconds).
 *   vTaskDelay() is the FreeRTOS equivalent — it yields the current task for
 *   the given number of ticks, letting other tasks run during the wait.
 *   pdMS_TO_TICKS() converts 5000 ms → the correct tick count regardless of
 *   configTICK_RATE_HZ.
 */
void sesProtocolInitialization_Example(void)
{
//    int input;

//    if (DEFAULT_SWITCH) {
//        printf("Initializing the switch with default configurations !!\n");
//        sesLldpInit_Example();
//        sesSingleASPtpInstance_Example();
//        sesMstpStackStart_Example();
//    }
//    else if (HSR_PRP) {
//        sesHsrPrp_Example();
//    }
//    else {
//        if (TIME_SYNC)          { sesTimeSync_Example(); }
//        if (SCHEDULED_TRAFFIC)  { sesScheduledTrafficGuardBand_Example(); }
//        if (FRAME_PREEMPTION)   { sesSetPreemptionConfigurationStructure_Example(); }
//        if (SENDLIST)           { sesSendlist_Example(); }
//        if (FRER)               { sesFrer_Example(); }
//        if (PSFP)               { sesPsfp_Example(); }
//        if (MRP)                { sesMrp_Example(); }
//        if (MSTP)               { sesMultipleMsti_Example(); }
//    }
//
//    if (EVENT)           { sesEvent_Example(); }
//    if (LLDP_INIT)       { sesLldpInit_Example(); }
//
//    if (FIRMWARE_UPDATE) {
//        sesFirmwareUpdate_Example();
//        /*
//         * CHANGED: Sleep(5000) → vTaskDelay(pdMS_TO_TICKS(5000))
//         *
//         * Wait 5 seconds for the firmware update to complete.
//         * vTaskDelay() blocks ONLY this task; the FreeRTOS scheduler
//         * continues running other tasks (IRQ handler, idle, etc.) during
//         * the wait.  Sleep() on Windows would have blocked the whole thread.
//         */
//        vTaskDelay(pdMS_TO_TICKS(5000));
//    }
//
//    if (IGMP_SNOOPING)   { sesIgmp_Example(); }
//    if (VLAN_CONFIG)     { sesVlan_Example(); }
//    if (SWITCH_EXAMPLE)  { sesSwitch_Example(); }
//    if (LAYER_2)         { seslayer2_Example(); }
//    if (UPDATE_SOURCE_MAC) { sesUpdateMACAddress_Example(); }
//
//    /*
//     * Interactive PTP parameter read loop.
//     * Runs on the Vitis UART console (stdin/stdout routed to UART0 by BSP).
//     * The while(1) here is intentional — the user types 9 to exit.
//     */
//    if (TIME_SYNC) {
//        while (1) {
//            printf("\n");
//            printf("*-------- Time Sync Read Parameters --------\n");
//            printf("1: Read Port Specific Parameters\n");
//            printf("2: Read CMLDS Specific Parameters\n");
//            printf("9: Exit\n");
//            scanf("%d", &input);
//            printf("Option Entered :: %d\n", input);
//
//            if (input == 9) {
//                break;
//            } else {
//                sesGetPtpParameters_Example(input);
//            }
//        }
//    }
}

// End Functions **************************************************************


//////memory and semaphore


// Includes *******************************************************************

#include "FreeRTOS.h"       /* pvPortMalloc / vPortFree                       */
#include "SES_port_api.h"

// End Includes ***************************************************************

// Macros, typedefs, enums ****************************************************

/*
 * The Windows reference implementation rounds every allocation up to 98 bytes
 * to mimic the fixed-block FIFO mempool behaviour of the original RTOS port.
 * Keep the same floor so that buffer-size assumptions in the SES driver hold.
 */
#define SES_PORT_MIN_BLK_SIZE   (98U)

// End Macros, typedefs, enums ************************************************

// Functions ******************************************************************

/**
 * @brief  Allocate a buffer of at least @p size bytes.
 *
 * Uses FreeRTOS heap (pvPortMalloc) so the allocation is thread-safe and
 * participates in the same heap-4/heap-5 pool as the rest of the application.
 *
 * @param[out] buf_pp  Receives the address of the allocated buffer.
 * @param[in]  size    Minimum number of bytes required.
 *
 * @retval SES_PORT_OK       Allocation succeeded; *buf_pp is valid.
 * @retval SES_PORT_BUF_ERR  buf_pp is NULL, or heap exhausted.
 */
int32_t SES_PORT_Malloc(void **buf_pp, int size)
{
    if (buf_pp == NULL) {
        return SES_PORT_BUF_ERR;
    }

    /* Mirror the reference: enforce a minimum block size */
    if (size < (int)SES_PORT_MIN_BLK_SIZE) {
        size = (int)SES_PORT_MIN_BLK_SIZE;
    }
    
    
    
    
    
#include "FreeRTOS.h"
#include "semphr.h"         /* SemaphoreHandle_t, xSemaphoreCreateCounting … */
#include "SES_port_api.h"

// End Includes ***************************************************************

// Macros, typedefs, enums ****************************************************

/*
 * Helper: convert the opaque (int*) handle used by the SES API back into
 * a FreeRTOS SemaphoreHandle_t.  Both are pointer-sized on Cortex-A53.
 */
#define TO_SEM_HANDLE(p)    ((SemaphoreHandle_t)(p))

// End Macros, typedefs, enums ************************************************

// Functions ******************************************************************

/**
 * @brief  Create a counting semaphore.
 *
 * @param[in] initCount  Initial semaphore count (tokens available immediately).
 * @param[in] maxCount   Maximum semaphore count.
 *
 * @return  Opaque handle cast to (int*) on success, NULL on failure.
 *
 * @note  maxCount must be >= initCount and >= 1, otherwise
 *        xSemaphoreCreateCounting() returns NULL.
 */
void* SES_PORT_CreateSemaphore(int initCount, int maxCount)
{
    SemaphoreHandle_t sem;

    sem = xSemaphoreCreateCounting((UBaseType_t)maxCount,
                                   (UBaseType_t)initCount);
    if (sem == NULL) {
        return NULL;
    }

    /*
     * Cast to (int*) to match the SES API signature.
     * The actual value stored is a FreeRTOS queue handle (pointer).
     */
    return (int*)sem;
}

/**
 * @brief  Wait (take) a semaphore, blocking for up to @p timeout ms.
 *
 * @param[in] semaphore_p  Handle returned by SES_PORT_CreateSemaphore().
 * @param[in] timeout      Maximum wait time in milliseconds.
 *                         Pass portMAX_DELAY_MS (0xFFFFFFFF) to wait forever.
 *
 * @retval SES_PORT_OK             Semaphore taken successfully.
 * @retval SES_PORT_TIMEOUT        Timed out before the semaphore was available.
 * @retval SES_PORT_INVALID_PARAM  semaphore_p is NULL.
 * @retval SES_PORT_ERROR          Unexpected FreeRTOS error.
 */
int32_t SES_PORT_WaitSemaphore(int* semaphore_p, int timeout)
{
    BaseType_t result;
    TickType_t ticks;

    if (semaphore_p == NULL) {
        return SES_PORT_INVALID_PARAM;
    }

    /*
     * Convert milliseconds → FreeRTOS ticks.
     * A timeout of portMAX_DELAY (0xFFFFFFFF ms) is treated as "wait forever"
     * so we propagate it as portMAX_DELAY ticks without overflow.
     */
    if ((uint32_t)timeout == 0xFFFFFFFFU) {
        ticks = portMAX_DELAY;
    } else {
        ticks = pdMS_TO_TICKS((uint32_t)timeout);
    }

    result = xSemaphoreTake(TO_SEM_HANDLE(semaphore_p), ticks);

    if (result == pdTRUE) {
        return SES_PORT_OK;
    }

    /*
     * FreeRTOS returns pdFALSE for both timeout and other failures;
     * in normal usage this is always a timeout (the semaphore was not
     * signalled within the allotted time).
     */
    return SES_PORT_TIMEOUT;
}

/**
 * @brief  Signal (give) a semaphore.
 *
 * @param[in] semaphore_p  Handle returned by SES_PORT_CreateSemaphore().
 *
 * @retval  1 (non-zero)          Semaphore signalled successfully.
 * @retval  0                     xSemaphoreGive() failed (count at maximum).
 * @retval  SES_PORT_INVALID_PARAM  semaphore_p is NULL.
 *
 * @note  Must NOT be called from an ISR.  If you need to signal from an ISR,
 *        replace xSemaphoreGive() with xSemaphoreGiveFromISR() and handle
 *        the higher-priority task wakeup accordingly.
 */
int32_t SES_PORT_SignalSemaphore(int* semaphore_p)
{
    BaseType_t result;

    if (semaphore_p == NULL) {
        return SES_PORT_INVALID_PARAM;
    }

    result = xSemaphoreGive(TO_SEM_HANDLE(semaphore_p));

    /*
     * Mirror the Windows port: return non-zero on success, zero on failure.
     * (The SES driver checks the return value as a boolean.)
     */
    return (result == pdTRUE) ? 1 : 0;
}

/**
 * @brief  Delete a semaphore and free its resources.
 *
 * @param[in] semaphore_p  Handle returned by SES_PORT_CreateSemaphore().
 *
 * @retval  1 (non-zero)          Semaphore deleted successfully.
 * @retval  SES_PORT_INVALID_PARAM  semaphore_p is NULL.
 *
 * @note  After this call the handle is invalid; do not use it again.
 *        Ensure no task is blocked on the semaphore before deleting it.
 */
int32_t SES_PORT_DeleteSemaphore(int* semaphore_p)
{
    if (semaphore_p == NULL) {
        return SES_PORT_INVALID_PARAM;
    }

    vSemaphoreDelete(TO_SEM_HANDLE(semaphore_p));

    /*
     * vSemaphoreDelete() is void — it always succeeds when given a valid
     * handle.  Return non-zero to indicate success, consistent with the
     * Windows reference implementation.
     */
    return 1;
}

// End Functions **************************************************************

  • Hey Kowsik,

     

    Thanks for reaching out and for your question. I'd like to clarify a few things and dive into the details to help tackle the issue you're experiencing.

     

    When you observe 1.25 seconds high and 2ms low, it indicates that the image request is originating from the ethernet switch, which suggests that there's no Firmware present on the switch. If you bypass the CheckMacFirmwareVersion() call and proceed directly to SES_InitializePorts(), you'll encounter a timeout error as expected, since the FW flash version check was skipped.

     

    To get a better handle on the problem, I'd appreciate it if you could answer a couple of questions:

     

    1) Are you using standard SPI to flash the Firmware, or are you utilizing QSPI?

    2) Would you be able to share the porting layer you've implemented for your host?

    3) Could you provide some logs or captures from your host when it's attempting to flash the firmware onto the chip?

     

    I'm attaching a Saleae capture that shows what a successful firmware load looks like. Please take a look at the SPI transactions in the capture – it might be helpful for comparison. If you have your own capture, feel free to share it, and we can review it.

     

    Looking forward to hearing from you and getting more information so we can resolve this.

     

    Best regards,

    Naman


    ses-5-1-fw-file-update-from-erased-prod.zip

  • Hi Naman

          Sorry for the late reply I went through the Saleae captures that you have provided .I enabled the SetMacCheckFirmwareVersion() and then debugged through it in the SetMacCheckFirmwareVersion() in the GetRemoteSesInterfaces() in that SES_MX_GETInterfaces() in that SES_GetInterfaces_non_blocking () in that RPC_SendRequest() in that SMP_SendMessage()in that SES_ROUTE_SendMessage() in that spi_sendmessage is called then the data is sent first 0x40 and the data is as same as the data that is shown in the captures given by you except after the 0x02 the last 7 bytes i am getting 0's  . but when it is entering into the read it is not reading any thing on the miso and on mosi i am getting 0x80 and 4 bytes 0 's .All i am seeing on the miso line some noise like during the spi clock but if no clock i am getting no noise flat zero line .

    /*
     * Copyright (c) 2024 Analog Devices, Inc. All Rights Reserved.
     *
     * This software is proprietary and confidential to Analog Devices, Inc.
     * and its licensors.
     */
    
    /*
     * FreeRTOS Port — Zynq UltraScale+ MPSoC (Vitis / FreeRTOS 10)
     * Target  : ADIN3310 Ethernet Switch — SPI interface abstraction
     * Toolchain: Xilinx Vitis, arm-none-eabi-gcc
     *
     * Hardware mapping
     * ─────────────────────────────────────────────────────────────────────────────
     *  Windows reference          This port
     *  ─────────────────────────  ──────────────────────────────────────────────
     *  FT4222 USB-SPI bridge      PS-SPI (XSpiPs) on Zynq MPSoC
     *  FT4222 GPIO port 3 (IRQ)   MIO GPIO pin 62  (XGpioPs)
     *  Windows CreateThread()     FreeRTOS xTaskCreate()
     *  WaitForSingleObject(event) FreeRTOS binary semaphore (IRQ → task notify)
     *  HANDLE / BOOL              XSpiPs_Config * / BaseType_t
     *
     * SPI configuration for ADIN3310 (UG2287 §3.1)
     *   Mode  : CPOL=0, CPHA=0  (SPI mode 0)
     *   Width : 8-bit
     *   CS    : active-low, driven by hardware (SSNBS)
     *   Max   : 25 MHz — use the clock divider appropriate for your PL/PS
     *
     * GPIO / Interrupt
     *   MIO 62 is configured as input with a rising-edge interrupt routed to
     *   GIC via XScuGic.  The ISR posts a binary semaphore; the IRQ task
     *   takes that semaphore and calls HandleReceivedMessage().
     *
     * FreeRTOSConfig.h requirements
     *   configUSE_COUNTING_SEMAPHORES   1
     *   configUSE_TASK_NOTIFICATIONS    1   (optional — binary sem used instead)
     *   INCLUDE_vTaskDelete             1
     *   configMAX_PRIORITIES            ≥ (SES_PORT_IRQ_TASK_PRIORITY + 1)
     *
     * Vivado / Vitis assumptions
     *   - xparameters.h defines XPAR_PSU_SPI_0_DEVICE_ID / BASEADDR
     *   - xparameters.h defines XPAR_PSU_GPIO_0_DEVICE_ID
     *   - xparameters.h defines XPAR_PSU_GPIO_0_INTR  (GIC SPI ID for GPIO)
     *   - GIC driver (XScuGic) is already initialised by the BSP before
     *     SES_PORT_SPI_Init() is called.
     * ─────────────────────────────────────────────────────────────────────────────
     */
    
    // Includes *******************************************************************
    
    #include <string.h>
    #include <stdint.h>
    #include <stdio.h>
    
    /* FreeRTOS */
    #include "FreeRTOS.h"
    #include "task.h"
    #include "semphr.h"
    
    /* Xilinx BSP */
    #include "xparameters.h"
    #include "xspips.h"          /* PS SPI driver                                 */
    #include "xgpiops.h"         /* PS GPIO driver                                */
    #include "xscugic.h"         /* GIC interrupt controller                      */
    #include "xil_exception.h"
    
    /* SES driver */
    #include "SES_debug.h"
    #include "SES_port_api.h"
    #include "SES_PORT_interface.h"
    
    // End Includes ***************************************************************
    
    // Macros, typedefs, enums ****************************************************
    
    /* ── General ── */
    #define SES_PORT_SPI_MAX_TYPE               (15)
    #define SES_PORT_SPI_TFER_TYPE_MASK         (0xF)
    #define SES_PORT_SEM_INFINITE_WAIT          (-1)
    
    /* Milliseconds the Release path waits for the IRQ task to finish */
    #define SES_PORT_SPI_WAIT_THREAD_STOP_MS    (5000U)
    
    /* ── SPI frame commands (identical to reference) ── */
    #define SES_PORT_SPI_READ_CMD               (0x80U)
    #define SES_PORT_SPI_WRITE_CMD              (0x40U)
    #define SES_PORT_SPI_CMD_SZ                 (1U)
    
    /* ── Hardware IDs ── pull from xparameters.h; override here if needed ── */
    #ifndef SES_SPI_DEVICE_ID
    #  define SES_SPI_DEVICE_ID                 XPAR_PSU_SPI_0_DEVICE_ID
    #endif
    
    #ifndef SES_GPIO_DEVICE_ID
    #  define SES_GPIO_DEVICE_ID                XPAR_PSU_GPIO_0_DEVICE_ID
    #endif
    
    /* MIO pin used as the ADIN3310 interrupt input */
    #define SES_PORT_IRQ_GPIO_PIN               (62U)
    #define gpio								(63U)
    
    /*
     * SPI clock divider.
     * PS-SPI ref clock is typically 200 MHz on ZU+.
     * XSPIPS_CLK_PRESCALE_8  → 25 MHz  (ADIN3310 maximum).
     * Adjust if your ref clock differs.
     */
    #define SES_SPI_CLK_PRESCALE                XSPIPS_CLK_PRESCALE_32
    
    /* FreeRTOS task parameters for the IRQ handler task */
    #define SES_PORT_IRQ_TASK_STACK_WORDS       (1024U)
    #define SES_PORT_IRQ_TASK_PRIORITY          (configMAX_PRIORITIES - 1U)
    #define SES_PORT_IRQ_TASK_NAME              "SES_SpiIrq"
    
    /* ── Struct ── */
    typedef struct {
        int               initialized;
        void             *spiProtectSem;   /* counting sem, max=1: SPI bus mutex  */
        SemaphoreHandle_t rxEventSem;      /* binary sem: ISR → IRQ task          */
        TaskHandle_t      irqTask;         /* FreeRTOS IRQ handler task           */
        volatile int      stopTask;        /* flag: ask the IRQ task to exit      */
        XSpiPs            spiInst;         /* Xilinx PS-SPI instance              */
        XGpioPs           gpioInst;        /* Xilinx PS-GPIO instance             */
        int               chipSelect;      /* SPI CS index (0 for single-device)  */
    } SES_PORT_spiLinkDescription_t;
    
    /* Passed through pvPortMalloc to the IRQ task on creation */
    typedef struct {
        int drvHdlIdx;
    } SES_PORT_threadData_t;
    
    // End Macros, typedefs, enums ************************************************
    
    // Global Variables ***********************************************************
    
    static SES_PORT_spiLinkDescription_t
        spiLinkDescription_g[SES_PORT_MAX_SPI_LINKS] = { { 0 } };
    
    /* Protects the spiLinkDescription_g[] table during Init/Release */
    static void *dataTableProtectionSem_gp = NULL;
    
    /*
     * Shared GIC handle.
     * The application must initialise this before calling SES_PORT_SPI_Init().
     * Declare it extern here so the SPI port can register its GPIO ISR.
     */
    extern XScuGic XInterruptController;
    
    // End Global Variables *******************************************************
    
    // Static Function Prototypes *************************************************
    
    static int  GetLinkDescriptionEntry(void);
    static int  HandleReceivedMessage(int tblIndex);
    static int  InitDrv(int tblIndex);
    static int  SpiClose(int tblIndex);
    static int  SpiRead(int tblIndex, uint16_t length, uint8_t *dataRx_p);
    static void TaskSpiIrq(void *pvParam);
    static void GpioIsr(void *callbackRef);
    
    // End Static Function Prototypes *********************************************
    
    // Functions ******************************************************************
    
    /**
     * @brief  Initialise the SPI interface to the ADIN3310.
     *
     * Replaces the Windows FT4222 open/init sequence with XSpiPs + XGpioPs
     * initialisation.  The init parameter struct is SES_PORT_zynqSpiInitParams_t
     * (defined in SES_PORT_interface.h — see note in that file).
     *
     * @param[in]  param_p     Pointer to SES_PORT_zynqSpiInitParams_t.
     * @param[out] intfType_p  Set to SES_PORT_spiInterface on success.
     * @param[out] srcMac_p    Not used for SPI; may be NULL.
     *
     * @return  Non-negative interface handle on success, negative error code on failure.
     */
    int SES_PORT_SPI_Init(void *param_p,
                          SES_PORT_intfType_t *intfType_p,
                          uint8_t *srcMac_p)
    {
        int intfHandle;
        int response;
        SES_PORT_zynqSpiInitParams_t *initParams_p =
            (SES_PORT_zynqSpiInitParams_t *)param_p;
    
        (void)srcMac_p; /* not used for SPI */
    
        /* ── Parameter check ── */
        if ((param_p == NULL) || (intfType_p == NULL)) {
            return SES_PORT_INVALID_PARAM;
        }
    
        /* ── Create the table-protection semaphore once ── */
        if (dataTableProtectionSem_gp == NULL) {
            dataTableProtectionSem_gp = SES_PORT_CreateSemaphore(1, 1);
            if (dataTableProtectionSem_gp == NULL) {
                return SES_PORT_ERROR;
            }
        }
    
        SES_PORT_WaitSemaphore(dataTableProtectionSem_gp,
                               SES_PORT_PROTECTION_TIMEOUT);
    
        /* ── Find a free link-description slot ── */
        intfHandle = GetLinkDescriptionEntry();
        if (intfHandle < SES_PORT_OK) {
            SES_PORT_SignalSemaphore(dataTableProtectionSem_gp);
            return SES_PORT_ERROR;
        }
    
        /* ── Per-link SPI-bus mutex (counting sem, max = 1) ── */
        spiLinkDescription_g[intfHandle].spiProtectSem =
            SES_PORT_CreateSemaphore(1, 1);
        if (spiLinkDescription_g[intfHandle].spiProtectSem == NULL) {
            SES_PORT_SignalSemaphore(dataTableProtectionSem_gp);
            return SES_PORT_ERROR;
        }
    
        /*
         * ── IRQ event semaphore ──
         * Binary semaphore:  ISR calls xSemaphoreGiveFromISR(),
         *                    TaskSpiIrq() calls xSemaphoreTake().
         * Replaces the Windows CreateEvent(auto-reset) + WaitForSingleObject().
         */
        spiLinkDescription_g[intfHandle].rxEventSem =
            xSemaphoreCreateBinary();
        if (spiLinkDescription_g[intfHandle].rxEventSem == NULL) {
            SES_PORT_DeleteSemaphore(
                spiLinkDescription_g[intfHandle].spiProtectSem);
            SES_PORT_SignalSemaphore(dataTableProtectionSem_gp);
            return SES_PORT_ERROR;
        }
    
        /* Store the chip-select index supplied by the caller */
        spiLinkDescription_g[intfHandle].chipSelect = initParams_p->chipSelect;
        spiLinkDescription_g[intfHandle].stopTask   = 0;
    
        SES_PORT_SignalSemaphore(dataTableProtectionSem_gp);
    
        /* ── Hardware init (SPI + GPIO + IRQ task) ── */
        response = InitDrv(intfHandle);
        if (response != SES_PORT_OK) {
            vSemaphoreDelete(spiLinkDescription_g[intfHandle].rxEventSem);
            SES_PORT_DeleteSemaphore(
                spiLinkDescription_g[intfHandle].spiProtectSem);
            return SES_PORT_ERROR;
        }
    
        spiLinkDescription_g[intfHandle].initialized = 1;
        *intfType_p = SES_PORT_spiInterface;
    
        return intfHandle;
    }
    
    /**
     * @brief  Release (close) the SPI interface.
     *
     * Signals the IRQ task to stop, waits up to SES_PORT_SPI_WAIT_THREAD_STOP_MS,
     * then tears down GPIO, SPI and semaphores.
     */
    int SES_PORT_SPI_Release(int intfHandle)
    {
        int rv = SES_PORT_OK;
    
        SES_PORT_WaitSemaphore(dataTableProtectionSem_gp,
                               SES_PORT_PROTECTION_TIMEOUT);
    
        if (!spiLinkDescription_g[intfHandle].initialized) {
            DBG_INFO("SES_PORT_SPI_Release called before Initialization");
            rv = SES_PORT_NOT_INITIALIZED;
        }
    
        if (rv == SES_PORT_OK) {
            rv = SpiClose(intfHandle);
            if (rv != SES_PORT_OK) {
                rv = SES_PORT_ERROR;
            }
        }
    
        SES_PORT_SignalSemaphore(dataTableProtectionSem_gp);
    
        return rv;
    }
    
    /**
     * @brief  Send a framed message to the ADIN3310 over SPI.
     *
     * Prepends the 1-byte WRITE command (0x40) then transfers the full frame
     * using XSpiPs_PolledTransfer() under the protection of the per-link mutex.
     *
     * @param[in] intfHandle  Handle returned by SES_PORT_SPI_Init().
     * @param[in] size        Payload size in bytes (NOT including command byte).
     * @param[in] data_p      Pointer to payload buffer (freed on success).
     *
     * @return SES_PORT_OK on success, negative error code on failure.
     */
    int SES_PORT_SPI_SendMessage(int intfHandle, int size, void *data_p)
    {
        int      result   = SES_PORT_ERROR;
        int      response = SES_PORT_ERROR;
        int i;
        int      spiFrmSz;
        int      count    = 0;
        int      status;
        uint8_t *tx_frame = NULL;
    
        /* Dummy RX buffer — XSpiPs always full-duplex */
        uint8_t *rx_dummy = NULL;
    
        if (!spiLinkDescription_g[intfHandle].initialized) {
            return SES_PORT_NOT_INITIALIZED;
        }
    
        if (data_p == NULL) {
            return SES_PORT_INVALID_PARAM;
        }
    
        /* Build TX frame: [0x40 | payload] */
        spiFrmSz = size + SES_PORT_SPI_CMD_SZ;
    
        result = SES_PORT_Malloc((void **)&tx_frame, spiFrmSz);
        if (result != SES_PORT_OK) {
            return SES_PORT_ERROR;
        }
    
        result = SES_PORT_Malloc((void **)&rx_dummy, spiFrmSz);
        if (result != SES_PORT_OK) {
            SES_PORT_Free(tx_frame);
            return SES_PORT_ERROR;
        }
    
        tx_frame[0] = SES_PORT_SPI_WRITE_CMD;
        memcpy(tx_frame + SES_PORT_SPI_CMD_SZ, data_p, size);
    
        /* ── Take SPI bus mutex ── */
        SES_PORT_WaitSemaphore(spiLinkDescription_g[intfHandle].spiProtectSem,
                               SES_PORT_SEM_INFINITE_WAIT);
    
        /*
         * XSpiPs_PolledTransfer drives CS automatically when configured with
         * XSPIPS_MANUAL_START_OPTION disabled (the default after SetOptions).
    //     */
    //    while(1){
        status = XSpiPs_PolledTransfer(
                     &spiLinkDescription_g[intfHandle].spiInst,
                     tx_frame,
                     rx_dummy,
                     (u32)spiFrmSz);
        for(i=0;i<1000;i++);
    //    }
    
        SES_PORT_SignalSemaphore(spiLinkDescription_g[intfHandle].spiProtectSem);
    
        count = (status == XST_SUCCESS) ? spiFrmSz : 0;
    
        SES_PORT_Free(tx_frame);
        SES_PORT_Free(rx_dummy);
    
        if (count != spiFrmSz) {
            response = SES_PORT_ERROR;
        } else {
            response = SES_PORT_OK;
            SES_PORT_Free(data_p);   /* convention: caller's buffer freed on OK */
        }
    
        return response;
    }
    
    /**
     * @brief  Return a human-readable string describing the SPI interface.
     *
     * The FT4222 device enumeration has no equivalent on Zynq; we return a
     * static description based on the BSP parameters instead.
     */
    int32_t SES_PORT_SPI_GetInterfaceInfo(char *interfaceInfo_p,
                                          uint16_t bufferSize)
    {
        if ((interfaceInfo_p == NULL) || (bufferSize == 0U)) {
            return SES_PORT_BUF_ERR;
        }
    
        int written = snprintf(interfaceInfo_p, bufferSize,
                               "0,Zynq-UltraScale-PS-SPI(dev=%d,gpio=%d);",
                               (int)SES_SPI_DEVICE_ID,
                               (int)SES_PORT_IRQ_GPIO_PIN);
    
        return (written > 0) ? SES_PORT_OK : SES_PORT_BUF_ERR;
    }
    
    // End Functions **************************************************************
    
    // Static Functions ***********************************************************
    
    /**
     * @brief  Handle one incoming SPI message from the ADIN3310.
     *
     * Reads the 4-byte header, validates the length and transfer-type fields,
     * allocates a receive buffer, reads the payload, then hands everything to
     * SES_ReceiveMessage().  Logic is identical to the Windows reference.
     */
    static int HandleReceivedMessage(int tblIndex)
    {
        int      response = SES_PORT_OK;
        uint8_t  transferType;
        int16_t  txLength;
        int16_t  pad_to_block;
        uint32_t header   = 0U;
        int      rxLength;
        uint8_t *rx_frame = NULL;
    
        /* Step 1 – read the 4-byte header */
        response = SpiRead(tblIndex, sizeof(header), (uint8_t *)&header);
        if (response != SES_PORT_OK) {
            return response;
        }
    
        /* Step 2 – decode and validate length (lower 12 bits) */
        rxLength = (int)((uint16_t)(header & SES_PORT_SPI_LENGTH_MASK));
        if (rxLength != (int)(((~header >> SES_PORT_SPI_INVERTED_LENGTH_SHIFT) &
                               SES_PORT_SPI_LENGTH_MASK))) {
            return SES_PORT_ERROR;
        }
    
        /* Step 3 – decode and validate transfer type (4 bits) */
        transferType = (uint8_t)((header >> SES_PORT_SPI_TYPE_SHIFT) &
                                 SES_PORT_SPI_TFER_TYPE_MASK);
        if (transferType != (uint8_t)(((~header >> SES_PORT_SPI_INVERTED_TYPE_SHIFT) &
                                       SES_PORT_SPI_TFER_TYPE_MASK))) {
            return SES_PORT_ERROR;
        }
    
        /* Step 4 – round up to block boundary */
        txLength     = (int16_t)(rxLength + SES_PORT_SPI_BLOCK_LENGTH - 1);
        txLength    &= (int16_t)(~(SES_PORT_SPI_BLOCK_LENGTH - 1));
        pad_to_block = txLength - (int16_t)rxLength;
    
        /* Step 5 – allocate and fill the payload buffer */
        response = SES_PORT_Malloc((void **)&rx_frame,
                                   (int)(rxLength + pad_to_block));
        if (response != SES_PORT_OK) {
            return SES_PORT_ERROR;
        }
    
        response = SpiRead(tblIndex, (uint16_t)(rxLength + pad_to_block), rx_frame);
        if (response != SES_PORT_OK) {
            (void)SES_PORT_Free(rx_frame);
            return response;
        }
    
        /* Step 6 – deliver to the SES stack */
        response = SES_ReceiveMessage(tblIndex,
                                      SES_PORT_spiInterface,
                                      rxLength,
                                      (void *)rx_frame,
                                      (-1),
                                      (0),
                                      NULL);
        SES_PORT_Free(rx_frame);
        return response;
    }
    
    /**
     * @brief  Initialise the PS-SPI peripheral, GPIO interrupt pin, and IRQ task.
     *
     * Replaces FT_OpenEx / FT4222_SPIMaster_Init / CreateThread.
     */
    static int InitDrv(int tblIndex)
    {
        int              rv;
        XSpiPs_Config   *spiCfg_p;
        XGpioPs_Config  *gpioCfg_p;
        int              xilStatus;
        SES_PORT_threadData_t *thStruct_p = NULL;
        BaseType_t        taskCreated;
    
        /* ── 1. Initialise PS-SPI ── */
        spiCfg_p = XSpiPs_LookupConfig(SES_SPI_DEVICE_ID);
        if (spiCfg_p == NULL) {
            return SES_PORT_ERROR;
        }
    
        xilStatus = XSpiPs_CfgInitialize(
                        &spiLinkDescription_g[tblIndex].spiInst,
                        spiCfg_p,
                        spiCfg_p->BaseAddress);
        if (xilStatus != XST_SUCCESS) {
            return SES_PORT_ERROR;
        }
    
        /*
         * ADIN3310 SPI: CPOL=0, CPHA=0 (mode 0), 8-bit, MSB first.
         * XSPIPS_MASTER_OPTION   – this device is the SPI master
         * XSPIPS_FORCE_SSELECT_OPTION – hardware drives CS automatically
         *                               (de-asserts after each transfer)
         */
        xilStatus = XSpiPs_SetOptions(
                        &spiLinkDescription_g[tblIndex].spiInst,
                        XSPIPS_MASTER_OPTION |
                        XSPIPS_FORCE_SSELECT_OPTION);
        if (xilStatus != XST_SUCCESS) {
            return SES_PORT_ERROR;
        }
    
        xilStatus = XSpiPs_SetClkPrescaler(
                        &spiLinkDescription_g[tblIndex].spiInst,
                        SES_SPI_CLK_PRESCALE);
        if (xilStatus != XST_SUCCESS) {
            return SES_PORT_ERROR;
        }
    
        /* Select the chip-select line */
        xilStatus = XSpiPs_SetSlaveSelect(
                        &spiLinkDescription_g[tblIndex].spiInst,
                        0x01);
        if (xilStatus != XST_SUCCESS) {
            return SES_PORT_ERROR;
        }
    
        /* ── 2. Initialise PS-GPIO for interrupt pin (MIO 62) ── */
        gpioCfg_p = XGpioPs_LookupConfig(SES_GPIO_DEVICE_ID);
        if (gpioCfg_p == NULL) {
            return SES_PORT_ERROR;
        }
    
        xilStatus = XGpioPs_CfgInitialize(
                        &spiLinkDescription_g[tblIndex].gpioInst,
                        gpioCfg_p,
                        gpioCfg_p->BaseAddr);
        if (xilStatus != XST_SUCCESS) {
            return SES_PORT_ERROR;
        }
    
        /* Configure MIO 62 as input */
        XGpioPs_SetDirectionPin(
            &spiLinkDescription_g[tblIndex].gpioInst,
            SES_PORT_IRQ_GPIO_PIN,
            0U);   /* 0 = input */
    
        /*
         * ── 3. Connect GPIO ISR to GIC ──
         *
         * XPAR_PSU_GPIO_0_INTR is the GIC SPI interrupt ID for the PS GPIO block.
         * All 78 MIO pins share this one GIC line; the ISR reads the interrupt
         * status register to identify which pin fired.
         */
        xilStatus = XScuGic_Connect(
                        &XInterruptController,
                        XPAR_PSU_GPIO_0_INTR,
                        (Xil_ExceptionHandler)GpioIsr,
                        (void *)&spiLinkDescription_g[tblIndex].gpioInst);
        if (xilStatus != XST_SUCCESS) {
            return SES_PORT_ERROR;
        }
    
        /* Rising-edge interrupt on MIO 62 */
        XGpioPs_SetIntrTypePin(
            &spiLinkDescription_g[tblIndex].gpioInst,
            SES_PORT_IRQ_GPIO_PIN,
    		XGPIOPS_IRQ_TYPE_EDGE_BOTH);
    
        XGpioPs_IntrEnablePin(
            &spiLinkDescription_g[tblIndex].gpioInst,
            SES_PORT_IRQ_GPIO_PIN);
    
        XScuGic_Enable(&XInterruptController, XPAR_PSU_GPIO_0_INTR);
    
        /* ── 4. Create the IRQ handler task ── */
        rv = SES_PORT_Malloc((void **)&thStruct_p,
                             sizeof(SES_PORT_threadData_t));
        if (rv != SES_PORT_OK) {
            return SES_PORT_ERROR;
        }
        thStruct_p->drvHdlIdx = tblIndex;
    
        /*
         * xTaskCreate() replaces CreateThread().
         * The task runs at the highest priority so interrupt-driven frames are
         * processed before any lower-priority application tasks — matching the
         * THREAD_PRIORITY_TIME_CRITICAL setting in the Windows reference.
         */
        taskCreated = xTaskCreate(TaskSpiIrq,
                                  SES_PORT_IRQ_TASK_NAME,
                                  SES_PORT_IRQ_TASK_STACK_WORDS,
                                  (void *)thStruct_p,
                                  SES_PORT_IRQ_TASK_PRIORITY,
                                  &spiLinkDescription_g[tblIndex].irqTask);
    
        if (taskCreated != pdPASS) {
            SES_PORT_Free(thStruct_p);
            return SES_PORT_THREAD_FAIL;
        }
    
        return SES_PORT_OK;
    }
    
    /**
     * @brief  Low-level SPI read helper.
     *
     * Prepends the 1-byte READ command (0x80), performs a full-duplex polled
     * transfer, and copies the received payload (skipping the command echo byte)
     * into @p dataRx_p.
     *
     * Block-alignment padding is added on the TX side so the ADIN3310 SPI framing
     * is satisfied (identical calculation to the Windows reference).
     */
    static int SpiRead(int tblIndex, uint16_t length, uint8_t *dataRx_p)
    {
        int      response = SES_PORT_OK;
        int16_t  txLength;
        int16_t  pad_to_block;
        uint8_t *tx_frame = NULL;
        uint8_t *rx_frame = NULL;
        int      spiFrmSz;
        int      xilStatus;
    
        /* Round up to block boundary */
        txLength     = (int16_t)(length + SES_PORT_SPI_BLOCK_LENGTH - 1);
        txLength    &= (int16_t)(~(SES_PORT_SPI_BLOCK_LENGTH - 1));
        pad_to_block = txLength - (int16_t)length;
    
        spiFrmSz = (int)(SES_PORT_SPI_CMD_SZ + length + pad_to_block);
    
        /* Allocate TX and RX working buffers */
        response = SES_PORT_Malloc((void **)&tx_frame, spiFrmSz);
        if (response != SES_PORT_OK) {
            return SES_PORT_ERROR;
        }
    
        response = SES_PORT_Malloc((void **)&rx_frame, spiFrmSz);
        if (response != SES_PORT_OK) {
            (void)SES_PORT_Free(tx_frame);
            return SES_PORT_ERROR;
        }
    
        /* Build TX frame: READ command + zero padding */
        tx_frame[0] = SES_PORT_SPI_READ_CMD;
        memset(tx_frame + SES_PORT_SPI_CMD_SZ, 0x00U,
               (size_t)(spiFrmSz - SES_PORT_SPI_CMD_SZ));
    
        /* ── Take SPI bus mutex ── */
        SES_PORT_WaitSemaphore(spiLinkDescription_g[tblIndex].spiProtectSem,
                               SES_PORT_SEM_INFINITE_WAIT);
        //while(1){
    
        xilStatus = XSpiPs_PolledTransfer(
                        &spiLinkDescription_g[tblIndex].spiInst,
                        tx_frame,
                        rx_frame,
                        (u32)spiFrmSz);
       // for(int i=0;i<10000;i++);
       // }
    
        SES_PORT_SignalSemaphore(spiLinkDescription_g[tblIndex].spiProtectSem);
    
        if (xilStatus != XST_SUCCESS) {
            response = SES_PORT_ERROR;
        }
    
        /* Free TX buffer */
        if (SES_PORT_Free(tx_frame) != SES_PORT_OK) {
            (void)SES_PORT_Free(rx_frame);
            return SES_PORT_ERROR;
        }
    
        /* Copy payload (skip the echoed command byte) into caller's buffer */
        if (response == SES_PORT_OK) {
            memcpy(dataRx_p, rx_frame + SES_PORT_SPI_CMD_SZ, length);
        }
    
        if (SES_PORT_Free(rx_frame) != SES_PORT_OK) {
            response = SES_PORT_ERROR;
        }
    
        return response;
    }
    
    /**
     * @brief  FreeRTOS IRQ handler task — replaces ThreadSpiIrq().
     *
     * Blocks indefinitely on rxEventSem.  The GPIO ISR posts that semaphore
     * whenever MIO 62 sees a rising edge.  On each wake-up the task reads the
     * GPIO pin level and, if still asserted, processes one received message.
     *
     * The task self-deletes when stopTask is set (by SpiClose).
     */
    static void TaskSpiIrq(void *pvParam)
    {
        SES_PORT_threadData_t  localData;
        SES_PORT_threadData_t *param_p = (SES_PORT_threadData_t *)pvParam;
    
        /* Copy data locally and free the heap allocation */
        memcpy(&localData, param_p, sizeof(SES_PORT_threadData_t));
        SES_PORT_Free(param_p);
    
        for (;;) {
            /*
             * Block until GpioIsr posts the semaphore.
             * portMAX_DELAY = wait forever (mirrors INFINITE in the Windows port).
             */
            xSemaphoreTake(
                spiLinkDescription_g[localData.drvHdlIdx].rxEventSem,
                portMAX_DELAY);
    
            /* Check the exit flag set by SpiClose() */
            if (spiLinkDescription_g[localData.drvHdlIdx].stopTask) {
                break;
            }
    
            /*
             * Confirm the interrupt pin is still high before reading.
             * Replaces FT4222_GPIO_Read() — reads MIO 62 directly.
             */
            if (XGpioPs_ReadPin(
                    &spiLinkDescription_g[localData.drvHdlIdx].gpioInst,
                    SES_PORT_IRQ_GPIO_PIN) ==1U) {//changed to 0
                HandleReceivedMessage(localData.drvHdlIdx);
            }
        }
    
        /* Signal SpiClose() that we have exited */
        xSemaphoreGive(
            spiLinkDescription_g[localData.drvHdlIdx].rxEventSem);
    
        vTaskDelete(NULL);
    }
    
    /**
     * @brief  GPIO ISR — fires on the rising edge of MIO 62.
     *
     * Clears the interrupt status flag and posts rxEventSem so that TaskSpiIrq
     * wakes up.  Replaces the FT_EVENT_RXCHAR / SetEvent() mechanism.
     *
     * @param callbackRef  Pointer to the XGpioPs instance for this link.
     */
    static void GpioIsr(void *callbackRef)
    {
        XGpioPs       *gpio_p = (XGpioPs *)callbackRef;
        BaseType_t     xHigherPriorityTaskWoken = pdFALSE;
        u32            pendingIrq;
    
        /* Read which pins triggered */
        pendingIrq = XGpioPs_IntrGetStatusPin(gpio_p, SES_PORT_IRQ_GPIO_PIN);
    
        if (pendingIrq) {
            /* Clear the interrupt at the GPIO level */
            XGpioPs_IntrClearPin(gpio_p, SES_PORT_IRQ_GPIO_PIN);
    
            /*
             * Find the link whose gpioInst matches and post its rxEventSem.
             * In a single-link build this is always index 0; the loop makes
             * it robust for multi-link configurations (SES_PORT_MAX_SPI_LINKS > 1).
             */
            for (int i = 0; i < SES_PORT_MAX_SPI_LINKS; i++) {
                if (spiLinkDescription_g[i].initialized &&
                    (&spiLinkDescription_g[i].gpioInst == gpio_p)) {
                    xSemaphoreGiveFromISR(
                        spiLinkDescription_g[i].rxEventSem,
                        &xHigherPriorityTaskWoken);
                    break;
                }
            }
        }
    
        /* Yield to a higher-priority task if one was unblocked */
        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    }
    
    /**
     * @brief  Scan the link-description table for a free slot.
     *
     * @return  Table index on success, -1 if all slots are occupied.
     */
    static int GetLinkDescriptionEntry(void)
    {
        int i;
        int freeEntry = -1;
    
        for (i = 0; i < SES_PORT_MAX_SPI_LINKS; i++) {
            if (spiLinkDescription_g[i].initialized == 0) {
                freeEntry = i;
                break;
            }
        }
    
        return freeEntry;
    }
    
    /**
     * @brief  Stop the IRQ task, disable the interrupt, and clean up resources.
     *
     * Replaces the Windows TerminateThread / FT4222_UnInitialize / FT_Close
     * sequence.
     */
    static int SpiClose(int tblIndex)
    {
        TickType_t waitTicks =
            pdMS_TO_TICKS(SES_PORT_SPI_WAIT_THREAD_STOP_MS);
    
        if (spiLinkDescription_g[tblIndex].irqTask == NULL) {
            DBG_INFO("SpiClose: invalid task handle");
            return SES_PORT_INVALID_PARAM;
        }
    
        /* ── 1. Ask the IRQ task to exit and wake it up ── */
        spiLinkDescription_g[tblIndex].stopTask = 1;
        xSemaphoreGive(spiLinkDescription_g[tblIndex].rxEventSem);
    
        /*
         * Wait for the task to re-post the semaphore as its exit handshake.
         * If it doesn't respond within the timeout, we give up and return error.
         */
        if (xSemaphoreTake(spiLinkDescription_g[tblIndex].rxEventSem,
                           waitTicks) != pdTRUE) {
            return SES_PORT_ERROR;
        }
    
        /* ── 2. Disable the GPIO interrupt ── */
        XGpioPs_IntrDisablePin(
            &spiLinkDescription_g[tblIndex].gpioInst,
            SES_PORT_IRQ_GPIO_PIN);
    
        XScuGic_Disconnect(&XInterruptController, XPAR_PSU_GPIO_0_INTR);
    
        /* ── 3. Delete the FreeRTOS binary semaphore ── */
        vSemaphoreDelete(spiLinkDescription_g[tblIndex].rxEventSem);
        spiLinkDescription_g[tblIndex].rxEventSem = NULL;
    
        /* ── 4. Clear the link-description entry ── */
        SES_PORT_WaitSemaphore(spiLinkDescription_g[tblIndex].spiProtectSem,
                               SES_PORT_PROTECTION_TIMEOUT);
        memset(&spiLinkDescription_g[tblIndex], 0,
               sizeof(SES_PORT_spiLinkDescription_t));
        /* NOTE: spiProtectSem is cleared by the memset above; do not signal
         * it after this point.  The SES_PORT_DeleteSemaphore() was called
         * implicitly when the slot was zeroed — if your RTOS heap requires
         * explicit deletion, call it before the memset. */
    
        return SES_PORT_OK;
    }
    
    // End Static Functions *******************************************************
    

           Reagarding to your questions 

    1) I am using standered spi in the is the PS spi in the zynq ultrascale +mpsoc 

    2) i am sharing the spi_porting_layer code 

    Looking forward to hearing from you 

    Thanks and Regards,

    kowsik.