Files
Jungfraujoch/tools/xbflash.qspi/xqspips.cpp

1611 lines
47 KiB
C++

/**
* Copyright (C) 2016-2022 Xilinx, Inc
* Author(s) : Min Ma
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may
* not use this file except in compliance with the License. A copy of the
* License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#include <fstream>
#include <iostream>
#include <cassert>
#include <cstring>
#include <array>
#include "xqspips.h"
#include "unistd.h"
#ifdef WINDOWS
#define __func__ __FUNCTION__
#endif
#ifdef __GNUC__
# define XQSPIPS_UNUSED __attribute__((unused))
#endif
#define SAVE_FILE 0
#define FLASH_BASE 0x040000
#define FLASH_TYPE "qspi_ps_x2_single"
/*
* The following constants define the commands which may be sent to the Flash device.
*/
#define WRITE_STATUS_CMD 0x01
#define WRITE_CMD 0x02
#define READ_CMD 0x03
#define WRITE_DISABLE_CMD 0x04
#define READ_STATUS_CMD 0x05
#define WRITE_ENABLE_CMD 0x06
#define FAST_READ_CMD 0x0B
#define FAST_READ_CMD_4B 0x0C
#define WRITE_4B_CMD 0x12
#define READ_CMD_4B 0x13
#define BANK_REG_RD 0x16
#define BANK_REG_WR 0x17
#define EXIT_4B_ADDR_MODE_ISSI 0x29
#define QUAD_WRITE_CMD 0x32
#define READ_CONFIG_CMD 0x35
#define DUAL_READ_CMD 0x3B
#define DUAL_READ_CMD_4B 0x3C
#define VOLATILE_WRITE_ENABLE_CMD 0x50
#define QUAD_READ_CMD 0x6B
#define QUAD_READ_CMD_4B 0x6C
#define READ_FLAG_STATUS_CMD 0x70
#define READ_ID 0x9F
#define DUAL_WRITE_CMD 0xA2
#define ENTER_4B_ADDR_MODE 0xB7
#define DIE_ERASE_CMD 0xC4
/* Bank register is called Extended Address Register in Micron */
#define EXTADD_REG_WR 0xC5
#define BULK_ERASE_CMD 0xC7
#define EXTADD_REG_RD 0xC8
#define FOURKB_SUBSECTOR_ERASE_CMD 0x20
#define SEC_ERASE_CMD 0xD8
#define SEC_4B_ERASE_CMD 0xDC
#define EXIT_4B_ADDR_MODE 0xE9
#define IDCODE_READ_BYTES 6
#define WRITE_ENABLE_BYTES 1 /* Write Enable bytes */
#define BULK_ERASE_BYTES 1 /* Bulk erase extra bytes */
#define STATUS_READ_BYTES 2 /* Status read bytes count */
#define STATUS_WRITE_BYTES 2 /* Status write bytes count */
#define FLASH_SR_BUSY_MASK 0x01
#define FOURKB_SUBSECTOR_SIZE 0x1000
#define SECTOR_SIZE 0x10000
#define ENTER_4B 1
#define EXIT_4B 0
/* Registers offset */
#define GQSPI_CFG_OFFSET 0x100 /* GQSPI Configuration Register*/
#define GQSPI_ISR_OFFSET 0x104 /* GQSPI Status Register */
#define GQSPI_IER_OFFSET 0x108 /* GQSPI Interrupt Enable Register */
#define GQSPI_IDR_OFFSET 0x10C /* GQSPI Interrupt Disable Register */
#define GQSPI_IMR_OFFSET 0x110 /* GQSPI Interrupt Mask Register */
#define GQSPI_EN_OFFSET 0x114 /* GQSPI Enable Register */
#define GQSPI_TXD_OFFSET 0x11C /* GQSPI Transmit Data Register */
#define GQSPI_RXD_OFFSET 0x120 /* GQSPI Receive Data Register */
#define GQSPI_TX_THRESH_OFFSET 0x128 /* GQSPI TX FIFO Threshold Level Register */
#define GQSPI_RX_THRESH_OFFSET 0x12C /* GQSPI RX FIFO Threshold Level Register */
#define GQSPI_GPIO_OFFSET 0x130 /* GQSPI GPIO for Write Protect Register */
#define GQSPI_LPBK_DLY_ADJ_OFFSET 0x138 /* GQSPI Lookback clock delay adjustment Register */
#define GQSPI_GEN_FIFO_OFFSET 0x140 /* GQSPI Generic FIFO Configuration Register */
#define GQSPI_SEL_OFFSET 0x144 /* GQSPI Select Register */
#define GQSPI_FIFO_CTRL_OFFSET 0x14C /* GQSPI FIFO Control Register */
#define GQSPI_GF_THRESH_OFFSET 0x150 /* GQSPI Generic FIFO Threshold Level Register */
#define GQSPI_POLL_CFG_OFFSET 0x154 /* GQSPI Poll Configuration Register */
#define GQSPI_P_TIMEOUT_OFFSET 0x158 /* GQSPI Poll Time Out Register */
#define GQSPI_DATA_DLY_ADJ_OFFSET 0x1F8 /* GQSPI Rx Data Delay Register */
#define GQSPI_MOD_ID_OFFSET 0x1FC /* GQSPI Module Identification Register */
/* Register constants/masks */
#define XQSPIPSU_CFG_MODE_EN_MASK 0XC0000000U
#define XQSPIPSU_CFG_GEN_FIFO_START_MODE_MASK 0X20000000U
#define XQSPIPSU_CFG_START_GEN_FIFO_MASK 0X10000000U
#define XQSPIPSU_CFG_ENDIAN_MASK 0X04000000U
#define XQSPIPSU_CFG_EN_POLL_TO_MASK 0X00100000U
#define XQSPIPSU_CFG_WP_HOLD_MASK 0X00080000U
#define XQSPIPSU_CFG_BAUD_RATE_DIV_MASK 0X00000038U
#define XQSPIPSU_CFG_CLK_PHA_MASK 0X00000004U
#define XQSPIPSU_CFG_CLK_POL_MASK 0X00000002U
#define XQSPIPSU_GENFIFO_IMM_DATA_MASK 0x000FFU
#define XQSPIPSU_GENFIFO_DATA_XFER 0x00100U
#define XQSPIPSU_GENFIFO_EXP 0x00200U
#define XQSPIPSU_GENFIFO_EXP_START 0x100U
#define XQSPIPSU_GENFIFO_MODE_MASK 0x00C00U /* And with ~MASK first */
#define XQSPIPSU_GENFIFO_BUS_MASK 0x0C000U /* And with ~MASK first */
#define XQSPIPSU_GENFIFO_TX 0x10000U /* inverse is zero pump */
#define XQSPIPSU_GENFIFO_RX 0x20000U /* inverse is RX discard */
#define XQSPIPSU_GENFIFO_STRIPE 0x40000U
#define XQSPIPSU_GENFIFO_POLL 0x80000U
#define XQSPIPSU_ISR_WR_TO_CLR_MASK 0X00000002U
#define XQSPIPSU_ISR_POLL_TIME_EXPIRE_MASK 0X00000002U
#define XQSPIPSU_ISR_TXNOT_FULL_MASK 0X00000004U
#define XQSPIPSU_ISR_TXFULL_MASK 0X00000008U
#define XQSPIPSU_ISR_RXNEMPTY_MASK 0X00000010U
#define XQSPIPSU_ISR_RXFULL_MASK 0X00000020U
#define XQSPIPSU_ISR_GENFIFOEMPTY_MASK 0X00000080U
#define XQSPIPSU_ISR_TXEMPTY_MASK 0X00000100U
#define XQSPIPSU_ISR_GENFIFOFULL_MASK 0X00000400U
#define XQSPIPSU_ISR_RXEMPTY_MASK 0X00000800U
#define XQSPIPSU_IDR_ALL_MASK 0X00000FBEU
#define XQSPIPSU_FIFO_CTRL_RST_GEN_FIFO_MASK 0X00000001U
#define XQSPIPSU_FIFO_CTRL_RST_TX_FIFO_MASK 0X00000002U
#define XQSPIPSU_FIFO_CTRL_RST_RX_FIFO_MASK 0X00000004U
#define XQSPIPSU_LPBK_DLY_ADJ_USE_LPBK_MASK 0X00000020U
#define CFG_BAUD_RATE_DIV_2 0X00000000U
#define CFG_BAUD_RATE_DIV_4 0X00000008U
#define CFG_BAUD_RATE_DIV_8 0X00000010U
#define CFG_BAUD_RATE_DIV_16 0X00000018U
#define CFG_BAUD_RATE_DIV_32 0X00000020U
#define CFG_BAUD_RATE_DIV_64 0X00000028U
#define CFG_BAUD_RATE_DIV_128 0X00000030U
#define CFG_BAUD_RATE_DIV_256 0X00000038U
#define XQSPIPSU_GENFIFO_CS_LOWER 0x01000U
#define XQSPIPSU_GENFIFO_CS_UPPER 0x02000U
#define XQSPIPSU_GENFIFO_CS_BOTH 0x03000U /* inverse is reserved */
#define XQSPIPSU_GENFIFO_BUS_LOWER 0x04000U
#define XQSPIPSU_GENFIFO_BUS_UPPER 0x08000U
#define XQSPIPSU_GENFIFO_BUS_BOTH 0x0C000U /* inverse is no bus */
#define XQSPIPSU_GENFIFO_MODE_SPI 0x00400U
#define XQSPIPSU_GENFIFO_MODE_DUALSPI 0x00800U
#define XQSPIPSU_GENFIFO_MODE_QUADSPI 0x00C00U
#define XQSPIPSU_GENFIFO_CS_SETUP 0x05U
#define XQSPIPSU_GENFIFO_CS_HOLD 0x04U
#define XQSPIPSU_TX_FIFO_THRESHOLD_RESET_VAL 0X00000001U
#define XQSPIPSU_RX_FIFO_THRESHOLD_RESET_VAL 0X00000001U
#define XQSPIPSU_GEN_FIFO_THRESHOLD_RESET_VAL 0X00000010U
#define XQSPIPSU_TXD_DEPTH 64
// JEDEC vendor IDs
#define MICRON_VENDOR_ID 0x20
#define MACRONIX_VENDOR_ID 0xC2
#define XQSpiPS_ReadReg(RegOffset) readReg(RegOffset)
#define XQSpiPS_WriteReg(RegOffset, Value) writeReg(RegOffset, Value)
#define XQSpiPS_GetConfigReg() XQSpiPS_ReadReg(GQSPI_CFG_OFFSET)
#define XQSpiPS_SetConfigReg(mask) XQSpiPS_WriteReg(GQSPI_CFG_OFFSET, mask)
#define XQSpiPS_GetStatusReg() XQSpiPS_ReadReg(GQSPI_ISR_OFFSET)
#define XQSpiPS_SetStatusReg(mask) XQSpiPS_WriteReg(GQSPI_ISR_OFFSET, mask)
#define XQSpiPS_Enable_GQSPI() XQSpiPS_WriteReg(GQSPI_EN_OFFSET, 0x1)
#define XQSpiPS_Disable_GQSPI() XQSpiPS_WriteReg(GQSPI_EN_OFFSET, 0x0)
#define XQSpiPS_Sel_GQSPI() XQSpiPS_WriteReg(GQSPI_SEL_OFFSET, 0x1)
#define is_GQSPI_Enable() XQSpiPS_ReadReg(GQSPI_EN_OFFSET)
#define is_GQSPI_Mode() XQSpiPS_ReadReg(GQSPI_SEL_OFFSET)
#define XQSPIPSU_MSG_FLAG_STRIPE 0x1U
#define XQSPIPSU_MSG_FLAG_RX 0x2U
#define XQSPIPSU_MSG_FLAG_TX 0x4U
#define XQSPIPSU_SELECT_MODE_SPI 0x1U
#define XQSPIPSU_SELECT_MODE_DUALSPI 0x2U
#define XQSPIPSU_SELECT_MODE_QUADSPI 0x4U
#define printHEX(RegName, RegValue) \
do { \
std::cout << RegName " 0x" << std::hex << RegValue << std::dec << std::endl; \
} while(0);
static bool TEST_MODE = false;
static std::array<int,2> flashVendors = {
MICRON_VENDOR_ID,
MACRONIX_VENDOR_ID
};
static int flashVendor = -1;
/**
* @brief XQSPIPS_Flasher::XQSPIPS_Flasher
*
* - Bring mgmt mapping from Flasher object
*/
XQSPIPS_Flasher::XQSPIPS_Flasher(pcidev::pci_device *dev)
{
std::string err;
std::string typeStr;
std::string baroffStr = "";
mDev = dev;
mTxBytes = 0;
mRxBytes = 0;
flash_base = dev->get_flash_offset();
if (flash_base == INVALID_OFFSET)
flash_base = FLASH_BASE;
typeStr = dev->get_flash_type();
if (typeStr.empty())
typeStr = FLASH_TYPE;
// By default, it is 'perallel'
mConnectMode = 0;
if (typeStr.find("single") != std::string::npos) {
mConnectMode = 1;
}
mBusWidth = 2;
}
/**
* @brief XQSPIPS_Flasher::~XQSPIPS_Flasher
*
* - munmap
* - delete file descriptor
*/
XQSPIPS_Flasher::~XQSPIPS_Flasher()
{
}
void XQSPIPS_Flasher::clearReadBuffer(unsigned size)
{
for (unsigned i = 0; i < size; i++) {
mReadBuffer[i] = 0;
}
}
void XQSPIPS_Flasher::clearWriteBuffer(unsigned size)
{
for (unsigned i = 0; i < size; i++) {
mWriteBuffer[i] = 0;
}
}
void XQSPIPS_Flasher::clearBuffers(unsigned size)
{
clearReadBuffer(PAGE_SIZE);
clearWriteBuffer(PAGE_SIZE);
}
uint32_t XQSPIPS_Flasher::readReg(unsigned RegOffset)
{
unsigned value;
int status = mDev->pcieBarRead(flash_base + RegOffset, &value, 4);
if(status != 0) {
assert(0);
std::cout << "read reg ERROR" << std::endl;
}
//std::cout << "Read 0x" << std::hex << RegOffset
// << ": 0x" << value << std::dec << std::endl;
return value;
}
int XQSPIPS_Flasher::writeReg(unsigned RegOffset, unsigned value)
{
int status = mDev->pcieBarWrite(flash_base + RegOffset, &value, 4);
if(status != 0) {
assert(0);
std::cout << "write reg ERROR " << std::endl;
}
//std::cout << "Write 0x" << std::hex << RegOffset
// << ": 0x" << value << std::dec << std::endl;
return 0;
}
uint32_t XQSPIPS_Flasher::selectSpiMode(uint8_t SpiMode)
{
uint32_t mask;
switch (SpiMode) {
case XQSPIPSU_SELECT_MODE_SPI:
mask = XQSPIPSU_GENFIFO_MODE_SPI;
break;
case XQSPIPSU_SELECT_MODE_DUALSPI:
mask = XQSPIPSU_GENFIFO_MODE_DUALSPI;
break;
case XQSPIPSU_SELECT_MODE_QUADSPI:
mask = XQSPIPSU_GENFIFO_MODE_QUADSPI;
break;
default:
mask = XQSPIPSU_GENFIFO_MODE_SPI;
}
#if defined(_DEBUG)
printHEX("SPI Mode is:", (unsigned)SpiMode);
#endif
return mask;
}
bool XQSPIPS_Flasher::waitGenFifoEmpty()
{
long long delay = 0;
const timespec req = {0, 5000};
while (delay < 30000000000) {
uint32_t StatusReg = XQSpiPS_GetStatusReg();
if (StatusReg & XQSPIPSU_ISR_GENFIFOEMPTY_MASK) {
return true;
}
#if defined(_DEBUG)
printHEX("Gen FIFO Not Empty", StatusReg);
#endif
nanosleep(&req, 0);
delay += 5000;
}
std::cout << "Unable to get Gen FIFO Empty" << std::endl;
return false;
}
bool XQSPIPS_Flasher::waitTxEmpty()
{
long long delay = 0;
const timespec req = {0, 5000};
while (delay < 30000000000) {
uint32_t StatusReg = XQSpiPS_GetStatusReg();
if (StatusReg & XQSPIPSU_ISR_TXEMPTY_MASK) {
return true;
}
#if defined(_DEBUG)
printHEX("TXD Not Empty", StatusReg);
#endif
nanosleep(&req, 0);
delay += 5000;
}
std::cout << "Unable to get Tx Empty" << std::endl;
return false;
}
void XQSPIPS_Flasher::program(std::istream& binStream, unsigned base)
{
unsigned total_size = 0;
unsigned remain = 0;
unsigned pages = 0;
unsigned addr = 0;
unsigned size = 0;
int beatCount = 0;
binStream.seekg(0, binStream.end);
total_size = binStream.tellg();
binStream.seekg(0, binStream.beg);
pages = total_size / PAGE_SIZE;
remain = total_size % PAGE_SIZE;
#if defined(_DEBUG)
std::cout << "Verify earse flash" << std::endl;
int mismatched = 0;
for (unsigned page = 0; page <= pages; page++) {
addr = page * PAGE_SIZE;
if (page != pages)
size = PAGE_SIZE;
else
size = remain;
readFlash(base + addr, size);
for (unsigned i = 0; i < size; i++) {
if (0xFF != mReadBuffer[i]) {
mismatched = 1;
}
}
if (mismatched) {
std::cout << "Erase failed at page " << page << std::endl;
mismatched = 0;
}
}
#endif
std::cout << "Programming flash" << std::flush;
beatCount = 0;
for (unsigned page = 0; page <= pages; page++) {
beatCount++;
if (beatCount % 4000 == 0) {
std::cout << "." << std::flush;
}
addr = page * PAGE_SIZE;
if (page != pages)
size = PAGE_SIZE;
else
size = remain;
binStream.read((char *)mWriteBuffer, size);
writeFlash(base + addr, size);
}
std::cout << std::endl;
}
int XQSPIPS_Flasher::verify(std::istream& binStream, unsigned base, bool quiet)
{
unsigned total_size = 0;
unsigned remain = 0;
unsigned pages = 0;
unsigned addr = 0;
unsigned size = 0;
int beatCount = 0;
int mismatched = 0;
binStream.seekg(0, binStream.end);
total_size = binStream.tellg();
binStream.seekg(0, binStream.beg);
#if SAVE_FILE
std::ofstream of_flash;
of_flash.open("/tmp/BOOT.BIN", std::ofstream::out);
if (!of_flash.is_open()) {
std::cout << "Could not open /tmp/BOOT.BIN" << std::endl;
return false;
}
#endif
remain = total_size % PAGE_SIZE;
pages = total_size / PAGE_SIZE;
if (!quiet)
std::cout << "Verifying" << std::flush;
beatCount = 0;
for (unsigned page = 0; page <= pages; page++) {
beatCount++;
if (beatCount % 4000 == 0 && !quiet) {
std::cout << "." << std::flush;
}
addr = page * PAGE_SIZE;
if (page != pages)
size = PAGE_SIZE;
else
size = remain;
binStream.read((char *)mWriteBuffer, size);
readFlash(base + addr, size);
mismatched = 0;
for (unsigned i = 0; i < size; i++) {
#if SAVE_FILE
of_flash << mReadBuffer[i];
#endif
if (mWriteBuffer[i] != mReadBuffer[i]) {
mismatched = 1;
break;
}
}
if (mismatched) {
std::cout << "Find mismatch at page " << page << std::endl;
break;
}
}
std::cout << std::endl;
#if SAVE_FILE
of_flash.close();
#endif
return mismatched;
}
int XQSPIPS_Flasher::xclReadBack(std::string output, unsigned base, unsigned total_size)
{
unsigned remain = 0;
unsigned pages = 0;
unsigned addr = 0;
unsigned size = 0;
int beatCount = 0;
initQSpiPS();
uint32_t StatusReg = XQSpiPS_GetStatusReg();
if (StatusReg == 0xFFFFFFFF) {
std::cout << "[ERROR]: Read PCIe device return -1. Cannot get QSPI status." << std::endl;
exit(-EOPNOTSUPP);
}
/* Make sure it is ready to receive commands. */
resetQSpiPS();
XQSpiPS_Enable_GQSPI();
if (!getFlashID()) {
std::cout << "[ERROR]: Could not get Flash ID" << std::endl;
exit(-EOPNOTSUPP);
}
std::ofstream of_flash;
of_flash.open(output, std::ofstream::out);
if (!of_flash.is_open()) {
std::cout << "Could not open " << output << std::endl;
return false;
}
if (!total_size)
total_size = FLASH_SIZE;
if (base + total_size > FLASH_SIZE) {
std::cout << "[ERROR]: Invalid argument" << std::endl;
exit(-EINVAL);
}
remain = total_size % PAGE_SIZE;
pages = total_size / PAGE_SIZE;
beatCount = 0;
for (unsigned page = 0; page <= pages; page++) {
beatCount++;
if (beatCount % 4000 == 0) {
std::cout << "." << std::flush;
}
addr = page * PAGE_SIZE;
if (page != pages)
size = PAGE_SIZE;
else
size = remain;
readFlash(base + addr, size);
for (unsigned i = 0; i < size; i++) {
of_flash << mReadBuffer[i];
}
}
std::cout << std::endl;
of_flash.close();
return 0;
}
int XQSPIPS_Flasher::revertToMFG(std::istream& binStream)
{
initQSpiPS();
uint32_t StatusReg = XQSpiPS_GetStatusReg();
if (StatusReg == 0xFFFFFFFF) {
std::cout << "[ERROR]: Read PCIe device return -1. Cannot get QSPI status." << std::endl;
exit(-EOPNOTSUPP);
}
/* Make sure it is ready to receive commands. */
resetQSpiPS();
XQSpiPS_Enable_GQSPI();
if (!getFlashID()) {
std::cout << "[ERROR]: Could not get Flash ID" << std::endl;
exit(-EOPNOTSUPP);
}
/* Use 4 bytes address mode */
enterOrExitFourBytesMode(ENTER_4B);
if (verify(binStream, GOLDEN_BASE, true)) {
std::cout << "[ERROR]: Doesn't find valid golden on flash !!" << std::endl;
return -ECANCELED;
}
std::cout << "Golden detected at 96MB " << std::endl;
// Sectoer size is defined by SECTOR_SIZE
std::cout << "Factory resetting " << std::flush;
eraseSector(0, GOLDEN_BASE);
std::cout << std::endl;
return 0;
}
int XQSPIPS_Flasher::xclUpgradeFirmware(std::istream& binStream, unsigned offset)
{
unsigned total_size = 0;
binStream.seekg(0, binStream.end);
total_size = binStream.tellg();
binStream.seekg(0, binStream.beg);
std::cout << "INFO: ***BOOT.BIN has " << total_size << " bytes" << std::endl;
if (total_size + offset > FLASH_SIZE) {
std::cout << "[ERROR]: Invalid argument" << std::endl;
exit(-EINVAL);
}
/* Test only */
//if (xclTestXQSpiPS(0))
// return -1;
//else
// return 0;
initQSpiPS();
uint32_t StatusReg = XQSpiPS_GetStatusReg();
if (StatusReg == 0xFFFFFFFF) {
std::cout << "[ERROR]: Read PCIe device return -1. Cannot get QSPI status." << std::endl;
exit(-EOPNOTSUPP);
}
/* Make sure it is ready to receive commands. */
resetQSpiPS();
XQSpiPS_Enable_GQSPI();
if (!getFlashID()) {
std::cout << "[ERROR]: Could not get Flash ID" << std::endl;
exit(-EOPNOTSUPP);
}
/* Use 4 bytes address mode */
enterOrExitFourBytesMode(ENTER_4B);
// Sectoer size is defined by SECTOR_SIZE
std::cout << "Erasing flash" << std::flush;
eraseSector(offset, offset >= GOLDEN_BASE ? FLASH_SIZE - offset : GOLDEN_BASE - offset);
//eraseBulk();
std::cout << std::endl;
program(binStream, offset);
int ret = verify(binStream, offset);
enterOrExitFourBytesMode(EXIT_4B);
return ret;
}
int XQSPIPS_Flasher::xclErase(unsigned offset, unsigned total_size)
{
initQSpiPS();
uint32_t StatusReg = XQSpiPS_GetStatusReg();
if (StatusReg == 0xFFFFFFFF) {
std::cout << "[ERROR]: Read PCIe device return -1. Cannot get QSPI status." << std::endl;
exit(-EOPNOTSUPP);
}
/* Make sure it is ready to receive commands. */
resetQSpiPS();
XQSpiPS_Enable_GQSPI();
if (!getFlashID()) {
std::cout << "[ERROR]: Could not get Flash ID" << std::endl;
exit(-EOPNOTSUPP);
}
/* Use 4 bytes address mode */
enterOrExitFourBytesMode(ENTER_4B);
// Sectoer size is defined by SECTOR_SIZE
std::cout << "Erasing flash..." << std::flush;
eraseSector(offset, total_size);
//eraseBulk();
std::cout << std::endl;
enterOrExitFourBytesMode(EXIT_4B);
return 0;
}
void XQSPIPS_Flasher::initQSpiPS()
{
/* Should Select GQSPI mode */
if (!is_GQSPI_Mode()) {
std::cout << "Not support LQSPI mode, switch to GQSPI mode" << std::endl;
XQSpiPS_Sel_GQSPI();
}
/* Disable GQSPI */
XQSpiPS_Disable_GQSPI();
if (TEST_MODE)
std::cout << "Initialize GQSPI done" << std::endl;
}
void XQSPIPS_Flasher::resetQSpiPS()
{
uint32_t ConfigReg;
abortQSpiPS();
/* Initial Configure register target value is 0x00080010 */
ConfigReg = XQSpiPS_GetConfigReg();
ConfigReg &= ~XQSPIPSU_CFG_MODE_EN_MASK; /* IO mode */
ConfigReg &= ~XQSPIPSU_CFG_GEN_FIFO_START_MODE_MASK; /* Auto start */
//ConfigReg |= XQSPIPSU_CFG_GEN_FIFO_START_MODE_MASK; /* Manual start */
ConfigReg &= ~XQSPIPSU_CFG_ENDIAN_MASK; /* Little endain by default */
ConfigReg &= ~XQSPIPSU_CFG_EN_POLL_TO_MASK; /* Disable poll timeout */
ConfigReg |= XQSPIPSU_CFG_WP_HOLD_MASK; /* Set hold bit */
ConfigReg &= ~XQSPIPSU_CFG_BAUD_RATE_DIV_MASK; /* Clear prescalar by default */
ConfigReg |= CFG_BAUD_RATE_DIV_8; /* Divide by 8 */
ConfigReg &= ~XQSPIPSU_CFG_CLK_PHA_MASK; /* CPHA 0 */
ConfigReg &= ~XQSPIPSU_CFG_CLK_POL_MASK; /* CPOL 0 */
XQSpiPS_SetConfigReg(ConfigReg);
//XQSpiPS_WriteReg(GQSPI_LPBK_DLY_ADJ_OFFSET, XQSPIPSU_LPBK_DLY_ADJ_USE_LPBK_MASK);
XQSpiPS_WriteReg(GQSPI_TX_THRESH_OFFSET, XQSPIPSU_TX_FIFO_THRESHOLD_RESET_VAL);
XQSpiPS_WriteReg(GQSPI_RX_THRESH_OFFSET, XQSPIPSU_RX_FIFO_THRESHOLD_RESET_VAL);
XQSpiPS_WriteReg(GQSPI_GF_THRESH_OFFSET, XQSPIPSU_GEN_FIFO_THRESHOLD_RESET_VAL);
if (TEST_MODE) {
printHEX("CFG Reg:", ConfigReg);
printHEX("TX Thresh Reg:", XQSpiPS_ReadReg(GQSPI_TX_THRESH_OFFSET));
printHEX("RX Thresh Reg:", XQSpiPS_ReadReg(GQSPI_RX_THRESH_OFFSET));
printHEX("GF Thresh Reg:", XQSpiPS_ReadReg(GQSPI_GF_THRESH_OFFSET));
std::cout << "Reset GQSPI done" << std::endl;
}
}
void XQSPIPS_Flasher::abortQSpiPS()
{
uint32_t StatusReg = XQSpiPS_GetStatusReg();
uint32_t ConfigReg = XQSpiPS_GetConfigReg();
/* Clear and diable interrupts (Ignore DMA register) */
XQSpiPS_WriteReg(GQSPI_ISR_OFFSET, StatusReg | XQSPIPSU_ISR_WR_TO_CLR_MASK);
XQSpiPS_WriteReg(GQSPI_IDR_OFFSET, XQSPIPSU_IDR_ALL_MASK);
/* Clear FIFO */
if (XQSpiPS_GetStatusReg() & XQSPIPSU_ISR_RXEMPTY_MASK) {
XQSpiPS_WriteReg(GQSPI_FIFO_CTRL_OFFSET, XQSPIPSU_FIFO_CTRL_RST_TX_FIFO_MASK | XQSPIPSU_FIFO_CTRL_RST_GEN_FIFO_MASK);
}
if (StatusReg & XQSPIPSU_ISR_RXEMPTY_MASK) {
/* Switch to IO mode to clear RX FIFO */
ConfigReg &= ~XQSPIPSU_CFG_MODE_EN_MASK; /* IO mode */
XQSpiPS_SetConfigReg(ConfigReg);
XQSpiPS_WriteReg(GQSPI_FIFO_CTRL_OFFSET, XQSPIPSU_FIFO_CTRL_RST_RX_FIFO_MASK);
}
/* Disable GQSPI */
XQSpiPS_Disable_GQSPI();
if (TEST_MODE)
std::cout << "Abort QSPI done" << std::endl;
}
void XQSPIPS_Flasher::readRxFifo(xqspips_msg_t *msg, int32_t Size)
{
int32_t Count = 0;
uint32_t Data = 0;
assert(msg != NULL);
while (mRxBytes != 0 && (Count < Size)) {
Data = XQSpiPS_ReadReg(GQSPI_RXD_OFFSET);
#if defined(_DEBUG)
printHEX("RX Data:", Data);
#endif
if (mRxBytes >= 4) {
memcpy(msg->bufPtr, &Data, 4);
msg->bufPtr += 4;
mRxBytes -= 4;
Count += 4;
} else {
/* less than 4 bytes */
memcpy(msg->bufPtr, &Data, mRxBytes);
msg->bufPtr += mRxBytes;
Count += mRxBytes;
mRxBytes = 0;
}
}
}
void XQSPIPS_Flasher::fillTxFifo(xqspips_msg_t *msg, int32_t Size)
{
int32_t Count = 0;
uint32_t Data = 0;
assert(msg != NULL);
while ((mTxBytes > 0) && (Count < Size)) {
if (mTxBytes >= 4) {
memcpy(&Data, msg->bufPtr, 4);
msg->bufPtr += 4;
Count += 4;
mTxBytes -= 4;
} else {
/* less than 4 bytes */
memcpy(&Data, msg->bufPtr, mTxBytes);
msg->bufPtr += mTxBytes;
Count += mTxBytes;
mTxBytes = 0;
}
XQSpiPS_WriteReg(GQSPI_TXD_OFFSET, Data);
#if defined(_DEBUG)
printHEX("TX Data:", Data);
#endif
}
#if defined(_DEBUG)
std::cout << "Fill Tx FIFO " << Count << " Bytes." << std::endl;
#endif
}
void XQSPIPS_Flasher::setupTXRX(xqspips_msg_t *msg, uint32_t *GenFifoEntry)
{
/* Transmit */
if (msg->flags & XQSPIPSU_MSG_FLAG_TX) {
*GenFifoEntry |= XQSPIPSU_GENFIFO_DATA_XFER;
*GenFifoEntry |= XQSPIPSU_GENFIFO_TX;
*GenFifoEntry &= ~XQSPIPSU_GENFIFO_RX;
mTxBytes = msg->byteCount;
mRxBytes = 0;
fillTxFifo(msg, XQSPIPSU_TXD_DEPTH);
return;
}
/* Receive */
if (msg->flags & XQSPIPSU_MSG_FLAG_RX) {
*GenFifoEntry &= ~XQSPIPSU_GENFIFO_TX;
*GenFifoEntry |= XQSPIPSU_GENFIFO_DATA_XFER;
*GenFifoEntry |= XQSPIPSU_GENFIFO_RX;
mRxBytes = msg->byteCount;
/* Support Rx DMA? */
return;
}
/* dummy */
if (!(msg->flags & XQSPIPSU_GENFIFO_RX) && !(msg->flags & XQSPIPSU_GENFIFO_TX)) {
*GenFifoEntry |= XQSPIPSU_GENFIFO_DATA_XFER;
*GenFifoEntry &= ~(XQSPIPSU_GENFIFO_TX | XQSPIPSU_GENFIFO_RX);
return;
}
}
void XQSPIPS_Flasher::sendGenFifoEntryCSAssert()
{
uint32_t GenFifoEntry = 0x0U;
GenFifoEntry &= ~(XQSPIPSU_GENFIFO_DATA_XFER | XQSPIPSU_GENFIFO_EXP);
GenFifoEntry &= ~XQSPIPSU_GENFIFO_MODE_MASK;
GenFifoEntry |= XQSPIPSU_GENFIFO_MODE_SPI;
/* By default, use upper and lower CS and Bus */
GenFifoEntry &= ~XQSPIPSU_GENFIFO_BUS_MASK;
if (mConnectMode == 0) {
GenFifoEntry |= XQSPIPSU_GENFIFO_BUS_BOTH;
GenFifoEntry |= XQSPIPSU_GENFIFO_CS_BOTH;
} else {
GenFifoEntry |= XQSPIPSU_GENFIFO_BUS_LOWER;
GenFifoEntry |= XQSPIPSU_GENFIFO_CS_LOWER;
}
GenFifoEntry &= ~(XQSPIPSU_GENFIFO_TX | XQSPIPSU_GENFIFO_RX |
XQSPIPSU_GENFIFO_STRIPE | XQSPIPSU_GENFIFO_POLL);
GenFifoEntry |= XQSPIPSU_GENFIFO_CS_SETUP;
/* Write GEN FIFO */
XQSpiPS_WriteReg(GQSPI_GEN_FIFO_OFFSET, GenFifoEntry);
#if defined(_DEBUG)
printHEX("Assert CS: expectd 0xf405, got", GenFifoEntry);
#endif
}
void XQSPIPS_Flasher::sendGenFifoEntryData(xqspips_msg_t *msg)
{
uint32_t GenFifoEntry = 0x0U;
uint32_t tmpCount = 0;
/* Mode SPI/Dual/Quad */
GenFifoEntry &= ~XQSPIPSU_GENFIFO_MODE_MASK;
GenFifoEntry |= selectSpiMode(msg->busWidth);
/* By default, use upper and lower CS and Bus */
GenFifoEntry &= ~XQSPIPSU_GENFIFO_BUS_MASK;
if (mConnectMode == 0) {
GenFifoEntry |= XQSPIPSU_GENFIFO_BUS_BOTH;
GenFifoEntry |= XQSPIPSU_GENFIFO_CS_BOTH;
} else {
GenFifoEntry |= XQSPIPSU_GENFIFO_BUS_LOWER;
GenFifoEntry |= XQSPIPSU_GENFIFO_CS_LOWER;
}
/* Stripe */
if (msg->flags & XQSPIPSU_MSG_FLAG_STRIPE) {
GenFifoEntry |= XQSPIPSU_GENFIFO_STRIPE;
} else {
GenFifoEntry &= ~XQSPIPSU_GENFIFO_STRIPE;
}
/* Do transfer in IO mode */
XQSpiPS_SetConfigReg(XQSpiPS_GetConfigReg() & ~XQSPIPSU_CFG_MODE_EN_MASK);
setupTXRX(msg, &GenFifoEntry);
if (msg->byteCount < XQSPIPSU_GENFIFO_IMM_DATA_MASK) {
GenFifoEntry &= ~XQSPIPSU_GENFIFO_IMM_DATA_MASK;
GenFifoEntry |= msg->byteCount;
#if defined(_DEBUG)
printHEX("GenFifo data:", GenFifoEntry);
#endif
XQSpiPS_WriteReg(GQSPI_GEN_FIFO_OFFSET, GenFifoEntry);
} else {
/* Exponent entries */
tmpCount = msg->byteCount;
uint8_t exponent = 8;
uint8_t immData = tmpCount & 0xFFU;
GenFifoEntry |= XQSPIPSU_GENFIFO_EXP;
while (tmpCount != 0x0U) {
/* only support 1, 2, 4, 8... pages*/
if (tmpCount & XQSPIPSU_GENFIFO_EXP_START) {
GenFifoEntry &= ~XQSPIPSU_GENFIFO_IMM_DATA_MASK;
GenFifoEntry |= exponent;
#if defined(_DEBUG)
printHEX("GenFifo data:", GenFifoEntry);
#endif
XQSpiPS_WriteReg(GQSPI_GEN_FIFO_OFFSET, GenFifoEntry);
}
tmpCount = tmpCount >> 1;
exponent++;
}
/* Immediate entry */
GenFifoEntry &= ~XQSPIPSU_GENFIFO_EXP;
if (immData > 0) {
GenFifoEntry &= ~XQSPIPSU_GENFIFO_IMM_DATA_MASK;
GenFifoEntry |= immData;
#if defined(_DEBUG)
printHEX("GenFifo data:", GenFifoEntry);
#endif
XQSpiPS_WriteReg(GQSPI_GEN_FIFO_OFFSET, GenFifoEntry);
}
}
#if defined(_DEBUG)
std::cout << "Sent GenFifo Entry Data" << std::endl;
#endif
}
void XQSPIPS_Flasher::sendGenFifoEntryCSDeAssert()
{
uint32_t GenFifoEntry = 0x0U;
GenFifoEntry &= ~(XQSPIPSU_GENFIFO_DATA_XFER | XQSPIPSU_GENFIFO_EXP);
GenFifoEntry &= ~XQSPIPSU_GENFIFO_MODE_MASK;
//GenFifoEntry |= XQSPIPSU_GENFIFO_MODE_SPI;
/* By default, use upper and lower CS and Bus */
GenFifoEntry &= ~XQSPIPSU_GENFIFO_BUS_MASK;
if (mConnectMode == 0)
GenFifoEntry |= XQSPIPSU_GENFIFO_BUS_BOTH;
else
GenFifoEntry |= XQSPIPSU_GENFIFO_BUS_LOWER;
GenFifoEntry &= ~(XQSPIPSU_GENFIFO_TX | XQSPIPSU_GENFIFO_RX |
XQSPIPSU_GENFIFO_STRIPE | XQSPIPSU_GENFIFO_POLL);
GenFifoEntry |= XQSPIPSU_GENFIFO_CS_HOLD;
/* Write GEN FIFO */
XQSpiPS_WriteReg(GQSPI_GEN_FIFO_OFFSET, GenFifoEntry);
#if defined(_DEBUG)
printHEX("De-Assert CS: expectd 0xc004, got", GenFifoEntry);
#endif
}
/**
* @brief XQSPIPS_Flasher::finalTransfer
*
* This function performs a transfer on the bus in polled mode. The messages passed are all transferred between one CS asser and de-assert.
*
* @param msg is a pointer to the struct containing transfer data
* @param numMsg is the number of message to be transferrd.
*
* @return
* - True Success
* - Error code
*
*/
bool XQSPIPS_Flasher::finalTransfer(xqspips_msg_t *msg, uint32_t numMsg)
{
uint32_t StatusReg;
/* Make sure GQSPI is enable */
XQSpiPS_Enable_GQSPI();
sendGenFifoEntryCSAssert();
for (uint32_t Index = 0; Index < numMsg; Index++) {
/* Only handle one message at a time */
sendGenFifoEntryData(&msg[Index]);
do {
StatusReg = XQSpiPS_GetStatusReg();
/* Transmit more data if left */
if (StatusReg & XQSPIPSU_ISR_TXNOT_FULL_MASK &&
msg[Index].flags & XQSPIPSU_MSG_FLAG_TX &&
mTxBytes > 0) {
fillTxFifo(&msg[Index], XQSPIPSU_TXD_DEPTH);
}
if (msg[Index].flags & XQSPIPSU_MSG_FLAG_RX) {
uint32_t RxThr = XQSpiPS_ReadReg(GQSPI_RX_THRESH_OFFSET);
if (StatusReg & XQSPIPSU_ISR_RXNEMPTY_MASK) {
readRxFifo(&msg[Index], RxThr * 4);
} else {
if (StatusReg & XQSPIPSU_ISR_GENFIFOEMPTY_MASK) {
readRxFifo(&msg[Index], msg[Index].byteCount);
}
}
}
if (!waitGenFifoEmpty() || !waitTxEmpty())
return false;
} while (mTxBytes != 0 || mRxBytes != 0);
}
sendGenFifoEntryCSDeAssert();
StatusReg = XQSpiPS_GetStatusReg();
while (!(StatusReg & XQSPIPSU_ISR_GENFIFOEMPTY_MASK)) {
StatusReg = XQSpiPS_GetStatusReg();
}
XQSpiPS_Disable_GQSPI();
#if defined(_DEBUG)
std::cout << "Final transfer finished" << std::endl;
#endif
return true;
}
bool XQSPIPS_Flasher::isFlashReady()
{
xqspips_msg_t msgFlashStatus[2];
uint8_t writeCmd = READ_STATUS_CMD;
uint32_t StatusReg = 0;
const timespec req = {0, 20000};
long long delay = 0;
msgFlashStatus[0].byteCount = 1;
msgFlashStatus[0].busWidth = XQSPIPSU_SELECT_MODE_SPI;
msgFlashStatus[0].flags = XQSPIPSU_MSG_FLAG_TX;
msgFlashStatus[1].byteCount = STATUS_READ_BYTES;
msgFlashStatus[1].busWidth = XQSPIPSU_SELECT_MODE_SPI;
msgFlashStatus[1].flags = XQSPIPSU_MSG_FLAG_RX | XQSPIPSU_MSG_FLAG_STRIPE;
while (delay < 30000000000) {
msgFlashStatus[0].bufPtr = &writeCmd;
msgFlashStatus[1].bufPtr = mReadBuffer;
bool Status = finalTransfer(msgFlashStatus, 2);
if (!Status) {
return false;
}
if (mConnectMode == 0)
StatusReg = mReadBuffer[1] |= mReadBuffer[0];
else
StatusReg = mReadBuffer[0];
#if defined(_DEBUG)
printHEX("Flash ready:", StatusReg);
#endif
if (!(StatusReg & FLASH_SR_BUSY_MASK)) {
return true;
}
nanosleep(&req, 0);
delay += 5000;
}
std::cout << "Unable to get Flash Ready" << std::endl;
return false;
}
bool XQSPIPS_Flasher::setWriteEnable()
{
xqspips_msg_t msgWriteEnable[2];
uint8_t writeCmd = WRITE_ENABLE_CMD;
uint32_t StatusReg = XQSpiPS_GetStatusReg();
if (StatusReg & XQSPIPSU_ISR_TXFULL_MASK) {
std::cout << "TXD FIFO full during WriteEnable" << std::endl;
return false;
}
msgWriteEnable[0].bufPtr = &writeCmd;
msgWriteEnable[0].byteCount = 1;
msgWriteEnable[0].busWidth = XQSPIPSU_SELECT_MODE_SPI;
msgWriteEnable[0].flags = XQSPIPSU_MSG_FLAG_TX;
if (!finalTransfer(msgWriteEnable, 1))
return false;
if (TEST_MODE)
std::cout << "Set write enable" << std::endl;
return waitTxEmpty();
}
bool XQSPIPS_Flasher::getFlashID()
{
xqspips_msg_t msgFlashID[2];
uint32_t Status;
if (!isFlashReady())
return false;
mWriteBuffer[0] = READ_ID;
msgFlashID[0].bufPtr = mWriteBuffer;
msgFlashID[0].byteCount = 1;
msgFlashID[0].busWidth = XQSPIPSU_SELECT_MODE_SPI;
msgFlashID[0].flags = XQSPIPSU_MSG_FLAG_TX;
msgFlashID[1].bufPtr = mReadBuffer;
msgFlashID[1].byteCount = IDCODE_READ_BYTES;
msgFlashID[1].busWidth = XQSPIPSU_SELECT_MODE_SPI;
msgFlashID[1].flags = XQSPIPSU_MSG_FLAG_RX | XQSPIPSU_MSG_FLAG_STRIPE;
Status = finalTransfer(msgFlashID, 2);
if ( !Status ) {
return false;
}
if (mConnectMode == 0) {
// Stripe data: Lower data bus uses even bytes, i.e byte 0, 2, 4, ...
// Upper data bus uses odd bytes, i.e byte 1, 3, 5, ...
if (mReadBuffer[0] != mReadBuffer[1]) {
std::cout << "Upper Flash chip and lower Flash chip have differe vender id" << std::endl;
return false;
}
if (mReadBuffer[2] != mReadBuffer[3]) {
std::cout << "Upper Flash chip and lower Flash chip have differe type" << std::endl;
return false;
}
if (mReadBuffer[4] != mReadBuffer[5]) {
std::cout << "Upper Flash chip and lower Flash chip have differe capacity" << std::endl;
return false;
}
}
//Update flash vendor
for (size_t i = 0; i < flashVendors.size(); i++)
if (mReadBuffer[0] == flashVendors[i])
flashVendor = flashVendors[i];
//Update max number of sector. Value of 0x18 is 1 128Mbit sector
if(mReadBuffer[4] == 0xFF)
return false;
for (int i = 0; i < IDCODE_READ_BYTES; i++) {
std::cout << "Idcode byte[" << i << "]=" << std::hex << (int)mReadBuffer[i] << std::dec << std::endl;
mReadBuffer[i] = 0;
}
return true;
}
bool XQSPIPS_Flasher::eraseSector(unsigned addr, uint32_t byteCount, uint8_t eraseCmd)
{
xqspips_msg_t msgEraseFlash[1];
uint8_t writeCmds[5];
uint32_t realAddr;
uint32_t Sector;
if (eraseCmd == 0xff)
eraseCmd = SEC_4B_ERASE_CMD;
int beatCount = 0;
//roundup byteCount to next SECTOR boundary
byteCount = (byteCount + SECTOR_SIZE - 1) & (~SECTOR_SIZE);
for (Sector = 0; Sector < (byteCount / SECTOR_SIZE); Sector++) {
if(!isFlashReady())
return false;
beatCount++;
if (beatCount % 64 == 0) {
std::cout << "." << std::flush;
}
if (mConnectMode == 0)
realAddr = addr / 2;
else
realAddr = addr;
if(!setWriteEnable())
return false;
writeCmds[0] = eraseCmd;
writeCmds[1] = (uint8_t)((realAddr & 0xFF000000) >> 24);
writeCmds[2] = (uint8_t)((realAddr & 0xFF0000) >> 16);
writeCmds[3] = (uint8_t)((realAddr & 0xFF00) >> 8);
writeCmds[4] = (uint8_t)(realAddr & 0xFF);
msgEraseFlash[0].bufPtr = writeCmds;
msgEraseFlash[0].byteCount = 5;
msgEraseFlash[0].busWidth = XQSPIPSU_SELECT_MODE_SPI;
msgEraseFlash[0].flags = XQSPIPSU_MSG_FLAG_TX;
if (!finalTransfer(msgEraseFlash, 1))
return false;
addr += SECTOR_SIZE;
}
if (TEST_MODE)
std::cout << "Erase Flash done " << byteCount << " bytes" << std::endl;
return true;
}
bool XQSPIPS_Flasher::eraseBulk()
{
xqspips_msg_t msgEraseFlash[1];
uint8_t writeCmds[5];
uint8_t eraseCmd;
if(!isFlashReady())
return false;
eraseCmd = BULK_ERASE_CMD;
if(!setWriteEnable())
return false;
writeCmds[0] = eraseCmd;
msgEraseFlash[0].bufPtr = writeCmds;
msgEraseFlash[0].byteCount = 1;
msgEraseFlash[0].busWidth = XQSPIPSU_SELECT_MODE_SPI;
msgEraseFlash[0].flags = XQSPIPSU_MSG_FLAG_TX;
if (!finalTransfer(msgEraseFlash, 1))
return false;
if(!isFlashReady())
return false;
return true;
}
bool XQSPIPS_Flasher::readFlash(unsigned addr, uint32_t byteCount, uint8_t readCmd)
{
xqspips_msg_t msgReadFlash[3];
uint8_t writeCmds[5];
uint32_t realAddr;
uint32_t commandBytes;
uint32_t msgCnt;
if (!isFlashReady())
return false;
if (mConnectMode == 0)
realAddr = addr / 2;
else
realAddr = addr;
if (readCmd == 0xff) {
switch(mBusWidth) {
case 2:
readCmd = DUAL_READ_CMD;
break;
case 4:
readCmd = QUAD_READ_CMD;
break;
default:
readCmd = READ_CMD;
}
}
writeCmds[0] = readCmd;
writeCmds[1] = (uint8_t)((realAddr & 0xFF000000) >> 24);
writeCmds[2] = (uint8_t)((realAddr & 0xFF0000) >> 16);
writeCmds[3] = (uint8_t)((realAddr & 0xFF00) >> 8);
writeCmds[4] = (uint8_t)(realAddr & 0xFF);
commandBytes = 5;
msgReadFlash[0].bufPtr = writeCmds;
msgReadFlash[0].byteCount = commandBytes;
msgReadFlash[0].busWidth = XQSPIPSU_SELECT_MODE_SPI;
msgReadFlash[0].flags = XQSPIPSU_MSG_FLAG_TX;
msgCnt = 1;
/* Dummy clock */
if (readCmd == QUAD_READ_CMD) {
msgReadFlash[msgCnt].bufPtr = NULL;
msgReadFlash[msgCnt].byteCount = 8;
msgReadFlash[msgCnt].busWidth = XQSPIPSU_SELECT_MODE_SPI;
msgReadFlash[msgCnt].flags = 0;
msgCnt++;
} else if (readCmd == DUAL_READ_CMD) {
msgReadFlash[msgCnt].bufPtr = NULL;
msgReadFlash[msgCnt].byteCount = 8;
msgReadFlash[msgCnt].busWidth = mBusWidth;
msgReadFlash[msgCnt].flags = 0;
msgCnt++;
}
msgReadFlash[msgCnt].bufPtr = mReadBuffer;
msgReadFlash[msgCnt].byteCount = byteCount;
msgReadFlash[msgCnt].busWidth = mBusWidth;
msgReadFlash[msgCnt].flags = XQSPIPSU_MSG_FLAG_RX | XQSPIPSU_MSG_FLAG_STRIPE;
msgCnt++;
if (!finalTransfer(msgReadFlash, msgCnt))
return false;
if (TEST_MODE)
std::cout << "Read Flash done " << byteCount << " bytes" << std::endl;
return true;
}
bool XQSPIPS_Flasher::writeFlash(unsigned addr, uint32_t byteCount, uint8_t writeCmd)
{
xqspips_msg_t msgWriteFlash[3];
uint8_t writeCmds[5];
uint32_t realAddr;
uint32_t commandBytes;
if(!isFlashReady())
return false;
if (mConnectMode == 0)
realAddr = addr / 2;
else
realAddr = addr;
if (!setWriteEnable())
return false;
if(!isFlashReady())
return false;
if (writeCmd == 0xff) {
switch(mBusWidth) {
case 2:
writeCmd = DUAL_WRITE_CMD;
break;
case 4:
writeCmd = QUAD_WRITE_CMD;
break;
default:
writeCmd = WRITE_CMD;
}
}
writeCmds[0] = writeCmd;
writeCmds[1] = (uint8_t)((realAddr & 0xFF000000) >> 24);
writeCmds[2] = (uint8_t)((realAddr & 0xFF0000) >> 16);
writeCmds[3] = (uint8_t)((realAddr & 0xFF00) >> 8);
writeCmds[4] = (uint8_t)(realAddr & 0xFF);
commandBytes = 5;
msgWriteFlash[0].bufPtr = writeCmds;
msgWriteFlash[0].byteCount = commandBytes;
msgWriteFlash[0].busWidth = XQSPIPSU_SELECT_MODE_SPI;
msgWriteFlash[0].flags = XQSPIPSU_MSG_FLAG_TX;
/* The data to write is already filled up */
msgWriteFlash[1].bufPtr = mWriteBuffer;
msgWriteFlash[1].byteCount = byteCount;
msgWriteFlash[1].busWidth = mBusWidth;
msgWriteFlash[1].flags = XQSPIPSU_MSG_FLAG_TX | XQSPIPSU_MSG_FLAG_STRIPE;
if (!finalTransfer(msgWriteFlash, 2))
return false;
if (TEST_MODE)
std::cout << "Write Flash done " << byteCount << " bytes" << std::endl;
return true;
}
bool XQSPIPS_Flasher::enterOrExitFourBytesMode(uint32_t enable)
{
uint8_t cmd;
if (enable) {
cmd = ENTER_4B_ADDR_MODE;
} else {
cmd = EXIT_4B_ADDR_MODE;
}
writeFlashReg(cmd, 0, 0);
if (!isFlashReady())
return false;
if (TEST_MODE)
std::cout << "Four Bytes Mode " << enable << std::endl;
return true;
}
bool XQSPIPS_Flasher::readFlashReg(unsigned commandCode, unsigned bytes)
{
xqspips_msg_t msgToFlash[2];
bool Status = false;
if (!isFlashReady())
return false;
mWriteBuffer[0] = commandCode;
msgToFlash[0].bufPtr = mWriteBuffer;
msgToFlash[0].byteCount = 1;
msgToFlash[0].busWidth = XQSPIPSU_SELECT_MODE_SPI;
msgToFlash[0].flags = XQSPIPSU_MSG_FLAG_TX;
msgToFlash[1].bufPtr = mReadBuffer;
msgToFlash[1].byteCount = bytes;
msgToFlash[1].busWidth = XQSPIPSU_SELECT_MODE_SPI;
msgToFlash[1].flags = XQSPIPSU_MSG_FLAG_RX | XQSPIPSU_MSG_FLAG_STRIPE;
Status = finalTransfer(msgToFlash, 2);
if ( !Status ) {
return false;
}
#if defined(_DEBUG)
std::cout << "Printing output (with some extra bytes of readFlashReg cmd)" << std::endl;
#endif
for(unsigned i = 0; i < bytes; ++ i) //Some extra bytes, no harm
{
#if defined(_DEBUG)
std::cout << i << " " << std::hex << (int)mReadBuffer[i] << std::dec << std::endl;
#endif
mReadBuffer[i] = 0; //clear
}
return Status;
}
bool XQSPIPS_Flasher::writeFlashReg(unsigned commandCode, unsigned value, unsigned bytes)
{
bool Status = false;
xqspips_msg_t msgWriteFlash[1];
if (!setWriteEnable())
return false;
mWriteBuffer[0] = commandCode;
switch (bytes) {
case 0:
break;
case 1:
mWriteBuffer[1] = (uint8_t) (value);
break;
case 2:
mWriteBuffer[1] = (uint8_t) (value >> 8);
mWriteBuffer[2] = (uint8_t) (value);
break;
default:
std::cout << "ERROR: Setting more than 2 bytes" << std::endl;
assert(0);
}
msgWriteFlash[0].bufPtr = mWriteBuffer;
msgWriteFlash[0].byteCount = 1 + bytes;
msgWriteFlash[0].busWidth = XQSPIPSU_SELECT_MODE_SPI;
msgWriteFlash[0].flags = XQSPIPSU_MSG_FLAG_TX;
Status = finalTransfer(msgWriteFlash, 1);
if (!Status)
return false;
if (!waitTxEmpty())
return false;
return Status;
}
int XQSPIPS_Flasher::xclTestXQSpiPS(int index)
{
TEST_MODE = false;
std::cout << ">>> Test XQSpiPS engine <<<" << std::endl;
initQSpiPS();
/* print the IP (not of flash) control/status register. */
uint32_t ConfigReg = XQSpiPS_GetConfigReg();
uint32_t StatusReg = XQSpiPS_GetStatusReg();
std::cout << "PS GQSPI Config/Status " << std::hex << ConfigReg << "/" << StatusReg << std::dec << std::endl;
/* Make sure it is ready to receive commands. */
resetQSpiPS();
XQSpiPS_Enable_GQSPI();
printHEX("GQSPI enable:", XQSpiPS_ReadReg(GQSPI_EN_OFFSET));
/* 1. idcode read */
std::cout << ">>> Testing read Flash ID" << std::endl;
if (!getFlashID()) {
std::cout << "[ERROR]: Could not get Flash ID" << std::endl;
exit(-EOPNOTSUPP);
}
std::cout << "id code successful (please verify the idcode output too)" << std::endl;
std::cout << ">>> Now reading various flash registers <<<" << std::endl;
/* 2. register read */
std::cout << "Testing READ_STATUS_CMD" << std::endl;
uint8_t Cmd = READ_STATUS_CMD;
readFlashReg(Cmd, STATUS_READ_BYTES);
std::cout << "Testing READ_FLAG_STATUS_CMD" << std::endl;
Cmd = READ_FLAG_STATUS_CMD;
readFlashReg(Cmd, STATUS_READ_BYTES);
std::cout << "Testing EXTADD_REG_RD" << std::endl;
Cmd = EXTADD_REG_RD;
readFlashReg(Cmd, STATUS_READ_BYTES);
/* 3. Testing simple read and write */
enterOrExitFourBytesMode(ENTER_4B);
std::cout << ">>> Testing simple read and write <<<" << std::endl;
unsigned addr = 0;
unsigned size = 0;
// Write/Read 16K + 100 bytes
//int total_size = 16 * 1024 + 100;
int total_size = 300;
int remain = total_size % PAGE_SIZE;
int pages = total_size / PAGE_SIZE;
std::cout << "Write " << total_size << " bytes" << std::endl;
std::cout << "earse flash" << std::endl;
eraseSector(0, total_size);
//eraseBulk();
std::cout << ">>>>>> Write " << std::endl;
for (int page = 0; page <= pages; page++) {
addr = page * PAGE_SIZE;
if (page != pages)
size = PAGE_SIZE;
else
size = remain;
for (unsigned index = 0; index < size; index++) {
mWriteBuffer[index] = (uint8_t)index;
}
writeFlash(addr, size);
}
remain = total_size % 256;
pages = total_size / 256;
std::cout << ">>>>>> Verify data" << std::endl;
for (int page = 0; page <= pages; page++) {
addr = page * 256;
if (page != pages)
size = 256;
else
size = remain;
readFlash(addr, size);
// Verify
for (unsigned i = 0; i < size; i++) {
std::cout << i << " 0x" << std::hex << (int)mReadBuffer[i] << std::dec << std::endl;
if (mReadBuffer[i] != (i % PAGE_SIZE)) {
std::cout << "Found mismatch" << std::endl;
return -1;
}
}
}
std::cout << ">>>>>> " << total_size << " bytes data correct!" << std::endl;
enterOrExitFourBytesMode(EXIT_4B);
std::cout << ">>> Test Passed <<<" << std::endl;
return 0;
}