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 **************************************************************