HI
I am currently usnig adin3310 etherenet switch in cusyom board i want too configure it through spi from xazu3eg1sfvc78i i am getting the pulses from the timer0 that there is no image my interrupt is taking that and reading i am getting the read first time the 4 bytes header and the second time header + 20 bytes of the payload but the problem i am not able to read the next data i am countinously reading the same data again ad again i am using the windows example i ported the spi porting layer and replayed the spi function everything is fine but the next reads are not happening i have the spi transactions pdf that i need to get for intilization i am attaching the spi porting layer and the read messages i observed .
Thanks and regards ,
kowsik
/*
* 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"
#include "xil_cache.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;
}
//pad_to_block
response = SpiRead(tblIndex, (uint16_t)(rxLength), 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,
0x00);
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);//changed to rising edge
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 d[22]={0};
uint8_t *rx_frame = NULL;
// uint8_t rx_frame[128];
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));
//
// Xil_DCacheFlushRange((INTPTR)tx_frame,(u32)spiFrmSz);
// Xil_DCacheInvalidateRange((INTPTR)rx_frame,(u32)spiFrmSz);
/* ── 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);
for(int i =0;i<=20;i++){
d[i]=rx_frame[i];
}
// Xil_DCacheInvalidateRange((INTPTR)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) {
if(length==4){
memcpy(dataRx_p, rx_frame+ SES_PORT_SPI_CMD_SZ, length);
}
else{
memcpy(dataRx_p, rx_frame+ SES_PORT_SPI_CMD_SZ+4, length);
}
}
for(int i =0;i<=20;i++){
d[i]=dataRx_p[i];
}
//
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);
// }
while (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 *******************************************************
