/*
* JR-IDE Project
* - (c) 2017 Alan Hightower
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include "machxo2_internal.h"
#define DEBUG 1
#ifdef DEBUG
#include
#define LOGERR(fmt, args...) fprintf(stderr, "\nERROR: " fmt "\n\n", ## args)
#define LOGDBG(fmt, args...) printf(fmt "\n", ## args)
#else
#define LOGERR(fmt, args...)
#define LOGDBG(fmt, args...)
#endif
struct _st_xo2_handle
{
const xo2_info_t *info;
xo2_exchange_cb exchange;
void *user_ctx;
};
uint32_t xo2_jpf_usercode (void *image_ptr)
{
machxo2_hdr_t *image = (machxo2_hdr_t *) image_ptr;
return image->user_code;
}
static const xo2_info_t *_lookup_device (uint32_t device_id)
{
int i;
for (i = 0; i < MachXO2_DEVICE_COUNT; i++)
{
if ((device_id == xo2_device_info[i].heze_id) ||
(device_id == xo2_device_info[i].hc_id))
return &xo2_device_info[i];
}
return NULL;
}
xo2_handle_t *xo2_open (xo2_exchange_cb cb, void *user_ctx)
{
int sts = -1;
xo2_handle_t *xo2;
xo2 = (xo2_handle_t *) malloc (sizeof (xo2_handle_t));
memset (xo2, 0, sizeof (xo2_handle_t));
xo2->exchange = cb;
xo2->user_ctx = user_ctx;
do
{
uint32_t device_id = 0;
if ((sts = xo2_read_device_id (xo2, &device_id)))
break;
if (!(xo2->info = _lookup_device (device_id)))
{
LOGERR("Unknown device ID 0x%08x", device_id);
sts = -1;
break;
}
sts = 0;
} while (0);
if (sts)
{
free (xo2);
return NULL;
}
return xo2;
}
/**
* Get descriptive name for the detected device
*
* Returns a name that can eb used to describe the overall part number
* of the XO2 device detected by the open call.
*
* @param xo2 Open API handle
*
* @return Device type description string
*/
const char *xo2_name (xo2_handle_t *xo2)
{
return xo2->info->name;
}
/**
* Close open API handle
*
* Closes the open API handle and frees any resources associated with it
*
* @param xo2 Open API handle
*/
void xo2_close (xo2_handle_t *xo2)
{
if (!xo2)
return;
free (xo2);
return;
}
/**
* Read the 4 byte Device ID from the XO2 configuration logic block.
* This function assembles the command sequence that allows reading
* the XO2 Device ID from the configuration logic. The command is
* first written to the XO2, then a read of 4 bytes is perfromed to
* return the value.
*
* @param xo2 Open API handle
* @param value pointer to the 32 bit integer to return the ID value in
*
* @return 0 on success, !0 on error
*/
int xo2_read_device_id (xo2_handle_t *xo2, uint32_t *value)
{
uint8_t cmd[4], data[4];
cmd[0] = 0xE0; // Read Device ID opcode
cmd[1] = 0x00; // arg0
cmd[2] = 0x00; // arg1
cmd[3] = 0x00; // arg2
int sts;
if (!(sts = xo2->exchange (xo2->user_ctx, cmd, 4, data, 4)))
*value = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
return sts;
}
/**
* Read the 4 byte USERCODE from the XO2 Configuration logic block.
* This function assembles the command sequence that allows reading the XO2 USERCODE
* value from the configuration Flash sector. The command is first written to the XO2, then a read
* of 4 bytes is perfromed to return the value.
*
* @param xo2 Open API handle
* @param value pointer to the 32 bit integer to return the USERCODE value in
*
* @return 0 on success, !0 on error
*/
int xo2_read_user_code (xo2_handle_t *xo2, uint32_t *value)
{
uint8_t cmd[4], data[4];
cmd[0] = 0xC0; // Read USERCODE opcode
cmd[1] = 0x00; // arg0
cmd[2] = 0x00; // arg1
cmd[3] = 0x00; // arg2
int sts;
if (!(sts = xo2->exchange (xo2->user_ctx, cmd, 4, data, 4)))
*value = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
return sts;
}
/**
* Read the 8 byte (64 bit) TraceID from the XO2 Feature Row.
* This function assembles the command sequence that allows reading the XO2 TraceID
* value from the Feature Row Flash sector. The command is first written to the XO2, then a read
* of 8 bytes is perfromed to return the value.
* The TraceID is set in the Global Settings of Spreadsheet view.
* The first byte read back (pVal[0]) can be set by the user in Spreadsheet view.
* The remaining 7 bytes are unique for each silicon die.
*
* @param xo2 Open API handle
* @param value pointer to the 8 byte array to return the TraceID value in
*
* @return 0 on success, !0 on error
*/
int xo2_read_trace_id (xo2_handle_t *xo2, uint8_t *value)
{
uint8_t cmd[4];
cmd[0] = 0x19; // Read TraceID opcode
cmd[1] = 0x00; // arg0
cmd[2] = 0x00; // arg1
cmd[3] = 0x00; // arg2
return xo2->exchange (xo2->user_ctx, cmd, 4, value, 8);
}
/**
* Read the 4 byte Status Register from the XO2 Configuration logic block.
* This function assembles the command sequence that allows reading the XO2 Status Register.
* The command is first written to the XO2, then a read of 4 bytes is perfromed to return the value.
*
* @param xo2 Open API handle
* @param value pointer to the 32 bit integer to return the Status Register value in.
*
* @return 0 on success, !0 on error
*/
int xo2_status_read (xo2_handle_t *xo2, uint32_t *value)
{
uint8_t cmd[4], data[4];
cmd[0] = 0x3C; // Read Status Register opcode
cmd[1] = 0x00; // arg0
cmd[2] = 0x00; // arg1
cmd[3] = 0x00; // arg2
int sts;
if (!(sts = xo2->exchange (xo2->user_ctx, cmd, 4, data, 4)))
*value = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
return sts;
}
/**
* Wait for the Status register to report no longer busy.
* Also check bit 13 for FAIL indication. Return error if an error
* condition is detected. Also return if exceed timeout.
*
* @param xo2 Open API handle
* @param timeout_ms Number of milliseconds to wait before returning error
*
* @return 0 on success, !0 on error
*/
int xo2_status_wait (xo2_handle_t *xo2, int timeout_ms)
{
int sts;
uint32_t value;
do
{
if ((sts = xo2_status_read (xo2, &value)) || (value & 0x2000))
return -1; // read failure or FAILURE bit
if (value & 0x1000) // BUSY
usleep (1000);
} while (--timeout_ms && (value & 0x1000)); // BUSY
return (value & 0x1000) ? -1 : 0;
}
/**
* Enable access to Configuration Logic in Transparent mode or Offline mode.
* This function issues one of the Enable Configuration Interface commands
* depending on the value of mode. Transparent mode allows the XO2 to
* continue operating in user mode while access to the config logic is
* performed. Offline mode halts all user logic, and tri-states I/Os,
* while access is occurring.
*
* @param xo2 Open API handle
* @param mode specify XO2_MODE_TRANSPARENT or XO2_MODE_OFFLINE
*
* @return 0 on success, !0 on error
*/
int xo2_mode_set (xo2_handle_t *xo2, int mode)
{
uint8_t cmd[4];
if (mode & XO2_MODE_TRANSPARENT)
cmd[0] = 0x74;
else
cmd[0] = 0xC6;
cmd[1] = 0x08; // arg0
cmd[2] = 0x00; // arg1
cmd[3] = 0x00; // arg2
int sts = xo2->exchange (xo2->user_ctx, cmd, 4, NULL, 0);
if (mode & XO2_MODE_TRANSPARENT)
{
if (sts)
return sts;
sts = xo2_status_wait (xo2, 2000);
}
else
{
int retries = 50;
while ((sts = xo2_status_wait (xo2, 500)) && retries--);
}
if (sts)
return sts;
LOGDBG ("Entered %s flash config mode",
(mode & XO2_MODE_TRANSPARENT) ? "transparent" : "offline");
return 0;
}
/**
* Reset the Address Regsiter to point to the first UFM or config
* page (sector 1, page 0).
*
* @param xo2 Open API handle
* @param mode which address to update: XO2_SECTOR_CFG or XO2_SECTOR_UFM
*
* @return 0 on success, !0 on error
*/
int xo2_page_reset (xo2_handle_t *xo2, XO2_SECTOR_MODE mode)
{
uint8_t cmd[4];
if (mode == XO2_SECTOR_CFG)
cmd[0] = 0x46;
else
cmd[0] = 0x47;
cmd[1] = 0x00; // arg0
cmd[2] = 0x00; // arg1
cmd[3] = 0x00; // arg2
return xo2->exchange (xo2->user_ctx, cmd, 4, NULL, 0);
}
/**
* Set the current Page Address in either Cfg Flash or UFM.
* The specific page is set for the next read/write operation.
* This is probably only useful for the UFM since skipping around
* in the configuration sector is very unlikely.
*
* @param xo2 Open API handle
* @param mode which address to update: XO2_SECTOR_CFG or XO2_SECTOR_UFM
* @param page the page number to set address pointer to
*
* @return 0 on success, !0 on error
*/
int xo2_page_set (xo2_handle_t *xo2, XO2_SECTOR_MODE mode, int page)
{
uint8_t cmd[8];
cmd[0] = 0xB4; // opcode
cmd[1] = 0x00; // arg0
cmd[2] = 0x00; // arg1
cmd[3] = 0x00; // arg2
if (mode == XO2_SECTOR_CFG)
cmd[4] = 0x00;
else
cmd[4] = 0x40;
cmd[5] = 0x00;
cmd[6] = page >> 8;
cmd[7] = page & 0xff;
return xo2->exchange (xo2->user_ctx, cmd, 8, NULL, 0);
}
/**
* Read the next page (16 bytes) from the UFM or config memory.
*
* @param xo2 Open API handle
* @param mode which address to update: XO2_SECTOR_CFG or XO2_SECTOR_UFM
* @param data pointer to the 16 byte array to return the UFM page bytes in.
*
* @return 0 on success, !0 on error
*
* @note There is no advantage to reading multiple pages since the interface
* is most likely so slow. The I2C only runs at 100kHz.
*/
int xo2_page_read (xo2_handle_t *xo2, XO2_SECTOR_MODE mode, uint8_t *data)
{
uint8_t cmd[4];
if (mode == XO2_SECTOR_CFG)
cmd[0] = 0x73;
else
cmd[0] = 0xCA;
cmd[1] = 0x00; // arg0
cmd[2] = 0x00; // arg1
cmd[3] = 0x01; // arg2 = 1 page
return xo2->exchange (xo2->user_ctx, cmd, 4, data, XO2_PAGE_SIZE);
}
/**
* Write a page (16 bytes) into the current UFM or config memory page.
* Page address can be set using SetAddress command.
* Page address advances to next page after programming is completed.
*
* @param xo2 Open API handle
* @param mode which address to update: XO2_SECTOR_CFG or XO2_SECTOR_UFM
* @param data pointer to the 16 byte array to write into the UFM page.
* @param timeout_ms Number of milliseconds to wait before returning error
*
* @return 0 on success, !0 on error
*
* @note Programming must be done on a page basis. Pages must be erased to 0's first.
*/
int xo2_page_write (xo2_handle_t *xo2, XO2_SECTOR_MODE mode, uint8_t *data, int timeout_ms)
{
uint8_t cmd[4 + XO2_PAGE_SIZE];
if (mode == XO2_SECTOR_CFG)
cmd[0] = 0x70;
else
cmd[0] = 0xC9;
cmd[1] = 0x00; // arg0
cmd[2] = 0x00; // arg1
cmd[3] = 0x01; // arg2
memcpy (&cmd[4], data, XO2_PAGE_SIZE);
int sts;
if (!(sts = xo2->exchange (xo2->user_ctx, cmd, 4 + XO2_PAGE_SIZE, NULL, 0)))
{
usleep (1000);
sts = xo2_status_wait (xo2, timeout_ms);
}
return sts;
}
/**
* Disable access to Configuration Logic Interface.
*
* This function issues the Disable Configuration Interface command and
* registers that the interface is no longer available for certain commands
* that require it to be enabled. It then issues a the bypass command.
*
* @param xo2 Open API handle
*
* @return 0 on success, !0 on error
*/
int xo2_config_exit (xo2_handle_t *xo2)
{
uint8_t cmd[3];
cmd[0] = 0x26; // Disable Config Interface opcode
cmd[1] = 0x00; // arg0
cmd[2] = 0x00; // arg1
int sts;
if ((sts = xo2->exchange (xo2->user_ctx, cmd, 3, NULL, 0)))
return sts;
cmd[0] = 0xFF; // Bypass opcode
return xo2->exchange (xo2->user_ctx, cmd, 1, NULL, 0);
}
/**
* Issue the Refresh command that updates the SRAM from Flash and
* boots the XO2.
*
* @param xo2 Open API handle
*
* @return 0 on success, !0 on error
*/
int xo2_refresh (xo2_handle_t *xo2)
{
uint8_t cmd[3];
cmd[0] = 0x79; // Refresh opcode
cmd[1] = 0x00; // arg0
cmd[2] = 0x00; // arg1
int sts;
if ((sts = xo2->exchange (xo2->user_ctx, cmd, 3, NULL, 0)))
return sts;
usleep (xo2->info->trefresh * 1000000ULL);
return xo2_status_wait (xo2, 2000);
}
/**
* Issue the Done command that updates the Program DONE bit.
* Typically used after programming the Cfg Flash and before
* closing access to the configuration interface.
*
* @param xo2 Open API handle
* @param timeout_ms Number of milliseconds to wait before returning error
*
* @return 0 on success, !0 on error
*/
int xo2_done (xo2_handle_t *xo2, int timeout_ms)
{
uint8_t cmd[4];
cmd[0] = 0x5E; // Program DONE bit opcode
cmd[1] = 0x00; // arg0
cmd[2] = 0x00; // arg1
cmd[3] = 0x00; // arg2
int sts;
if (!(sts = xo2->exchange (xo2->user_ctx, cmd, 4, NULL, 0)))
{
usleep (200);
sts = xo2_status_wait (xo2, timeout_ms);
}
return sts;
}
/**
* Erase any/all entire sectors of the XO2 Flash or SRAM memory.
*
* @param xo2 Open API handle
* @param mode bit map of what sector contents to erase
*
* @return 0 on success, !0 on error
*
* @note Erases bits to a value of 0. Any bit that is a 0 can then be programmed to a 1.
*/
int xo2_flash_erase (xo2_handle_t *xo2, int mode)
{
uint8_t cmd[4];
cmd[0] = 0x0E; // Erase Flash opcode
cmd[1] = mode; // arg0 = which sectors to clear
cmd[2] = 0x00; // arg1
cmd[3] = 0x00; // arg2
int sts;
if ((sts = xo2->exchange (xo2->user_ctx, cmd, 4, NULL, 0)))
return sts;
if (mode & XO2_MODE_ERASE_CONFIG)
usleep (xo2->info->cfg_erase * 1000ULL);
else if (mode & XO2_MODE_ERASE_UFM)
usleep (xo2->info->ufm_erase * 1000ULL);
else
usleep (50000ULL);
return xo2_status_wait (xo2, 10000);
}
/**
* Clear a failed programming attempt.
* Call when programming fails. This erases the Config, UFM, Feature Row
* sectors of the XO2 Flash and clears the SRAM by doing a Refresh with
* the now blank flash contents. This will put the part into a factory
* blank state. Factory blank state has I2C and SPI enabled for
* configuration, so a new programming cycle can be re-attempted.
*
* @return 0 on success, !0 on error
*/
int xo2_factory_reset (xo2_handle_t *xo2)
{
int sts;
if ((sts = xo2_mode_set (xo2, XO2_MODE_OFFLINE)))
return sts;
int mode = XO2_MODE_ERASE_UFM
| XO2_MODE_ERASE_CONFIG
| XO2_MODE_ERASE_FEATROW;
return xo2_flash_erase (xo2, mode);
}
/**
* Set the Feature Row.
* This function assembles the command sequence that allows programming the XO2 FEATURE
* bits and the FEATURE_BITS in the Feature Row. The 8 FEATURE bytes and 2 FEATURE_BITS bytes
* must be properly formatted or possible
* lock-up of the XO2 could occur. Only the values obtained from properly parsing the JEDEC
* file should be used.
*
* @param xo2 Open API handle
* @param pXO2 pointer to the XO2 device to access
* @param pFeature pointer to the Feature Row structure containing the encoded data to write
* into the Feature and FEATURE_BITS fields in the Feature Row.
*
* @return 0 on success, !0 on error
*
* @note The Feature Row must first be erased
*/
int xo2_feature_write (xo2_handle_t *xo2, const uint8_t *feature_row, const uint8_t *feature_bits)
{
uint8_t cmd[12];
cmd[0] = 0xE4; // program Feature opcode
cmd[1] = 0x00; // arg0
cmd[2] = 0x00; // arg1
cmd[3] = 0x00; // arg2
memcpy (&cmd[4], feature_row, 8);
int sts;
if ((sts = xo2->exchange (xo2->user_ctx, cmd, 12, NULL, 0)))
return sts;
usleep (200);
cmd[0] = 0xF8; // program FEATURE_BITS opcode
cmd[1] = 0x00; // arg0
cmd[2] = 0x00; // arg1
cmd[3] = 0x00; // arg2
cmd[4] = feature_bits[0];
cmd[5] = feature_bits[1];
if ((sts = xo2->exchange (xo2->user_ctx, cmd, 6, NULL, 0)))
return sts;
usleep (200);
return xo2_status_wait (xo2, 2000);
}
/**
* Read the Feature Row contents.
* This function assembles the command sequence that allows reading back the Feature Row
* contents. The FEATURE bytes and FEATURE_BITS fields are returned in a XO2 specific stucture.
* Uses would be to verify successful XO2ECAcmd_FeatureWrite() programing. Or to read back
* and preserve during an update.
* @param xo2 Open API handle
* @param pXO2 pointer to the XO2 device to access
* @param pFeature pointer to the Feature Row structure that will be loaded with the
* feature row contents.
*
* @return 0 on success, !0 on error
*/
int xo2_feature_read (xo2_handle_t *xo2, uint8_t *feature_row, uint8_t *feature_bits)
{
uint8_t cmd[4];
cmd[0] = 0xE7; // Read Feature opcode
cmd[1] = 0x00; // arg0
cmd[2] = 0x00; // arg1
cmd[3] = 0x00; // arg2
int sts;
if ((sts = xo2->exchange (xo2->user_ctx, cmd, 4, feature_row, 8)))
return sts;
cmd[0] = 0xFB; // Read FEATURE_BITS opcode
cmd[1] = 0x00; // arg0
cmd[2] = 0x00; // arg1
cmd[3] = 0x00; // arg2
return xo2->exchange (xo2->user_ctx, cmd, 4, feature_bits, 2);
}
/**
* Erase, program, and/or verify flash sections of device.
*
* The caller can select to erase, program, and/or verify individual
* sectors of flash of the device. The part is first placed into either
* transparent or offline config mode. Transparent leaves the part
* operating from SRAM while the flash is programmed in background
* mode. However the feature bits cannot be programmed in this mode.
* Offline mode tri-states all pins and suspends user logic while being
* programmed.
*
* @param xo2 Open API handle
* @param pProgJED reference to the converted XO2 JEDEC file data
* @param mode bitmap of what to erase/program and whether to verify or not
*
* @note If programming fails, an attempt is made to close confgiuration mode.
* The user may wish to retry or erase the Cfg and UFM flash sectors
* in an attempt to return the device to a blank state.
*
* @return 0 on success, !0 on error
*/
int xo2_program (xo2_handle_t *xo2, const void *image_ptr, int mode)
{
int sts, reset_device = 0;
unsigned int page;
const machxo2_hdr_t *image = (const machxo2_hdr_t *) image_ptr;
uint8_t *cfg_start = (uint8_t *) (image + 1);
uint8_t *ufm_start = cfg_start + (XO2_PAGE_SIZE * image->cfg_pages);
uint8_t buffer[XO2_PAGE_SIZE], *ptr;
if (mode & XO2_MODE_TRANSPARENT)
{
sts = xo2_mode_set (xo2, XO2_MODE_TRANSPARENT);
// Prevent erasing the Feature Row in Transparent mode. The user logic is running and
// operating per the settings of the current Feature Row. Erasing it can lead to
// instability in the running design. Use Offline mode (design halted) when re-
// programming Feature Row (changing device behavior).
mode &= ~(XO2_MODE_ERASE_FEATROW | XO2_MODE_PROGRAM_FEATROW);
}
else
{
sts = xo2_mode_set (xo2, XO2_MODE_OFFLINE);
mode |= XO2_MODE_ERASE_SRAM;
}
if (sts)
return sts;
do
{
if (mode & (XO2_MODE_ERASE_CONFIG | XO2_MODE_ERASE_UFM | XO2_MODE_ERASE_FEATROW))
{
reset_device = 1;
LOGDBG ("Erasing device");
if ((sts = xo2_flash_erase (xo2, mode)))
break;
}
if (mode & XO2_MODE_PROGRAM_CONFIG)
{
reset_device = 1;
LOGDBG ("Config Sector Program");
if ((sts = xo2_page_reset (xo2, XO2_SECTOR_CFG)))
break;
ptr = cfg_start;
for (page = 0; page < image->cfg_pages; page++)
{
// LOGDBG ("Writing config page: %d", page + 1);
if ((sts = xo2_page_write (xo2, XO2_SECTOR_CFG, ptr, 2000)))
break;
ptr += XO2_PAGE_SIZE;
}
if (page != image->cfg_pages)
break;
}
if (mode & XO2_MODE_VERIFY_CONFIG)
{
LOGDBG ("Config Sector Verify");
if ((sts = xo2_page_reset (xo2, XO2_SECTOR_CFG)))
break;
ptr = cfg_start;
for (page = 0; page < image->cfg_pages; page++)
{
// LOGDBG ("Verify config page: %d", page + 1);
if ((sts = xo2_page_read (xo2, XO2_SECTOR_CFG, buffer)))
return sts;
if (memcmp (buffer, ptr, XO2_PAGE_SIZE))
{
LOGERR("Verify config page %d error", page + 1);
sts = -1;
break;
}
ptr += XO2_PAGE_SIZE;
}
if (page != image->cfg_pages)
break;
}
if ((mode & XO2_MODE_PROGRAM_UFM) && image->ufm_pages)
{
reset_device = 1;
LOGDBG("UFM Sector Program");
if ((sts = xo2_page_reset (xo2, XO2_SECTOR_UFM)))
break;
ptr = ufm_start;
for (page = 0; page < image->ufm_pages; page++)
{
// LOGDBG ("Writing UFM page: %d", page + 1);
if ((sts = xo2_page_write (xo2, XO2_SECTOR_UFM, ptr, 2000)))
break;
ptr += XO2_PAGE_SIZE;
}
if (page != image->ufm_pages)
break;
}
if ((mode & XO2_MODE_VERIFY_UFM) && image->ufm_pages)
{
LOGDBG("UFM Sector Verify");
if ((sts = xo2_page_reset (xo2, XO2_SECTOR_UFM)))
break;
ptr = ufm_start;
for (page = 0; page < image->ufm_pages; page++)
{
// LOGDBG ("Verify UFM page: %d", page + 1);
if ((sts = xo2_page_read (xo2, XO2_SECTOR_UFM, buffer)))
return sts;
if (memcmp (buffer, ptr, XO2_PAGE_SIZE))
{
LOGERR("Verify UFMPage(%d) error", page + 1);
sts = -1;
break;
}
ptr += XO2_PAGE_SIZE;
}
if (page != image->ufm_pages)
break;
}
if (mode & XO2_MODE_PROGRAM_FEATROW)
{
reset_device = 1;
LOGDBG ("Feature row program");
if ((sts = xo2_feature_write (xo2, image->feature_row, image->feature_bits)))
break;
}
if (mode & XO2_MODE_VERIFY_FEATROW)
{
LOGDBG ("Feature row verify");
uint8_t feature_row[8], feature_bits[2];
if ((sts = xo2_feature_read (xo2, feature_row, feature_bits)))
break;
if (memcmp (feature_row, image->feature_row, 8))
{
LOGERR("Feature row verify failure");
sts = -1;
break;
}
if (memcmp (feature_bits, image->feature_bits, 2))
{
LOGERR("Feature bits verify failure");
sts = -1;
break;
}
}
if (!reset_device)
break;
// FINALIZE and CLOSE CONFIG INTERFACE
// Set DONE bit indicating valid design loaded into flash
if ((sts = xo2_done (xo2, 2000)))
break;
// Boot design to user mode (i.e. like hitting PROGRAM pin)
// Refresh command will clear SRAM, load from Flash, set Done, exit config mode.
// Sometimes it needs to be called more than once.
// But eventually it boots and Done goes high.
int loops = 10;
do
{
if (!xo2_refresh (xo2))
return 0;
} while (--loops);
sts = 0;
} while (0);
xo2_config_exit (xo2);
return sts;
}
int xo2_file_info (const void *buffer, uint32_t *user_code, uint32_t *device_id)
{
const machxo2_hdr_t *hdr = (const machxo2_hdr_t *) buffer;
if (user_code)
*user_code = hdr->user_code;
if (device_id)
*device_id = hdr->device_id;
return 0;
}