From 2d669b2c21f7100fd68989fc66af3c0252923e7a Mon Sep 17 00:00:00 2001 From: P L Sai Krishna Date: Fri, 7 Aug 2015 21:41:41 +0530 Subject: [PATCH] xilisf: Added examples to test QSPIPSU interface. This patch add polled and interrupt examples to test QSPIPSU flash interface. Signed-off-by: P L Sai Krishna Reviewed-by: Harini Katakam --- .../xilisf_qspipsu_stm_intr_example.c | 740 ++++++++++++++++++ .../xilisf_qspipsu_stm_polled_example.c | 558 +++++++++++++ 2 files changed, 1298 insertions(+) create mode 100644 lib/sw_services/xilisf/examples/xilisf_qspipsu_stm_intr_example.c create mode 100644 lib/sw_services/xilisf/examples/xilisf_qspipsu_stm_polled_example.c diff --git a/lib/sw_services/xilisf/examples/xilisf_qspipsu_stm_intr_example.c b/lib/sw_services/xilisf/examples/xilisf_qspipsu_stm_intr_example.c new file mode 100644 index 00000000..0d8ef9cb --- /dev/null +++ b/lib/sw_services/xilisf/examples/xilisf_qspipsu_stm_intr_example.c @@ -0,0 +1,740 @@ +/****************************************************************************** +* +* Copyright (C) 2012 - 2015 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. +* +* Use of the Software is limited solely to applications: +* (a) running on a Xilinx device, or +* (b) that interact with a Xilinx device through a bus or interconnect. +* +* 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 xilisf_qspipsu_flash_intr_example.c +* +* +* This file contains a design example using the XILISF Library in +* interrupt mode with a serial FLASH device. This examples performs +* some transfers in Auto mode and Manual start mode, to illustrate the modes +* available. +* The hardware which this example runs on, must have a serial FLASH (Numonyx +* N25Q, Winbond W25Q, or Spansion S25FL) for it to run. This example has been +* tested with the Numonyx Serial Flash (N25Q128). +* +* @note +* +* None. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who Date     Changes
+* ----- --- -------- -----------------------------------------------
+* 5.4  sk 08/07/15 First release
+*
+*
+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xparameters.h" /**< EDK generated parameters */ +#include "xscugic.h" /**< Interrupt controller device driver */ +#include "xil_exception.h" +#include "xil_printf.h" +#include /**< Serial Flash Library header file */ + +/************************** Constant Definitions *****************************/ +/** @name Device ID's + * + * @{ + */ +/* + * 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 QSPI_DEVICE_ID XPAR_XQSPIPSU_0_DEVICE_ID +#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID +#define QSPI_INTR_ID XPAR_XQSPIPS_0_INTR +/*@}*/ + +/** + * The following constants define the offsets within a FlashBuffer data + * type for each kind of data. Note that the read data offset is not the + * same as the write data because the QSPI driver is designed to allow full + * duplex transfers such that the number of bytes received is the number + * sent and received. + */ +#define DATA_OFFSET 5 /**< Data byte offset for fast, dual + and quad reads */ +#define DUMMY_SIZE 1 /**< Number of dummy bytes for fast, + dual and quad reads */ + +/** + * The following constants specify the page size, sector size, and number of + * pages and sectors for the FLASH. The page size specifies a max number of + * bytes that can be written to the FLASH with a single transfer. + */ +#define NUM_PAGES 0x10000 /**< Number of Pages in the flash */ +#define PAGE_SIZE 256 /**< Page Size for Read/Write Operation */ + +/** + * Number of flash pages to be written. + */ +#define PAGE_COUNT 16 /**< Number of Pages for + Read/Write Operation */ + +/* + * Max page size to initialize write and read buffer + */ +#define MAX_PAGE_SIZE 1024 + +/** + * Flash address to which data is to be written. + */ +#define TEST_ADDRESS 0x0080000 /**< Test Address in the flash */ +#define UNIQUE_VALUE 0x05 /**< Unique Value for Test */ + +/** + * The following constants specify the max amount of data and the size of the + * the buffer required to hold the data and overhead to transfer the data to + * and from the FLASH. + */ +#define MAX_DATA PAGE_COUNT * PAGE_SIZE /**< Max Data Calculated by + multiplying Page count and Page Size*/ + +/** + * The following constant defines the slave select signal that is used to + * to select the FLASH device on the QSPI bus, this signal is typically + * connected to the chip select of the device + */ +#define FLASH_QSPI_SELECT 0x01 + +#define INTR_MODE 1 /**< Interrupt Mode Enable */ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +static int QspiSetupIntrSystem(XScuGic *IntcInstancePtr, + XQspiPsu *QspiInstancePtr, u16 QspiIntrId); + +static void QspiDisableIntrSystem(XScuGic *IntcInstancePtr, u16 QspiIntrId); + +void XilIsf_Handler(void *CallBackRef, u32 StatusEvent, unsigned int ByteCount); + +int FlashErase(XIsf *InstancePtr, u32 Address, u32 ByteCount); +int FlashWrite(XIsf *InstancePtr, u32 Address, u32 ByteCount, u8 Command); +int FlashRead(XIsf *InstancePtr, u32 Address, u32 ByteCount, u8 Command); +int QspiFlashIntrExample(XScuGic *IntcInstancePtr, XQspiPsu *QspiInstancePtr, + u16 QspiDeviceId, u16 QspiIntrId); +u32 SectorMask(u32 SectorSize); + +/************************** 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 they are zeroed. + */ +static XScuGic IntcInstance; +static XQspiPsu QspiInstance; +static XIsf Isf; + +/** + * The following variables are shared between non-interrupt processing and + * interrupt processing such that they must be global. + */ +volatile int TransferInProgress; + +/** + * The following variable tracks any errors that occur during interrupt + * processing + */ +int ErrorCount; + +/** + * The following variable allows a test value to be added to the values that + * are written to the FLASH such that unique values can be generated to + * guarantee the writes to the FLASH were successful + */ +int Test = 5; + +/** + * The following variables are used to read and write to the eeprom and they + * are global to avoid having large buffers on the stack + */ +u8 ReadBuffer[(PAGE_COUNT * MAX_PAGE_SIZE) + (DATA_OFFSET + DUMMY_SIZE)*8] __attribute__ ((aligned(64))); +u8 WriteBuffer[(PAGE_COUNT * MAX_PAGE_SIZE) + DATA_OFFSET]; +u8 IsfWriteBuffer[PAGE_SIZE + XISF_CMD_SEND_EXTRA_BYTES];/**< IsfWrite Buffer + used in XilISF Initialization */ + +/*****************************************************************************/ +/** +* +* Main function to call the QSPIPSU Flash example. +* +* @return XST_SUCCESS if successful, otherwise XST_FAILURE. +* +* @note None +* +******************************************************************************/ +int main(void) +{ + int Status; + + xil_printf("QSPIPSU FLASH Interrupt Example Test \r\n"); + + /* + * Run the QSPIPSU Interrupt example. + */ + Status = QspiFlashIntrExample(&IntcInstance, &QspiInstance, + QSPI_DEVICE_ID, QSPI_INTR_ID); + if (Status != XST_SUCCESS) { + xil_printf("QSPIPSU FLASH Interrupt Example Test Failed\r\n"); + return XST_FAILURE; + } + + xil_printf("Successfully ran QSPIPSU FLASH Interrupt Example Test\r\n"); + return XST_SUCCESS; +} + + +/****************************************************************************/ +/** +* The purpose of this function is to illustrate how to use the XQspiPsu +* device driver in interrupt mode. This function writes and reads data +* from a serial FLASH. +* +* @param IntcInstancePtr is the instance of the interrupt +* @param QspiInstancePtr is the Pointer to the qspipsu driver instance +* @param QspiDeviceId is the Device ID of qspipsu +* @param QspiIntrId is the interrupt ID for QSPIPSU driver +* +* @return XST_SUCCESS if successful else XST_FAILURE. +* +* @note +* +* This function calls other functions which contain loops that may be infinite +* if interrupts are not working such that it may not return. If the device +* slave select is not correct and the device is not responding on bus it will +* read a status of 0xFF for the status register as the bus is pulled up. +* +*****************************************************************************/ +int QspiFlashIntrExample(XScuGic *IntcInstancePtr, XQspiPsu *QspiInstancePtr, + u16 QspiDeviceId, u16 QspiIntrId) +{ + int Status; + u8 *BufferPtr; + u8 UniqueValue; + int Count; + int Page; + XQspiPsu_Config *ConfigPtr; /* Pointer to Configuration ROM data */ + u32 Options; + + /* + * Lookup the device configuration in the temporary CROM table. Use this + * configuration info down below when initializing this component. + */ + ConfigPtr = XQspiPsu_LookupConfig(QspiDeviceId); + if (ConfigPtr == NULL) { + return XST_DEVICE_NOT_FOUND; + } + + Status = XQspiPsu_CfgInitialize(QspiInstancePtr, ConfigPtr, + ConfigPtr->BaseAddress); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + + /* + * Set the QSPI options + */ + Options = XQSPIPSU_MANUAL_START_OPTION; + Status = XIsf_SetSpiConfiguration(&Isf, QspiInstancePtr, + Options, XISF_SPI_PRESCALER); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* Initialize the XILISF Library */ + XIsf_Initialize(&Isf, QspiInstancePtr, FLASH_QSPI_SELECT, + IsfWriteBuffer); + + + + XIsf_SetTransferMode(&Isf, XISF_INTERRUPT_MODE); + + /* + * Connect the Qspipsu device to the interrupt subsystem such that + * interrupts can occur. This function is application specific + */ + Status = QspiSetupIntrSystem(IntcInstancePtr, QspiInstancePtr, + QspiIntrId); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Setup the handler for the QSPI that will be called from the + * interrupt context when an QSPI status occurs, specify a pointer to + * the QSPI driver instance as the callback reference so the handler is + * able to access the instance data + */ + XIsf_SetStatusHandler(&Isf, QspiInstancePtr, + (XQspiPsu_StatusHandler) XilIsf_Handler); + + /* + * Initialize the write buffer for a pattern to write to the FLASH + * and the read buffer to zero so it can be verified after the read, + * the test value that is added to the unique value allows the value + * to be changed in a debug environment to guarantee + */ + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < PAGE_SIZE; + Count++, UniqueValue++) { + WriteBuffer[Count] = (u8)(UniqueValue + Test); + } + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + + Status = FlashErase(&Isf, TEST_ADDRESS, MAX_DATA); + if(Status != XST_SUCCESS){ + return XST_FAILURE; + } + + /* + * Write the data in the write buffer to the serial FLASH a page at a + * time, starting from TEST_ADDRESS + */ + for (Page = 0; Page < PAGE_COUNT; Page++) { + FlashWrite(&Isf, (Page * PAGE_SIZE) + TEST_ADDRESS, + PAGE_SIZE, XISF_WRITE); + } + + /* + * Read the contents of the FLASH from TEST_ADDRESS, using Normal Read + * command + */ + FlashRead(&Isf, TEST_ADDRESS, MAX_DATA, XISF_QUAD_OP_FAST_READ); + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + + BufferPtr = ReadBuffer; + + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA; + Count++, UniqueValue++) { + + if (BufferPtr[Count] != (u8)(UniqueValue + Test)) { + return XST_FAILURE; + } + } + + QspiDisableIntrSystem(IntcInstancePtr, QspiIntrId); + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* This function is the handler which performs processing for the QSPIPSU driver. +* It is called from an interrupt context such that the amount of processing +* performed should be minimized. It is called when a transfer of QSPI data +* completes or an error occurs. +* +* This handler provides an example of how to handle QSPIPSU interrupts but is +* application specific. +* +* @param CallBackRef is a reference passed to the handler. +* @param StatusEvent is the status of the QSPIPSU . +* @param ByteCount is the number of bytes transferred. +* +* @return None +* +* @note None. +* +******************************************************************************/ +void XilIsf_Handler(void *CallBackRef, u32 StatusEvent, unsigned int ByteCount) +{ + /* + * Indicate the transfer on the QSPI bus is no longer in progress + * regardless of the status event + */ + TransferInProgress = FALSE; + + /* + * If the event was not transfer done, then track it as an error + */ + if (StatusEvent != XST_SPI_TRANSFER_DONE) { + ErrorCount++; + } +} + +/*****************************************************************************/ +/** +* +* This function writes to the serial FLASH connected to the QSPIPSU interface. +* The FLASH contains a 256 byte write buffer which can be filled and then a +* write is automatically performed by the device. All the data put into the +* buffer must be in the same page of the device with page boundaries being on +* 256 byte boundaries. +* +* @param InstancePtr is a pointer to the XIsf component to use. +* @param Address contains the address to write data to in the FLASH. +* @param ByteCount contains the number of bytes to write. +* @param Command is the command used to write data to the flash. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +int FlashWrite(XIsf *InstancePtr, u32 Address, u32 ByteCount, u8 Command) +{ + XIsf_WriteParam WriteParam; + + int Status; + + WriteParam.Address = Address; + WriteParam.NumBytes = ByteCount; + WriteParam.WritePtr = WriteBuffer; + /* + * Perform the Write operation. + */ + TransferInProgress = TRUE; + Status = XIsf_Write(&Isf, Command, (void*) &WriteParam); + if(Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Wait till the Transfer is complete and check if there are any errors + * in the transaction. + */ + while(TransferInProgress); + if(ErrorCount != 0) { + return XST_FAILURE; + } + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* This function reads from the serial FLASH connected to the +* QSPIPSU interface. +* +* @param InstancePtr is a pointer to the XIsf component to use. +* @param Address contains the address to read data from in the FLASH. +* @param ByteCount contains the number of bytes to read. +* @param Command is the command used to read data from the flash. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +int FlashRead(XIsf *InstancePtr, u32 Address, u32 ByteCount, u8 Command) +{ + XIsf_ReadParam ReadParam; + int Status; + + /* + * Set the + * - Address in the Serial Flash where the data is to be read from. + * - Number of bytes to be read from the Serial Flash. + * - Read Buffer to which the data is to be read. + */ + ReadParam.Address = Address; + ReadParam.NumBytes = ByteCount; + ReadParam.ReadPtr = ReadBuffer; + + /* + * Perform the Read operation. + */ + TransferInProgress = TRUE; + Status = XIsf_Read(&Isf, Command, (void*) &ReadParam); + if(Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Wait till the Transfer is complete and check if there are any errors + * in the transaction. + */ + while(TransferInProgress); + if(ErrorCount != 0) { + return XST_FAILURE; + } + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* This function erases the sectors in the serial FLASH connected to the +* QSPIPSU interface. +* +* @param InstancePtr is a pointer to the XIsf component to use. +* @param Address contains the address of the first sector which needs to +* be erased. +* @param ByteCount contains the total size to be erased. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +int FlashErase(XIsf *InstancePtr, u32 Address, u32 ByteCount) +{ + int Status; + int Sector; + u32 NumSect; + u32 SectorSize; + u32 NumSectors; + u32 Sector_Mask; + + SectorSize = Isf.SectorSize; + NumSectors = Isf.NumSectors; + + /* Get the sector mask value */ + Sector_Mask = SectorMask(SectorSize); + + /* + * If erase size is same as the total size of the flash, use bulk erase + * command + */ + if (ByteCount == (NumSectors * SectorSize)) { + + /* + * Perform the Bulk Erase operation. + */ + TransferInProgress = TRUE; + Status = XIsf_Erase(InstancePtr, XISF_BULK_ERASE, Address); + if(Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Wait till the Transfer is complete and check if there + * are any errors in the transaction. + */ + while(TransferInProgress); + if(ErrorCount != 0) { + return XST_FAILURE; + } + + } + + /* + * Calculate no. of sectors to erase based on byte count + */ + NumSect = ByteCount/SectorSize + 1; + + /* + * If ByteCount to k sectors, + * but the address range spans from N to N+k+1 sectors, then + * increment no. of sectors to be erased + */ + + if( ((Address + ByteCount) & Sector_Mask) == + ((Address + (NumSect * SectorSize)) & + Sector_Mask) ) { + NumSect++; + } + + /* + * If the erase size is less than the total size of the flash, use + * sector erase command + */ + for (Sector = 0; Sector < NumSect; Sector++) { + + /* + * Perform the Bulk Erase operation. + */ + TransferInProgress = TRUE; + Status = XIsf_Erase(InstancePtr, XISF_SECTOR_ERASE, Address); + if(Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Wait till the Transfer is complete and check if there + * are any errors in the transaction. + */ + while(TransferInProgress); + if(ErrorCount != 0) { + return XST_FAILURE; + } + + Address += SectorSize; + } + return XST_SUCCESS; +} + + +/*****************************************************************************/ +/** +* +* This function setups the interrupt system for an Qspipsu device. +* +* @param IntcInstancePtr is a pointer to the instance of the Intc device. +* @param QspiInstancePtr is a pointer to the instance of the Qspipsu device. +* @param QspiIntrId is the interrupt Id for an QSPIPSU device. +* +* @return XST_SUCCESS if successful, otherwise XST_FAILURE. +* +* @note None. +* +******************************************************************************/ +static int QspiSetupIntrSystem(XScuGic *IntcInstancePtr, + XQspiPsu *QspiInstancePtr, u16 QspiIntrId) +{ + int Status; + + XScuGic_Config *IntcConfig; /* Instance of the interrupt controller */ + + Xil_ExceptionInit(); + + /* + * Initialize the interrupt controller driver so that it is ready to + * use. + */ + IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID); + if (NULL == IntcConfig) { + return XST_FAILURE; + } + + Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig, + IntcConfig->CpuBaseAddress); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Connect the interrupt controller interrupt handler to the hardware + * interrupt handling logic in the processor. + */ + Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, + (Xil_ExceptionHandler)XScuGic_InterruptHandler, + IntcInstancePtr); + + /* + * Connect the device driver handler that will be called when an + * interrupt for the device occurs, the handler defined above performs + * the specific interrupt processing for the device. + */ + Status = XScuGic_Connect(IntcInstancePtr, QspiIntrId, + (Xil_ExceptionHandler)XQspiPsu_InterruptHandler, + (void *)QspiInstancePtr); + if (Status != XST_SUCCESS) { + return Status; + } + + /* + * Enable the interrupt for the Qspi device. + */ + XScuGic_Enable(IntcInstancePtr, QspiIntrId); + + /* + * Enable interrupts in the Processor. + */ + Xil_ExceptionEnable(); + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* This function disables the interrupts that occur for the Qspipsu device. +* +* @param IntcInstancePtr is the pointer to an INTC instance. +* @param QspiIntrId is the interrupt Id for an QSPIPSU device. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +static void QspiDisableIntrSystem(XScuGic *IntcInstancePtr, u16 QspiIntrId) +{ + /* + * Disable the interrupt for the QSPI device. + */ + XScuGic_Disable(IntcInstancePtr, QspiIntrId); + + /* + * Disconnect and disable the interrupt for the Qspi device. + */ + XScuGic_Disconnect(IntcInstancePtr, QspiIntrId); +} + +/*****************************************************************************/ +/** +* +* This function calculates the sector mask based upon the sector size value +* +* +* @param SectorSize is the size of the sector of the flash +* available on the board. +* +* @return will return the sector mask after calculation. +* +* @note None. +* +******************************************************************************/ +u32 SectorMask(u32 SectorSize){ + + u32 Mask; + + switch(SectorSize){ + case 0x10000: + Mask = 0xFFFF0000; + break; + + case 0x20000: + Mask = 0xFFFE0000; + break; + + case 0x40000: + Mask = 0xFFFC0000; + break; + + case 0x80000: + Mask = 0xFFF80000; + break; + + default: + break; + } + + return Mask; +} diff --git a/lib/sw_services/xilisf/examples/xilisf_qspipsu_stm_polled_example.c b/lib/sw_services/xilisf/examples/xilisf_qspipsu_stm_polled_example.c new file mode 100644 index 00000000..82d5b5a4 --- /dev/null +++ b/lib/sw_services/xilisf/examples/xilisf_qspipsu_stm_polled_example.c @@ -0,0 +1,558 @@ +/****************************************************************************** +* +* Copyright (C) 2012 - 2015 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. +* +* Use of the Software is limited solely to applications: +* (a) running on a Xilinx device, or +* (b) that interact with a Xilinx device through a bus or interconnect. +* +* 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 xilisf_qspipsu_flash_polled_example.c +* +* +* This file contains a design example using the XILISF Library in +* interrupt mode with a serial FLASH device. This examples performs +* some transfers in Auto mode and Manual start mode, to illustrate the modes +* available. +* The hardware which this example runs on, must have a serial FLASH (Numonyx +* N25Q, Winbond W25Q, or Spansion S25FL) for it to run. This example has been +* tested with the Numonyx Serial Flash (N25Q128). +* +* @note +* +* None. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who Date     Changes
+* ----- --- -------- -----------------------------------------------
+* 5.4   sk  08/07/15 First Release.
+*
+*
+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xparameters.h" /* EDK generated parameters */ +#include "xscugic.h" /* Interrupt controller device driver */ +#include "xil_exception.h" +#include "xil_printf.h" +#include "xilisf.h" /* Serial Flash Library header file */ + +/************************** Constant Definitions *****************************/ +/** @name Device ID's + * + * @{ + */ +/* + * 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 QSPI_DEVICE_ID XPAR_XQSPIPSU_0_DEVICE_ID +#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID +#define QSPI_INTR_ID XPAR_XQSPIPS_0_INTR +/*@}*/ + +/** + * The following constants define the offsets within a FlashBuffer data + * type for each kind of data. Note that the read data offset is not the + * same as the write data because the QSPI driver is designed to allow full + * duplex transfers such that the number of bytes received is the number + * sent and received. + */ +#define DATA_OFFSET 5 /**< Data byte offset for fast, dual + and quad reads */ +#define DUMMY_SIZE 1 /**< Number of dummy bytes for fast, + dual and quad reads */ + +/** + * The following constants specify the page size and number of + * pages for the FLASH. The page size specifies a max number of + * bytes that can be written to the FLASH with a single transfer. + */ +#define NUM_PAGES 0x10000 /**< Number of Pages in the flash */ +#define PAGE_SIZE 256 /**< Page Size for Read/Write Operation */ + +/** + * Number of flash pages to be written. + */ +#define PAGE_COUNT 2 /**< Number of Pages for r/w Operation */ + +/* + * Max page size to initialize write and read buffer + */ +#define MAX_PAGE_SIZE 1024 + +/** + * Flash address to which data is ot be written. + */ +#define TEST_ADDRESS 0x1000000 /**< Test Address in the flash */ +#define UNIQUE_VALUE 0x08 /**< Unique Value for Test */ + + +/** + * The following constants specify the max amount of data and the size of the + * the buffer required to hold the data and overhead to transfer the data to + * and from the FLASH. + */ +#define MAX_DATA PAGE_COUNT * PAGE_SIZE /**< Max Data Calculated by + multiplying Page count and Page Size */ + +/** + * The following constant defines the slave select signal that is used to + * to select the FLASH device on the QSPI bus, this signal is typically + * connected to the chip select of the device + */ +#define FLASH_QSPI_SELECT 0x01 /**< Interrupt Mode Enable */ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +int FlashErase(XIsf *InstancePtr, u32 Address, u32 ByteCount); +int FlashWrite(XIsf *InstancePtr, u32 Address, u32 ByteCount, + u8 Command); +int FlashRead(XIsf *InstancePtr, u32 Address, u32 ByteCount, + u8 Command); +int QspiFlashPollExample(XScuGic *IntcInstancePtr, XQspiPsu *QspiInstancePtr, + u16 QspiDeviceId, u16 QspiIntrId); +static u32 SectorMask(u32 SectorSize); + +/************************** 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 they are zeroed. + */ +static XScuGic IntcInstance; +static XQspiPsu QspiInstance; +static XIsf Isf; +static XQspiPsu_Config *ConfigPtr; /**< Pointer to Configuration ROM data */ + +/** + * The following variables are shared between non-interrupt processing and + * interrupt processing such that they must be global. + */ +volatile int TransferInProgress; + +/** + * The following variable allows a test value to be added to the values that + * are written to the FLASH such that unique values can be generated to + * guarantee the writes to the FLASH were successful + */ +int Test_Polled = 7; + +/** + * The following variables are used to read and write to the eeprom and they + * are global to avoid having large buffers on the stack + */ +u8 ReadBuffer[(PAGE_COUNT * MAX_PAGE_SIZE) + (DATA_OFFSET + DUMMY_SIZE)*8] __attribute__ ((aligned(64))); +u8 WriteBuffer[(PAGE_COUNT * MAX_PAGE_SIZE) + DATA_OFFSET]; +u8 IsfWriteBuffer[PAGE_SIZE + XISF_CMD_SEND_EXTRA_BYTES];/**< IsfWrite Buffer + used in XilISF Initialization */ + +/*****************************************************************************/ +/** +* +* Main function to call the QSPI Flash example. +* +* @return XST_SUCCESS if successful, otherwise XST_FAILURE. +* +* @note None +* +******************************************************************************/ +int main(void) +{ + int Status; + + xil_printf("QSPIPSU FLASH Polling Example Test \r\n"); + + /* + * Run the Qspipsu Interrupt example. + */ + Status = QspiFlashPollExample(&IntcInstance, &QspiInstance, + QSPI_DEVICE_ID, QSPI_INTR_ID); + if (Status != XST_SUCCESS) { + xil_printf("QSPIPSU FLASH Polling Example Test Failed\r\n"); + return XST_FAILURE; + } + + xil_printf("Successfully ran QSPIPSU FLASH Polling Example Test\r\n"); + return XST_SUCCESS; +} + + +/****************************************************************************/ +/** +* The purpose of this function is to illustrate how to use the XQspiPsu +* device driver in interrupt mode. This function writes and reads data +* from a serial FLASH. +* +* @return XST_SUCCESS if successful else XST_FAILURE. +* +* @note +* +* This function calls other functions which contain loops that may be infinite +* if interrupts are not working such that it may not return. If the device +* slave select is not correct and the device is not responding on bus it will +* read a status of 0xFF for the status register as the bus is pulled up. +* +*****************************************************************************/ +int QspiFlashPollExample(XScuGic *IntcInstancePtr, XQspiPsu *QspiInstancePtr, + u16 QspiDeviceId, u16 QspiIntrId) +{ + u8 *BufferPtr; + u8 UniqueValue; + int Count; + int Page; + int Status; + u32 Options; + + /* + * Lookup the device configuration in the temporary CROM table. Use this + * configuration info down below when initializing this component. + */ + ConfigPtr = XQspiPsu_LookupConfig(QspiDeviceId); + if (ConfigPtr == NULL) { + return XST_DEVICE_NOT_FOUND; + } + + Status = XQspiPsu_CfgInitialize(QspiInstancePtr, ConfigPtr, + ConfigPtr->BaseAddress); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Set the QSPI options + */ + Options = XQSPIPSU_MANUAL_START_OPTION; + XIsf_SetSpiConfiguration(&Isf, QspiInstancePtr, Options, + XISF_SPI_PRESCALER); + + /* Initialize the XILISF Library */ + XIsf_Initialize(&Isf, QspiInstancePtr, FLASH_QSPI_SELECT, + IsfWriteBuffer); + + /* + * Initialize the write buffer for a pattern to write to the FLASH + * and the read buffer to zero so it can be verified after the read, + * the test value that is added to the unique value allows the value + * to be changed in a debug environment to guarantee + */ + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < PAGE_SIZE; + Count++, UniqueValue++) { + WriteBuffer[Count] = (u8)(UniqueValue + Test_Polled); + } + memset(ReadBuffer, 0x00, sizeof(ReadBuffer)); + + Status = FlashErase(&Isf, TEST_ADDRESS, MAX_DATA); + if(Status != XST_SUCCESS){ + return XST_FAILURE; + } + + /* + * Write the data in the write buffer to the serial FLASH a page at a + * time, starting from TEST_ADDRESS + */ + + for (Page = 0; Page < PAGE_COUNT; Page++) { + Status = FlashWrite(&Isf, + (Page * PAGE_SIZE) + TEST_ADDRESS, PAGE_SIZE, + XISF_WRITE); + if(Status != XST_SUCCESS){ + return XST_FAILURE; + } + } + + /****************************************************** + ******************QUAD OP FAST READ******************* + ******************************************************/ + + /* + * Read the contents of the FLASH from TEST_ADDRESS, using QUAD IO + * Fast Read command + */ + Status = FlashRead(&Isf, TEST_ADDRESS, MAX_DATA, + XISF_QUAD_OP_FAST_READ); + if(Status != XST_SUCCESS){ + return XST_FAILURE; + } + + /* + * Setup a pointer to the start of the data that was read into the read + * buffer and verify the data read is the data that was written + */ + + BufferPtr = ReadBuffer; + for (UniqueValue = UNIQUE_VALUE, Count = 0; Count < MAX_DATA; + Count++, UniqueValue++) { + if (BufferPtr[Count] != (u8)(UniqueValue + Test_Polled)) { + return XST_FAILURE; + } + } + + return XST_SUCCESS; +} + + + +/*****************************************************************************/ +/** +* +* This function writes to the serial FLASH connected to the QSPIPSU interface. +* The FLASH contains a 256 byte write buffer which can be filled and then a +* write is automatically performed by the device. All the data put into the +* buffer must be in the same page of the device with page boundaries being on +* 256 byte boundaries. +* +* @param InstancePtr is a pointer to the XIsf component to use. +* @param Address contains the address to write data to in the FLASH. +* @param ByteCount contains the number of bytes to write. +* @param Command is the command used to write data to the flash. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +int FlashWrite(XIsf *InstancePtr, u32 Address, u32 ByteCount, + u8 Command) +{ + XIsf_WriteParam WriteParam; + + int Status; + + WriteParam.Address = Address; + WriteParam.NumBytes = ByteCount; + WriteParam.WritePtr = WriteBuffer; + + /* + * Perform the Write operation. + */ + Status = XIsf_Write(&Isf, Command, (void*) &WriteParam); + if(Status != XST_SUCCESS) { + return XST_FAILURE; + } + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* This function reads from the serial FLASH connected to the +* QSPIPSU interface. +* +* @param InstancePtr is a pointer to the XIsf component to use. +* @param Address contains the address to read data from in the FLASH. +* @param ByteCount contains the number of bytes to read. +* @param Command is the command used to read data from the flash. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +int FlashRead(XIsf *InstancePtr, u32 Address, u32 ByteCount, u8 Command) +{ + XIsf_ReadParam ReadParam; + int Status; + + /* + * Set the + * - Address in the Serial Flash where the data is to be read from. + * - Number of bytes ReadParam.NumDummyBytesto be read from the Serial Flash. + * - Read Buffer to which the data is to be read. + */ + ReadParam.Address = Address; + ReadParam.NumBytes = ByteCount; + ReadParam.ReadPtr = ReadBuffer; + + /* + * Perform the Read operation. + */ + Status = XIsf_Read(&Isf, Command, (void*) &ReadParam); + if(Status != XST_SUCCESS) { + return XST_FAILURE; + } + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* This function erases the sectors in the serial FLASH connected to the +* QSPIPSU interface. +* +* @param InstancePtr is a pointer to the XIsf component to use. +* @param Address contains the address of the first sector which needs to +* be erased. +* @param ByteCount contains the total size to be erased. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +int FlashErase(XIsf *InstancePtr, u32 Address, u32 ByteCount) +{ + int Status; + int Sector; + u32 NumSect; + u32 SectorSize; + u32 NumSectors; + u32 Sector_Mask; + /* + * Get the value of Sector Size and Number of Sectors for the flash + */ + SectorSize = Isf.SectorSize; + NumSectors = Isf.NumSectors; + + /* Get the sector mask value */ + Sector_Mask = SectorMask(SectorSize); + + /* + * If erase size is same as the total size of the flash, use bulk erase + * command + */ + if (ByteCount == (NumSectors * SectorSize)) { + + if(InstancePtr->SpiInstPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_STACKED){ + XQspiPsu_SelectFlash(InstancePtr->SpiInstPtr, + XQSPIPSU_SELECT_FLASH_CS_LOWER, XQSPIPSU_SELECT_FLASH_BUS_LOWER); + } + + /* + * Call Bulk erase + */ + Status = XIsf_Erase(InstancePtr, + XISF_BULK_ERASE, Address); + if(Status != XST_SUCCESS) { + return XST_FAILURE; + } + + if(InstancePtr->SpiInstPtr->Config.ConnectionMode == XQSPIPSU_CONNECTION_MODE_STACKED){ + + XQspiPsu_SelectFlash(InstancePtr->SpiInstPtr, + XQSPIPSU_SELECT_FLASH_CS_UPPER, XQSPIPSU_SELECT_FLASH_BUS_LOWER); + + /* + * Call Bulk erase + */ + Status = XIsf_Erase(InstancePtr, + XISF_BULK_ERASE, Address); + if(Status != XST_SUCCESS) { + return XST_FAILURE; + } + } + return Status; + } + + /* + * Calculate no. of sectors to erase based on byte count + */ + NumSect = ByteCount/SectorSize + 1; + + /* + * If ByteCount to k sectors, + * but the address range spans from N to N+k+1 sectors, then + * increment no. of sectors to be erased + */ + + if( ((Address + ByteCount) & Sector_Mask) == + ((Address + (NumSect * SectorSize)) & + Sector_Mask) ) { + NumSect++; + } + + /* + * If the erase size is less than the total size of the flash, use + * sector erase command + */ + for (Sector = 0; Sector < NumSect; Sector++) { + + /* + * Perform the Sector Erase operation. + */ + Status = XIsf_Erase(InstancePtr, XISF_SECTOR_ERASE, Address); + if(Status != XST_SUCCESS) { + return XST_FAILURE; + } + + Address += SectorSize; + } + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* This function calculates the sector mask based upon the sector size value +* +* +* @param SectorSize is the size of the sector of the flash +* available on the board. +* +* @return will return the sector mask after calculation. +* +* @note None. +* +******************************************************************************/ +u32 SectorMask(u32 SectorSize){ + + u32 Mask; + + switch(SectorSize){ + case 0x10000: + Mask = 0xFFFF0000; + break; + + case 0x20000: + Mask = 0xFFFE0000; + break; + case 0x40000: + Mask = 0xFFFC0000; + break; + + case 0x80000: + Mask = 0xFFF80000; + break; + + default: + break; + } + + return Mask; +}