Notice
Recent Posts
Recent Comments
Link
관리 메뉴

거북이처럼 천천히

xspi_slave_intr_example.c 분석 본문

FPGA 정리/Xilinx SDK Example 분석

xspi_slave_intr_example.c 분석

유로 청년 2025. 1. 15. 15:15

1. xspi_slave_intr_example.c 

 

/******************************************************************************
*
* Copyright (C) 2008 - 2014 Xilinx, Inc.  All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* XILINX  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/
/*****************************************************************************/
/**
* @file xspi_slave_intr_example.c
*
*
* This file contains a design example using the Spi driver (XSpi) and the Spi
* device as a Slave, in interrupt mode.
*
* This example fills the Spi Tx buffer with the number of data bytes it expects
* to receive from the master and then Spi device waits for an external master to
* initiate the transfer. When the master initiates the transfer, the Spi device
* receives data from the master and simultaneously sends the data in Tx buffer
* to the master. Once the transfer is complete, a transfer complete interrupt is
* generated and this example prints the data received from the master. The
* number of bytes to be received by the Spi slave is defined by the constant
* BUFFER_SIZE in this file.
*
* The external SPI devices that are present on the Xilinx boards don't support
* the Master functionality. This example has been tested with Aardvark I2C/SPI
* Host Adapter, an off board external SPI Master device and the Xilinx SPI
* device configured as a Slave. This example has been tested for byte-wide SPI
* transfers.
*
* @note
*
* This example assumes that there is a STDIO device in the system.
*
*<pre>
* MODIFICATION HISTORY:
*
* Ver   Who  Date     Changes
* ----- ---- -------- ---------------------------------------------------------
* 1.00a psk  09/05/08 First Release
* 3.00a ktn  10/22/09 Converted all register accesses to 32 bit access.
*		      Updated to use the HAL APIs/macros. Replaced call to
*		      XSpi_Initialize API with XSpi_LookupConfig and
*		      XSpi_CfgInitialize.
* 3.01a sdm  04/23/10 Enabled DTR Half_empty interrupt so that Tx FIFO is
*		      not empty during a transfer in slave mode.
* 4.2   ms   01/23/17 Added xil_printf statement in main function to
*                     ensure that "Successfully ran" and "Failed" strings
*                     are available in all examples. This is a fix for
*                     CR-965028.
*
*</pre>
******************************************************************************/

/***************************** Include Files *********************************/

#include "xparameters.h"	/* XPAR parameters */
#include "xspi.h"		/* SPI device driver */
#include "xintc.h"		/* Interrupt controller devive driver */
#include "stdio.h"
#include "xil_exception.h"
#include "xil_printf.h"

/************************** Constant Definitions *****************************/

/*
 * The following constants map to the XPAR parameters created in the
 * xparameters.h file. They are defined here such that a user can easily
 * change all the needed parameters in one place.
 */
#define SPI_DEVICE_ID		XPAR_SPI_0_DEVICE_ID
#define INTC_DEVICE_ID		XPAR_INTC_0_DEVICE_ID
#define SPI_IRPT_INTR		XPAR_INTC_0_SPI_0_VEC_ID

/*
 * This is the size of the buffer to be transmitted/received in this example.
 */
#define BUFFER_SIZE		32

/**************************** Type Definitions *******************************/


/***************** Macros (Inline Functions) Definitions *********************/


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

static int SpiSlaveIntrExample(XSpi *SpiInstancePtr, u16 SpiDeviceId);

static int SetupInterruptSystem(XSpi *SpiInstance);

static void SpiHandler(void *CallBackRef, u32 StatusEvent,
		       unsigned int ByteCount);

/************************** Variable Definitions *****************************/
/*
 * The instances to support the device drivers are global such that they are
 * initialized to zero each time the program runs. They could be local but
 * should at least be static so that they are zeroed.
 */
static XSpi  SpiInstance;   /* Instance of the SPI device */
static XIntc IntcInstance;  /* Instance of the Interrupt controller device */

/*
 * The following variables are used to read/write from the  Spi device, these
 * are global to avoid having large buffers on the stack.
 */
u8 ReadBuffer[BUFFER_SIZE];
u8 WriteBuffer[BUFFER_SIZE];

/*
 * The following variable allows a test value to be added to the values that
 * are sent in reflection to the Master transmission such that unique values can
 * be generated to guarantee the transfer from Slave to Master is successful.
 */
int Test;

/*
 * The following variables are shared between non-interrupt processing and
 * interrupt processing such that they must be global.
 */
static volatile int TransferInProgress;

/*****************************************************************************/
/**
*
* Main function to call the Spi Slave example in interrupt mode.
*
* @param	None
*
* @return	XST_SUCCESS if successful, otherwise XST_FAILURE.
*
* @note		None
*
******************************************************************************/
int main(void)
{
	int Status;

	/*
	 * Run the Spi Slave interrupt example.
	 */
	Status = SpiSlaveIntrExample(&SpiInstance, SPI_DEVICE_ID);
	if (Status != XST_SUCCESS) {
		xil_printf("Spi slave interrupt Example Failed\r\n");
		return XST_FAILURE;
	}

	xil_printf("Successfully ran Spi slave interrupt Example\r\n");
	return XST_SUCCESS;
}

/*****************************************************************************/
/**
*
* This function does a minimal test on the Spi device and driver as a design
* example. The purpose of this function is to illustrate the device slave
* functionality in interrupt mode. This function receives data from a master and
* prints the received data.
*
* @param	SpiInstancePtr is a pointer to the instance of Spi component.
* @param	SpiDeviceId is the Device ID of the Spi Device and is the
*		XPAR_<SPI_instance>_DEVICE_ID value from xparameters.h.
*
* @return	XST_SUCCESS if successful, otherwise XST_FAILURE.
*
* @note		This function contains an infinite loop such that if the Spi
*		device doesn't receive any data or if the interrupts are not
*		working, it may never return.
*
******************************************************************************/
static int SpiSlaveIntrExample(XSpi *SpiInstancePtr, u16 SpiDeviceId)
{
	XSpi_Config *ConfigPtr;
	int Status;
	u32 Count;

	xil_printf("\r\nEntering the Spi Slave Interrupt Example.\r\n");
	xil_printf("Waiting for data from SPI master\r\n");

	/*
	 * Initialize the SPI driver so that it's ready to use, specify the
	 * device ID that is generated in xparameters.h.
	 */
	ConfigPtr = XSpi_LookupConfig(SpiDeviceId);
	if (ConfigPtr == NULL) {
		return XST_FAILURE;
	}

	Status = XSpi_CfgInitialize(SpiInstancePtr, ConfigPtr,
			ConfigPtr->BaseAddress);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*
	 * Connect the SPI driver to the interrupt subsystem such that
	 * interrupts can occur. This function is application specific.
	 */
	Status = SetupInterruptSystem(SpiInstancePtr);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*
	 * Setup the handler for the SPI that will be called from the interrupt
	 * context when an SPI status occurs, specify a pointer to the SPI
	 * driver instance as the callback reference so the handler is able to
	 * access the instance data.
	 */
	XSpi_SetStatusHandler(SpiInstancePtr,SpiInstancePtr,(XSpi_StatusHandler)
			      SpiHandler);

	/*
	 * The SPI device is a slave by default and the clock phase and polarity
	 * have to be set according to its master. In this example, CPOL is set
	 * to active low and CPHA is set to 1.
	 */
	Status = XSpi_SetOptions(SpiInstancePtr, XSP_CLK_PHASE_1_OPTION |
				 XSP_CLK_ACTIVE_LOW_OPTION);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*
	 * Start the SPI driver so that the device is enabled.
	 */
	XSpi_Start(SpiInstancePtr);

	/*
	 * Enable the DTR half-empty interrupt while transfering more than
	 * FIFO_DEPTH number of bytes in slave mode, so that the Tx FIFO
	 * is never empty during a transfer. If the Tx FIFO is empty during
	 * a transfer, it results in master receiving invalid data.
	 */
	XSpi_IntrEnable(SpiInstancePtr, XSP_INTR_TX_HALF_EMPTY_MASK);

	/*
	 * Initialize the write buffer with pattern to write, initialize the
	 * read buffer to zero so it can be verified after the read.
	 */
	Test = 0x50;
	for (Count = 0; Count < BUFFER_SIZE; Count++) {
		WriteBuffer[Count] = (u8)(Count + Test);
		ReadBuffer[Count] = 0;
	}

	/*
	 * Transmit data as a slave, when the master starts sending data.
	 */
	TransferInProgress = TRUE;
	Status = XSpi_Transfer(SpiInstancePtr, WriteBuffer, ReadBuffer,
				BUFFER_SIZE);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*
	 * Wait till the transfer is complete.
	 */
	while (TransferInProgress == TRUE);

	/*
	 * Print all the data received from the master.
	 */
	xil_printf("\r\nReceived data is:\r\n");
	for (Count = 0; Count < BUFFER_SIZE; Count++) {
		xil_printf("0x%x \r\n", ReadBuffer[Count]);
	}

	xil_printf("\r\nExiting the Spi Slave Interrupt Example.\r\n");

	return XST_SUCCESS;
}
/****************************************************************************/
/**
*
* This function setups the interrupt system such that interrupts can occur
* for the SPI driver. This function is application specific since the actual
* system may or may not have an interrupt controller. The SPI device could
* be directly connected to a processor without an interrupt controller.  The
* user should modify this function to fit the application.
*
* @param	SpiInstance contains a pointer to the instance of the XSpi
* 		component which is going to be connected to the interrupt
*		controller.
*
* @return	XST_SUCCESS if successful, else XST_FAILURE.
*
* @note		None.
*
****************************************************************************/
static int SetupInterruptSystem(XSpi *SpiInstance)
{
	int Status;

	/*
	 * Initialize the interrupt controller driver so that it's ready to use,
	 * specify the device ID that is generated in "xparameters.h".
	 */
	Status = XIntc_Initialize(&IntcInstance, INTC_DEVICE_ID);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*
	 * Connect a device driver handler that will be called when an interrupt
	 * for the device occurs, the device driver handler performs the
	 * specific interrupt processing for the device.
	 */
	Status = XIntc_Connect(&IntcInstance,
				SPI_IRPT_INTR,
				(XInterruptHandler)XSpi_InterruptHandler,
				(void *)SpiInstance);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*
	 * Start the interrupt controller such that interrupts are enabled for
	 * all devices that cause interrupts, specific real mode so that the SPI
	 * can cause interrupts through the interrupt controller.
	 */
	Status = XIntc_Start(&IntcInstance, XIN_REAL_MODE);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*
	 * Enable the interrupt for the Spi device.
	 */
	XIntc_Enable(&IntcInstance, SPI_IRPT_INTR);


	/*
	 * Initialize the exception table.
	 */
	Xil_ExceptionInit();

	/*
	 * Register the interrupt controller handler with the exception table.
	 */
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
				(Xil_ExceptionHandler)XIntc_InterruptHandler,
				&IntcInstance);

	/*
	 * Enable non-critical exceptions.
	 */
	Xil_ExceptionEnable();

	return XST_SUCCESS;
}

/******************************************************************************/
/**
*
* This function is the handler which performs processing for the SPI driver. It
* is called from an interrupt context such that the amount of processing to be
* performed should be minimized. This handler provides an example of how to
* handle SPI interrupts but is application specific.
*
* @param 	CallBackRef is a reference passed to the handler.
* @param	StatusEvent is the status of the SPI.
* @param	ByteCount is the number of bytes transferred.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
static void SpiHandler(void *CallBackRef, u32 StatusEvent,
		       unsigned int ByteCount)
{
	if (StatusEvent == XST_SPI_TRANSFER_DONE) {
		/*
		 * Indicate the transfer on the SPI bus is no longer in
		 * progress.
		 */
		TransferInProgress = FALSE;
	}
}

 

 

 


 

 

 

 

2. 각각의 변수의 역활 및 의미 

2.1) SPI_DEVICE_ID (=XPAR_SPI_0_DEVICE_ID)

#define SPI_DEVICE_ID		XPAR_SPI_0_DEVICE_ID
  • FPGA 시스템에서 SPI 모듈을 식별하기 위한 SPI 모듈 식별자

 

 

2.2) ConfigPtr

XSpi_Config *ConfigPtr;

ConfigPtr = XSpi_LookupConfig(SpiDeviceId);
  • SPI 모듈에 대한 정보를 담고 있는 XSpi_Config 자료형 포인터 변수
  • 해당 포인터 변수를 통해 SPI 모듈을 제어할 수 있는 포인터 변수를 획득에 사용

 

 


 

 

 

 

3. 각 함수의 역활

3.1) XSpi_LookupConfig

/*
* Initialize the SPI driver so that it's ready to use, specify the
* device ID that is generated in xparameters.h.
*/

ConfigPtr = XSpi_LookupConfig(SpiDeviceId);
	
if (ConfigPtr == NULL) {
    return XST_FAILURE;
}
  • XSpi_LookupConfig 함수는 argument로 전달 받은 SPI Device ID를 토대로 해당 SPI 모듈에 대한 정보를 조회하여 해당 SPI 모듈에 대한 정보를 XSpi_Config 자료형 포인터 변수로 반환한다.
  • 이렇게 반환받은 XSpi_Config 형 포인터 변수인 ConfigPtr은 SPI 모듈을 제어하는 XSpi 객체 인스턴스를 초기화하는 작업에 사용된다.

 

 

 

 

3.2) XSpi_CfgInitialize 

Status = XSpi_CfgInitialize(SpiInstancePtr, ConfigPtr,
			ConfigPtr->BaseAddress);

if (Status != XST_SUCCESS) {
	return XST_FAILURE;
}
  • SPI 모듈의 정보를 담고 있는 포인터 변수인 ConfigPtr와 XSpi_CfgInitialize 함수를 활용하여 SPI 모듈을 제어하는 XSpi 객체의 포인터 변수인 SpiInstancePtr를 획득할 수 있다.

 

 

 

 

3.3) XSpi_SetStatusHandler 

/*
* Setup the handler for the SPI that will be called from the interrupt
* context when an SPI status occurs, specify a pointer to the SPI
* driver instance as the callback reference so the handler is able to
* access the instance data.*/
	
XSpi_SetStatusHandler(SpiInstancePtr,SpiInstancePtr,(XSpi_StatusHandler)
		SpiHandler);
  • SPI 장치가 Slave 모드 입장에서 Master으로부터 데이터를 송신 완료되었을 때, 발생하는 Interrupt를 처리하기 위한 Interrupt Handler를 등록하는 과정이다.

 

 

 

3.4) XSpi_SetOptions 

/*
 * The SPI device is a slave by default and the clock phase and polarity
 * have to be set according to its master. In this example, CPOL is set
 * to active low and CPHA is set to 1. */
 
Status = XSpi_SetOptions(SpiInstancePtr, XSP_CLK_PHASE_1_OPTION |
		 XSP_CLK_ACTIVE_LOW_OPTION);

if (Status != XST_SUCCESS) {
	return XST_FAILURE; }
  • XSpi_SetOptions 함수는 SPI 모듈의 동작 조건을 설정하는 역할을 합니다.
  • 이 코드에서 수행하는 작업은 다음과 같습니다:
  • SPI 동작 모드 설정:
    • SPI 통신에서 클럭의 위상(CPHA)와 극성(CPOL)은 데이터 송수신 시 중요한 요소입니다. 이 설정은 마스터 장치의 클럭 신호에 맞춰 슬레이브 장치가 올바르게 동작할 수 있도록 합니다.
    • 이 코드에서는:
      • XSP_CLK_ACTIVE_LOW_OPTION: 클럭 극성을 Active Low로 설정합니다. 즉, 클럭 신호가 LOW일 때 유효하다는 의미입니다.
      • XSP_CLK_PHASE_1_OPTION: 클럭 위상을 1로 설정합니다. 이는 클럭의 첫 번째 엣지(즉, 상승 또는 하강 엣지)에 데이터가 유효하다는 것을 의미합니다.

 

 

 

 

 

3.5) XSpi_IntrEnable 

/*
 * Enable the DTR half-empty interrupt while transfering more than
 * FIFO_DEPTH number of bytes in slave mode, so that the Tx FIFO
 * is never empty during a transfer. If the Tx FIFO is empty during
 * a transfer, it results in master receiving invalid data.
*/
	
XSpi_IntrEnable(SpiInstancePtr, XSP_INTR_TX_HALF_EMPTY_MASK);
  • Slave SPI 모듈의 Interrupt를 활성화시켜주는 동시에 Slave SPI 모듈의 Interrupt 발생 시점을 정의하는 과정이다.
  • Interrupt 발생 시점은 두 번째 Parameter를 통해 정의할 수 있다.

xspi.h 파일내에 Interrupt 발생 시점에 대해서 상수로 정의

'FPGA 정리 > Xilinx SDK Example 분석' 카테고리의 다른 글

xuartlite_intr_example.c 분석  (0) 2025.01.15
xintc_example.c 분석  (0) 2025.01.15