Compare commits
5 Commits
098cdd466a
...
803bdfecce
| Author | SHA1 | Date |
|---|---|---|
|
|
803bdfecce | |
|
|
10fbb8a153 | |
|
|
775477ba8e | |
|
|
a7b56c3732 | |
|
|
3d7cb14195 |
|
|
@ -152,3 +152,7 @@ OPENFPGALOADER_SOJ_DIR=/somewhere openFPGALoader xxxx
|
|||
|
||||
`OPENFPGALOADER_SOJ_DIR` must point to directory containing **spiOverJtag**
|
||||
bitstreams.
|
||||
|
||||
## Sponsors/Partners
|
||||
|
||||

|
||||
|
|
|
|||
|
|
@ -179,6 +179,12 @@ Lattice:
|
|||
Memory: OK
|
||||
Flash: OK
|
||||
|
||||
- Description: ECP3
|
||||
Model: LFE3-70E
|
||||
URL: https://www.latticesemi.com/Products/FPGAandCPLD/LatticeECP3
|
||||
Memory: OK
|
||||
Flash: TBD
|
||||
|
||||
- Description: ECP5
|
||||
Model:
|
||||
- LFE5U-12
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
|
|
@ -93,12 +93,32 @@ Supported Boards:
|
|||
Supported File Types:
|
||||
|
||||
* ``svf``
|
||||
* ``svf``
|
||||
* ``pof``
|
||||
* ``bin`` (arbitrary binary files)
|
||||
|
||||
Internal Flash Organization
|
||||
---------------------------
|
||||
|
||||
The internal flash is divided into five sections:
|
||||
|
||||
- ``UFM1`` and ``UFM0`` for user data
|
||||
- ``CFM2``, ``CFM1``, and ``CFM0`` for storing one or two bitstreams
|
||||
|
||||
.. image:: ../figs/max10_flash-memory.png
|
||||
:alt: max10 internal flash memory structure
|
||||
|
||||
Flash usage depends on the configuration mode. In all modes:
|
||||
|
||||
- ``CFM0`` is used to store a bitstream
|
||||
- ``UFM0`` and ``UFM1`` are available for user data
|
||||
- The remaining ``CFMx`` sections (``CFM1``, ``CFM2``) can be used for
|
||||
additional bitstreams or user data
|
||||
|
||||
Using ``svf``
|
||||
-------------
|
||||
|
||||
This method is the **simplest** (and slowest) way to load or write a bitstream.
|
||||
|
||||
.. note::
|
||||
|
||||
This method is required to load a bitstream into *SRAM*.
|
||||
|
|
@ -107,7 +127,7 @@ Using ``svf``
|
|||
|
||||
openFPGALoader [-b boardname] -c cablename the_svf_file.svf
|
||||
|
||||
Parameters:
|
||||
**Parameters:**
|
||||
|
||||
* ``boardname``: One of the boards supported by ``openFPGALoader`` (optional).
|
||||
* ``cablename``: One of the supported cables (see ``--list-cables``).
|
||||
|
|
@ -115,53 +135,69 @@ Parameters:
|
|||
Using ``pof``
|
||||
-------------
|
||||
|
||||
When writing the bitstream to internal flash, using a ``pof`` file is the fastest approach.
|
||||
To write a bitstream into the internal flash, using a ``pof`` file is the
|
||||
**fastest** approach.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
openFPGALoader [-b boardname] [--flash-sector] -c cablename the_pof_file.pof
|
||||
|
||||
Parameters:
|
||||
**Parameters:**
|
||||
|
||||
* ``boardname``: One of the boards supported by ``openFPGALoader`` (optional).
|
||||
* ``boardname``: A board supported by ``openFPGALoader`` (optional).
|
||||
* ``cablename``: One of the supported cables (see ``--list-cables``).
|
||||
* ``--flash-sector``: Specifies which internal flash sectors to erase/update instead of modifying the entire flash. One
|
||||
or more section may be provided, with ``,`` as separator. When this option isn't provided a full internal flash erase/
|
||||
update is performed
|
||||
* ``--flash-sector``: Optional. Comma-separated list of sectors to update.
|
||||
If omitted, the entire flash is erased and reprogrammed.
|
||||
|
||||
Accepted Flash Sectors:
|
||||
|
||||
* ``UFM0``, ``UFM1``: User Flash Memory sections.
|
||||
* ``CFM0``, ``CFM1``, ``CFM2``: Configuration Flash Memory sectors.
|
||||
|
||||
Example:
|
||||
**Example:**
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
openFPGALoader -c usb-blaster --flash-sector UFM1,CFM0,CFM2 the_pof_file.pof
|
||||
|
||||
This command updates ``UFM1``, ``CFM0``, and ``CFM2``, while leaving other sectors unchanged.
|
||||
This command updates ``UFM1``, ``CFM0``, and ``CFM2``, leaving all other
|
||||
sectors unchanged.
|
||||
|
||||
Using an arbitrary binary file
|
||||
------------------------------
|
||||
|
||||
This command updates only *User Flash Memory* sectors without modifying ``CFMx``. Unlike Altera Quartus, it supports
|
||||
any binary format without limitations (not limited to a ``.bin``.
|
||||
Unlike Altera Quartus, it supports any binary format without limitations
|
||||
(not limited to a ``.bin``).
|
||||
With this feature, it's not required to provides the file at gateware build
|
||||
time: it may be updated at any time without gateware modification/rebuild.
|
||||
|
||||
.. note:: This approach is useful to updates, for example, a softcore CPU firmware.
|
||||
|
||||
**Basic usage:**
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
openFPGALoader [-b boardname] -c cablename the_bin_file.bin
|
||||
openFPGALoader [-b boardname] -c cablename [--offset $OFFSET] the_bin_file.bin
|
||||
|
||||
* ``boardname``: One of the boards supported by ``openFPGALoader`` (optional).
|
||||
* ``boardname``: a boards supported by ``openFPGALoader`` (optional).
|
||||
* ``cablename``: One of the supported cables (see ``--list-cables``).
|
||||
* ``$OFFSET``: To start writing ``$OFFSET`` bytes after *User Flash memory*
|
||||
start address (optional, default: 0x00).
|
||||
|
||||
Behavior:
|
||||
This command erases and writes the contents of ``the_bin_file.bin`` into
|
||||
``UFM1`` and ``UFM0``. If ``--offset`` is specified, the binary content is
|
||||
written starting from that offset.
|
||||
|
||||
``UFM0`` and ``UFM1`` will be erased before writing the binary file.
|
||||
Depending on the max10 configuration mode (see picture), it's possible to
|
||||
extend *User Flash Memory* area by using `CFM2` and `CFM1`. This is not the
|
||||
default behavior and user must explictly change this by using
|
||||
`--flash-sector` argument:
|
||||
|
||||
.. note:: Depending on the internal flash configuration, ``CFM1`` and ``CFM2`` may also store arbitrary data. However, currently, ``openFPGALoader`` only supports writing to ``UFMx``.
|
||||
* ``--flash-sector UFMx`` or ``--flash-sector CFMy`` (with x= 1 or 0 and
|
||||
y = 2 or 1) to specify only one sector
|
||||
* ``--flash-sector UFM1,UFM0`` is equivalent to the default behavior
|
||||
* ``--flash-sector UFM1,CFM2`` to erase and update ``UFM1``, ``UFM0``
|
||||
and ``CFM2`` (equivalent to ``--flash-sector UFM1,UFM0,CFM2``)
|
||||
|
||||
Intel/Altera (Old Boards)
|
||||
=========================
|
||||
|
|
|
|||
|
|
@ -73,8 +73,8 @@ Bin file load:
|
|||
|
||||
Since it's a direct access to the flash (SPI) the ``-b`` option is required.
|
||||
|
||||
ECP5/Crosslink-NX
|
||||
=================
|
||||
ECP5/ECP3/Certus-NX/CertusPro-NX/Crosslink-NX
|
||||
=============================================
|
||||
|
||||
SRAM
|
||||
----
|
||||
|
|
@ -89,6 +89,10 @@ SRAM
|
|||
SPI Flash
|
||||
---------
|
||||
|
||||
.. note::
|
||||
|
||||
SPI Flash write is not supported for ECP3 family.
|
||||
|
||||
BIT:
|
||||
|
||||
.. code-block:: bash
|
||||
|
|
|
|||
173
src/altera.cpp
173
src/altera.cpp
|
|
@ -341,17 +341,47 @@ const std::map<uint32_t, Altera::max10_mem_t> Altera::max10_memory_map = {
|
|||
},
|
||||
};
|
||||
|
||||
/* Write an arbitrary file in UFM1 and UFM0
|
||||
* FIXME: in some mode its also possible to uses CFM2 & CFM1
|
||||
/* Write an arbitrary file in UFM1, UFM0 by default and also CFM2 and CFM1 if
|
||||
* requested.
|
||||
*/
|
||||
bool Altera::max10_program_ufm(const Altera::max10_mem_t *mem, unsigned int offset)
|
||||
bool Altera::max10_program_ufm(const Altera::max10_mem_t *mem, uint32_t offset,
|
||||
uint8_t update_sectors)
|
||||
{
|
||||
uint32_t start_addr = 0; // 32bit align
|
||||
uint32_t end_addr = 0; // 32bit align
|
||||
uint8_t erase_sectors_mask;
|
||||
|
||||
/* check CFM0 is not mentionned */
|
||||
if (update_sectors & (1 << 4))
|
||||
std::runtime_error("Error: CFM0 cant't be used to store User Binary");
|
||||
|
||||
/* First task: search for the first and the last sector to use */
|
||||
sectors_mask_start_end_addr(mem, update_sectors,
|
||||
&start_addr, &end_addr, &erase_sectors_mask);
|
||||
|
||||
RawParser _bit(_filename, true);
|
||||
_bit.parse();
|
||||
_bit.displayHeader();
|
||||
if (_verbose)
|
||||
_bit.displayHeader();
|
||||
|
||||
const uint8_t *data = _bit.getData();
|
||||
const uint32_t length = _bit.getLength() / 8;
|
||||
const uint32_t base_addr = mem->ufm_addr + offset;
|
||||
const uint32_t base_addr = start_addr + offset / 4; // 32bit align
|
||||
const uint32_t flash_len = (end_addr - base_addr) * 4; // Byte align
|
||||
|
||||
/* check */
|
||||
if (base_addr > end_addr) { // wrong offset
|
||||
printError("Error: start offset is out of xFM region");
|
||||
return false;
|
||||
}
|
||||
if (flash_len < length) { // too big file
|
||||
printError("Error: no enough space to write\n");
|
||||
return false;
|
||||
}
|
||||
if (base_addr + (length / 4) > end_addr) {
|
||||
printError("Error: end address is out of xFM region");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t *buff = (uint8_t *)malloc(length);
|
||||
if (!buff) {
|
||||
|
|
@ -359,17 +389,6 @@ bool Altera::max10_program_ufm(const Altera::max10_mem_t *mem, unsigned int offs
|
|||
return false;
|
||||
}
|
||||
|
||||
/* check */
|
||||
const uint32_t ufmx_len = 4 * (mem->ufm_len[0] + mem->ufm_len[1]);
|
||||
if (base_addr > length) {
|
||||
printError("Error: start offset is out of UFM region");
|
||||
return false;
|
||||
}
|
||||
if (base_addr + length > ufmx_len) {
|
||||
printError("Error: end address is out of UFM region");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* data needs to be re-ordered */
|
||||
for (uint32_t i = 0; i < length; i+=4) {
|
||||
for (int b = 0; b < 4; b++) {
|
||||
|
|
@ -377,18 +396,23 @@ bool Altera::max10_program_ufm(const Altera::max10_mem_t *mem, unsigned int offs
|
|||
}
|
||||
}
|
||||
|
||||
printf("%x %x %x %x\n", update_sectors, erase_sectors_mask,
|
||||
base_addr, end_addr);
|
||||
|
||||
// Start!
|
||||
max10_flow_enable();
|
||||
|
||||
/* Erase UFM1 & UFM0 */
|
||||
printInfo("Erase UFM ", false);
|
||||
max10_flow_erase(mem, 0x3);
|
||||
/* Erase xFM sectors */
|
||||
printInfo("Erase xFM ", false);
|
||||
max10_flow_erase(mem, erase_sectors_mask);
|
||||
printInfo("Done");
|
||||
|
||||
/* Program UFM1 & UFM0 */
|
||||
/* Program xFM */
|
||||
// Simplify code:
|
||||
// UFM0 follows UFM1, so we don't need to iterate
|
||||
printInfo("Write UFM");
|
||||
// UFM0 follows UFM1,
|
||||
// CFM2 follow UFM0, etc...
|
||||
// so we don't need to iterate
|
||||
printInfo("Write xFM");
|
||||
writeXFM(buff, base_addr, 0, length / 4);
|
||||
|
||||
/* Verify */
|
||||
|
|
@ -417,8 +441,12 @@ void Altera::max10_program(unsigned int offset)
|
|||
}
|
||||
const Altera::max10_mem_t mem = mem_map->second;
|
||||
|
||||
/* Check for a full update or only for a subset */
|
||||
update_sectors = max10_flash_sectors_to_mask(_flash_sectors);
|
||||
|
||||
if (_file_extension != "pof") {
|
||||
max10_program_ufm(&mem, offset);
|
||||
max10_program_ufm(&mem, offset,
|
||||
(_flash_sectors.size() == 0) ? 0 : update_sectors);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -471,10 +499,10 @@ void Altera::max10_program(unsigned int offset)
|
|||
|
||||
// UFM Mapping
|
||||
ufm_data[1] = _bit.getData("UFM");
|
||||
ufm_data[0] = &ufm_data[1][mem.ufm_len[0] * 4]; // Just after UFM1 (but size may differs
|
||||
ufm_data[0] = &ufm_data[1][mem.ufm_len[1] * 4]; // Just after UFM1 (but size may differs
|
||||
|
||||
// CFM Mapping
|
||||
cfm_data[2] = &ufm_data[0][mem.ufm_len[1] * 4]; // First CFM section in FPGA internal flash
|
||||
cfm_data[2] = &ufm_data[0][mem.ufm_len[0] * 4]; // First CFM section in FPGA internal flash
|
||||
cfm_data[1] = &cfm_data[2][mem.cfm_len[2] * 4]; // Second CFM section but just after CFM2
|
||||
cfm_data[0] = &cfm_data[1][mem.cfm_len[1] * 4]; // last CFM section but just after CFM1
|
||||
|
||||
|
|
@ -482,28 +510,6 @@ void Altera::max10_program(unsigned int offset)
|
|||
const uint8_t *dsm_data = _bit.getData("ICB");
|
||||
const int dsm_len = _bit.getLength("ICB") / 32; // getLength (bits) dsm_len in 32bits word
|
||||
|
||||
/* Check for a full update or only for a subset */
|
||||
if (_flash_sectors.size() > 0) {
|
||||
const std::vector<std::string> sectors = splitString(_flash_sectors, ',');
|
||||
update_sectors = 0;
|
||||
for (const auto §or: sectors) {
|
||||
if (sector == "UFM1")
|
||||
update_sectors |= (1 << 0);
|
||||
else if (sector == "UFM0")
|
||||
update_sectors |= (1 << 1);
|
||||
else if (sector == "CFM2")
|
||||
update_sectors |= (1 << 2);
|
||||
else if (sector == "CFM1")
|
||||
update_sectors |= (1 << 3);
|
||||
else if (sector == "CFM0")
|
||||
update_sectors |= (1 << 4);
|
||||
else
|
||||
throw std::runtime_error("Unknown sector " + sector);
|
||||
}
|
||||
} else { // full update
|
||||
update_sectors = 0x1F;
|
||||
}
|
||||
|
||||
// Start!
|
||||
max10_flow_enable();
|
||||
|
||||
|
|
@ -913,6 +919,79 @@ bool Altera::max10_dump()
|
|||
return true;
|
||||
}
|
||||
|
||||
uint8_t Altera::max10_flash_sectors_to_mask(std::string flash_sectors)
|
||||
{
|
||||
uint8_t mask = 0;
|
||||
|
||||
if (flash_sectors.size() > 0) {
|
||||
const std::vector<std::string> sectors = splitString(flash_sectors, ',');
|
||||
for (const auto §or: sectors) {
|
||||
if (sector == "UFM1")
|
||||
mask |= (1 << 0);
|
||||
else if (sector == "UFM0")
|
||||
mask |= (1 << 1);
|
||||
else if (sector == "CFM2")
|
||||
mask |= (1 << 2);
|
||||
else if (sector == "CFM1")
|
||||
mask |= (1 << 3);
|
||||
else if (sector == "CFM0")
|
||||
mask |= (1 << 4);
|
||||
else
|
||||
throw std::runtime_error("Unknown sector " + sector);
|
||||
}
|
||||
} else { // full update
|
||||
mask = 0x1F;
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
bool Altera::sectors_mask_start_end_addr(const Altera::max10_mem_t *mem,
|
||||
const uint8_t update_sectors, uint32_t *start, uint32_t *end,
|
||||
uint8_t *sectors_mask)
|
||||
{
|
||||
uint32_t saddr = mem->ufm_addr;
|
||||
uint32_t eaddr = saddr;
|
||||
uint8_t start_bit = 0, end_bit = 0;
|
||||
/* For sake of simplicity: create an array with all length aligned
|
||||
* as it in MAX10 devices
|
||||
*/
|
||||
const uint32_t mem_map_length[] = {
|
||||
mem->ufm_len[1], mem->ufm_len[0],
|
||||
mem->cfm_len[2], mem->cfm_len[1]
|
||||
};
|
||||
|
||||
if (update_sectors == 0) {
|
||||
eaddr = mem->ufm_addr + (mem->ufm_len[0] + mem->ufm_len[1]);
|
||||
*sectors_mask = 0x3;
|
||||
} else {
|
||||
/* eaddr start with full memory size */
|
||||
for (uint8_t i = 0; i < 4; i++)
|
||||
eaddr += mem_map_length[i];
|
||||
/* search first bit == 1 and increment start address */
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
if (update_sectors & (1 << i)) {
|
||||
start_bit = i;
|
||||
break;
|
||||
}
|
||||
saddr += mem_map_length[i];
|
||||
}
|
||||
|
||||
/* decrement eaddr until last bit == 1 found */
|
||||
for (uint8_t i = 3; i >= 0; i--) {
|
||||
if (update_sectors & (1 << i)) {
|
||||
end_bit = i + 1;
|
||||
break;
|
||||
}
|
||||
eaddr -= mem_map_length[i];
|
||||
}
|
||||
*sectors_mask = ((1 << end_bit) - 1) - ((1 << start_bit) - 1);
|
||||
}
|
||||
*start = saddr;
|
||||
*end = eaddr;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* SPI interface */
|
||||
|
||||
int Altera::spi_put(uint8_t cmd, const uint8_t *tx, uint8_t *rx, uint32_t len)
|
||||
|
|
|
|||
|
|
@ -97,13 +97,16 @@ class Altera: public Device, SPIInterface {
|
|||
/*************************/
|
||||
/* max10 specific */
|
||||
/*************************/
|
||||
public:
|
||||
struct max10_mem_t;
|
||||
private:
|
||||
static const std::map<uint32_t, Altera::max10_mem_t> max10_memory_map;
|
||||
|
||||
/* Write a full POF file, or updates UFM with an arbitrary binary file */
|
||||
void max10_program(uint32_t offset);
|
||||
/* Write something in UFMx sections after erase */
|
||||
bool max10_program_ufm(const max10_mem_t *mem, uint32_t offset);
|
||||
bool max10_program_ufm(const max10_mem_t *mem, uint32_t offset,
|
||||
uint8_t update_sectors);
|
||||
/* Write len Word from cfg_data at a specific address */
|
||||
void writeXFM(const uint8_t *cfg_data, uint32_t base_addr, uint32_t offset, uint32_t len);
|
||||
/* Compare cfg_data with data stored at base_addr */
|
||||
|
|
@ -120,7 +123,13 @@ class Altera: public Device, SPIInterface {
|
|||
bool max10_dsm_verify();
|
||||
bool max10_dump();
|
||||
bool max10_read_section(FILE *fd, const uint32_t base_addr, const uint32_t addr);
|
||||
|
||||
/* Utils methods */
|
||||
public:
|
||||
static uint8_t max10_flash_sectors_to_mask(std::string flash_sectors);
|
||||
static bool sectors_mask_start_end_addr(const Altera::max10_mem_t *mem,
|
||||
const uint8_t update_sectors, uint32_t *start, uint32_t *end,
|
||||
uint8_t *sectors_mask);
|
||||
private:
|
||||
/*!
|
||||
* \brief with intel devices SPI flash direct access is not possible
|
||||
* so a bridge must be loaded in RAM to access flash
|
||||
|
|
|
|||
329
src/lattice.cpp
329
src/lattice.cpp
|
|
@ -12,7 +12,9 @@
|
|||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "jtag.hpp"
|
||||
|
|
@ -141,6 +143,17 @@ using namespace std;
|
|||
|
||||
#define PUBKEY_LENGTH_BYTES 64 /* length of the public key (MachXO3D) in bytes */
|
||||
|
||||
/* ECP3 */
|
||||
#define ECP3_LSCC_BITSTREAM_BURST 0x02
|
||||
#define ECP3_ISC_ERASE 0x03 /* ISC_ERASE */
|
||||
#define ECP3_ISC_ENABLE 0x15
|
||||
#define ECP3_IDCODE 0x16
|
||||
#define ECP3_READ_USERCODE 0x17
|
||||
#define ECP3_ISC_PROGRAM_USERCODE 0x1A
|
||||
#define ECP3_RESET_ADDRESS 0x21
|
||||
#define ECP3_LSCC_REFRESH 0x23
|
||||
#define ECP3_READ_STATUS_REGISTER 0x53
|
||||
|
||||
Lattice::Lattice(Jtag *jtag, const string filename, const string &file_type,
|
||||
Device::prog_type_t prg_type, std::string flash_sector, bool verify, int8_t verbose, bool skip_load_bridge, bool skip_reset):
|
||||
Device(jtag, filename, file_type, verify, verbose),
|
||||
|
|
@ -203,6 +216,8 @@ Lattice::Lattice(Jtag *jtag, const string filename, const string &file_type,
|
|||
printError("Unknown flash sector");
|
||||
throw std::exception();
|
||||
}
|
||||
} else if (family == "ECP3") {
|
||||
_fpga_family = ECP3_FAMILY;
|
||||
} else if (family == "ECP5") {
|
||||
_fpga_family = ECP5_FAMILY;
|
||||
} else if (family == "CrosslinkNX") {
|
||||
|
|
@ -270,6 +285,7 @@ bool Lattice::checkStatus(uint64_t val, uint64_t mask)
|
|||
bool Lattice::program_mem()
|
||||
{
|
||||
bool err;
|
||||
|
||||
LatticeBitParser _bit(_filename, false, _verbose);
|
||||
|
||||
printInfo("Open file: ", false);
|
||||
|
|
@ -308,14 +324,21 @@ bool Lattice::program_mem()
|
|||
* PRELOAD/SAMPLE 0x1C
|
||||
* For NEXUS family fpgas, the Bscan register is 362 bits long or
|
||||
* 45.25 bytes => 46 bytes
|
||||
* For ECP3 family fpgas, the Bscan register is 1077 bits long or
|
||||
* 134.62 bytes => 135 bytes
|
||||
*/
|
||||
uint8_t tx_buf[46];
|
||||
memset(tx_buf, 0xff, 46);
|
||||
uint8_t tx_buf[135];
|
||||
memset(tx_buf, 0xff, 135);
|
||||
int tx_len;
|
||||
if(_fpga_family == NEXUS_FAMILY){
|
||||
tx_len = 46;
|
||||
} else {
|
||||
tx_len = 26;
|
||||
switch (_fpga_family) {
|
||||
case NEXUS_FAMILY:
|
||||
tx_len = 46;
|
||||
break;
|
||||
case ECP3_FAMILY:
|
||||
tx_len = 135;
|
||||
break;
|
||||
default:
|
||||
tx_len = 26;
|
||||
}
|
||||
wr_rd(PRELOAD_SAMPLE, tx_buf, tx_len, NULL, 0);
|
||||
|
||||
|
|
@ -326,32 +349,41 @@ bool Lattice::program_mem()
|
|||
*/
|
||||
/*flag to understand if we refreshed or not*/
|
||||
bool was_refreshed;
|
||||
if (_fpga_family == NEXUS_FAMILY) {
|
||||
if (!checkStatus(0, REG_STATUS_PRV_CNF_CHK_MASK)) {
|
||||
printInfo("Error in previous bitstream execution. REFRESH: ", false);
|
||||
wr_rd(REFRESH, NULL, 0, NULL, 0);
|
||||
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
/* In Lattice FPGA-TN-02099 document in a note it's reported that there
|
||||
is a delay time after LSC_REFRESH where "Duration could be in
|
||||
seconds". Without whis waiting time, busy flag can't be cleared.*/
|
||||
sleep(5);
|
||||
was_refreshed = true;
|
||||
switch (_fpga_family) {
|
||||
case NEXUS_FAMILY:
|
||||
if (!checkStatus(0, REG_STATUS_PRV_CNF_CHK_MASK)) {
|
||||
printError("FAIL");
|
||||
displayReadReg(readStatusReg());
|
||||
return false;
|
||||
printInfo("Error in previous bitstream execution. REFRESH: ", false);
|
||||
wr_rd(REFRESH, NULL, 0, NULL, 0);
|
||||
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
/* In Lattice FPGA-TN-02099 document in a note it's reported that there
|
||||
is a delay time after LSC_REFRESH where "Duration could be in
|
||||
seconds". Without whis waiting time, busy flag can't be cleared.*/
|
||||
sleep(5);
|
||||
was_refreshed = true;
|
||||
if (!checkStatus(0, REG_STATUS_PRV_CNF_CHK_MASK)) {
|
||||
printError("FAIL");
|
||||
displayReadReg(readStatusReg());
|
||||
return false;
|
||||
} else {
|
||||
printSuccess("DONE");
|
||||
}
|
||||
} else {
|
||||
printSuccess("DONE");
|
||||
was_refreshed = false;
|
||||
if (_verbose){
|
||||
printInfo("No error in previous bitstream execution.", true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
case ECP3_FAMILY:
|
||||
wr_rd(ECP3_LSCC_REFRESH, NULL, 0, NULL, 0);
|
||||
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(5, 1);
|
||||
usleep_ecp3(500000); // 0.5s
|
||||
was_refreshed = false;
|
||||
break;
|
||||
default:
|
||||
was_refreshed = false;
|
||||
if (_verbose){
|
||||
printInfo("No error in previous bitstream execution.", true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
was_refreshed = false;
|
||||
}
|
||||
|
||||
/* ISC Enable 0xC6 */
|
||||
|
|
@ -387,6 +419,11 @@ bool Lattice::program_mem()
|
|||
printSuccess("DONE");
|
||||
}
|
||||
|
||||
if (_fpga_family == ECP3_FAMILY) {
|
||||
if (!write_userCode(0xffffffff))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ISC ERASE
|
||||
* For Nexus family (from svf file): 1 byte to tx 0x00
|
||||
*/
|
||||
|
|
@ -404,15 +441,43 @@ bool Lattice::program_mem()
|
|||
}
|
||||
|
||||
/* LSC_INIT_ADDRESS */
|
||||
wr_rd(0x46, NULL, 0, NULL, 0);
|
||||
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
if (_fpga_family == ECP3_FAMILY) {
|
||||
wr_rd(ECP3_RESET_ADDRESS, NULL, 0, NULL, 0);
|
||||
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(5);
|
||||
usleep_ecp3(2000); // 2ms
|
||||
|
||||
/* read user code (User Code register must have all bit set cleared) */
|
||||
const uint32_t dummy = 0xffffffff;
|
||||
uint32_t rx = 0;
|
||||
wr_rd(ECP3_READ_USERCODE, (uint8_t *)&dummy, 4, (uint8_t *)&rx, 4);
|
||||
if (rx != 0x00000000) {
|
||||
char message[256];
|
||||
snprintf(message, 256, "failed: 0x%08x instead of 0xffffffff", rx);
|
||||
printError(message);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
wr_rd(0x46, NULL, 0, NULL, 0);
|
||||
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
}
|
||||
|
||||
const uint8_t *data = _bit.getData();
|
||||
int length = _bit.getLength()/8;
|
||||
wr_rd(0x7A, NULL, 0, NULL, 0);
|
||||
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(2);
|
||||
if (_fpga_family == ECP3_FAMILY) {
|
||||
/* Reset Address */
|
||||
wr_rd(ECP3_RESET_ADDRESS, NULL, 0, NULL, 0);
|
||||
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(5, 1);
|
||||
usleep_ecp3(2000); // 2ms
|
||||
|
||||
wr_rd(ECP3_LSCC_BITSTREAM_BURST, NULL, 0, NULL, 0);
|
||||
} else {
|
||||
wr_rd(0x7A, NULL, 0, NULL, 0);
|
||||
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(2);
|
||||
}
|
||||
|
||||
uint8_t tmp[1024];
|
||||
int size = 1024;
|
||||
|
|
@ -431,27 +496,45 @@ bool Lattice::program_mem()
|
|||
for (int ii = 0; ii < size; ii++)
|
||||
tmp[ii] = ConfigBitstreamParser::reverseByte(data[i+ii]);
|
||||
|
||||
_jtag->shiftDR(tmp, NULL, size*8, next_state);
|
||||
_jtag->shiftDR(tmp, NULL, size * 8, next_state);
|
||||
}
|
||||
|
||||
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
|
||||
uint32_t status_mask;
|
||||
if (_fpga_family == MACHXO3D_FAMILY)
|
||||
status_mask = REG_STATUS_MACHXO3D_CNF_CHK_MASK;
|
||||
else
|
||||
status_mask = REG_STATUS_CNF_CHK_MASK;
|
||||
if (_fpga_family == ECP3_FAMILY) {
|
||||
_jtag->toggleClk(256, 1);
|
||||
usleep_ecp3(2000);
|
||||
|
||||
if (checkStatus(0, status_mask)) {
|
||||
/* Verifiy User Code register */
|
||||
uint32_t rx = userCode();
|
||||
if (rx != 0x00000000) {
|
||||
progress.fail();
|
||||
char message[256];
|
||||
snprintf(message, 256, "failed: 0x%08x instead of 0x00000000", rx);
|
||||
printError(message);
|
||||
return false;
|
||||
}
|
||||
progress.done();
|
||||
} else {
|
||||
progress.fail();
|
||||
displayReadReg(readStatusReg());
|
||||
return false;
|
||||
}
|
||||
_jtag->toggleClk(1000);
|
||||
|
||||
wr_rd(0xff, NULL, 0, NULL, 0);
|
||||
uint32_t status_mask;
|
||||
if (_fpga_family == MACHXO3D_FAMILY)
|
||||
status_mask = REG_STATUS_MACHXO3D_CNF_CHK_MASK;
|
||||
else
|
||||
status_mask = REG_STATUS_CNF_CHK_MASK;
|
||||
|
||||
if (checkStatus(0, status_mask)) {
|
||||
progress.done();
|
||||
} else {
|
||||
progress.fail();
|
||||
displayReadReg(readStatusReg());
|
||||
return false;
|
||||
}
|
||||
|
||||
/* bypass */
|
||||
wr_rd(0xff, NULL, 0, NULL, 0);
|
||||
}
|
||||
|
||||
if (_verbose)
|
||||
printf("userCode: %08x\n", userCode());
|
||||
|
|
@ -468,6 +551,25 @@ bool Lattice::program_mem()
|
|||
printSuccess("DONE");
|
||||
}
|
||||
|
||||
if (_fpga_family == ECP3_FAMILY) {
|
||||
/* Bypass */
|
||||
wr_rd(0xff, NULL, 0, NULL, 0);
|
||||
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(100, 1);
|
||||
usleep_ecp3(1000);
|
||||
|
||||
// Verify STATUS Register
|
||||
uint64_t status = readStatusReg();
|
||||
_jtag->toggleClk(5, 1);
|
||||
if ((status & 0x60007) != 0x20000) {
|
||||
char message[256];
|
||||
snprintf(message, 256, "Programming failed: status 0x%" PRIx64 "instead of 0x20000", status);
|
||||
printError(message);
|
||||
displayReadReg(status);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (_verbose)
|
||||
displayReadReg(readStatusReg());
|
||||
|
||||
|
|
@ -898,6 +1000,14 @@ void Lattice::program(unsigned int offset, bool unprotect_flash)
|
|||
*/
|
||||
bool Lattice::EnableISC(uint8_t flash_mode)
|
||||
{
|
||||
if (_fpga_family == ECP3_FAMILY) {
|
||||
wr_rd(ECP3_ISC_ENABLE, NULL, 0, NULL, 0);
|
||||
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(5, 1);
|
||||
usleep_ecp3(20000); // 0.20s
|
||||
return true;
|
||||
}
|
||||
|
||||
wr_rd(ISC_ENABLE, &flash_mode, 1, NULL, 0);
|
||||
|
||||
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||
|
|
@ -911,6 +1021,13 @@ bool Lattice::EnableISC(uint8_t flash_mode)
|
|||
|
||||
bool Lattice::DisableISC()
|
||||
{
|
||||
if (_fpga_family == ECP3_FAMILY) {
|
||||
/** Shift in ISC DISABLE instruction */
|
||||
_jtag->shiftIR(0x1E, 8, Jtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(5, 1);
|
||||
usleep_ecp3(200000);
|
||||
return true;
|
||||
}
|
||||
wr_rd(ISC_DISABLE, NULL, 0, NULL, 0);
|
||||
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
|
|
@ -942,7 +1059,8 @@ bool Lattice::DisableCfg()
|
|||
uint32_t Lattice::idCode()
|
||||
{
|
||||
uint8_t device_id[4];
|
||||
wr_rd(READ_DEVICE_ID_CODE, NULL, 0, device_id, 4);
|
||||
const uint8_t idcode = (_fpga_family == ECP3_FAMILY) ? ECP3_IDCODE : READ_DEVICE_ID_CODE;
|
||||
wr_rd(idcode, NULL, 0, device_id, 4);
|
||||
return device_id[3] << 24 |
|
||||
device_id[2] << 16 |
|
||||
device_id[1] << 8 |
|
||||
|
|
@ -952,13 +1070,40 @@ uint32_t Lattice::idCode()
|
|||
int Lattice::userCode()
|
||||
{
|
||||
uint8_t usercode[4];
|
||||
wr_rd(0xC0, NULL, 0, usercode, 4);
|
||||
wr_rd((_fpga_family == ECP3_FAMILY) ? ECP3_READ_USERCODE : 0xC0,
|
||||
NULL, 0, usercode, 4);
|
||||
return usercode[3] << 24 |
|
||||
usercode[2] << 16 |
|
||||
usercode[1] << 8 |
|
||||
usercode[0];
|
||||
}
|
||||
|
||||
bool Lattice::write_userCode(uint32_t usercode)
|
||||
{
|
||||
// ! Shift in ISC PROGRAM USERCODE(0x1A) instruction
|
||||
// SIR 8 TDI (1A);
|
||||
// SDR 32 TDI (FFFFFFFF);
|
||||
// RUNTEST IDLE 5 TCK 2,00E-03 SEC;
|
||||
// ! Shift in READ USERCODE(0x17) instruction
|
||||
// SIR 8 TDI (17);
|
||||
// SDR 32 TDI (FFFFFFFF)
|
||||
// TDO (FFFFFFFF);
|
||||
wr_rd(ECP3_ISC_PROGRAM_USERCODE, (uint8_t *)&usercode, 4, NULL, 0);
|
||||
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(5, 1);
|
||||
usleep_ecp3(2000); // 2ms
|
||||
|
||||
uint32_t rd_usercode = userCode();
|
||||
if (rd_usercode != usercode) {
|
||||
char message[256];
|
||||
snprintf(message, 256, "failed: 0x%08x instead of 0x%08x",
|
||||
rd_usercode, usercode);
|
||||
printError(message);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Lattice::checkID()
|
||||
{
|
||||
printf("\n");
|
||||
|
|
@ -1005,12 +1150,13 @@ uint64_t Lattice::readStatusReg()
|
|||
uint64_t reg;
|
||||
uint8_t rx[8], tx[8];
|
||||
|
||||
int reg_len = get_statusreg_size() / 8;
|
||||
const int reg_len = get_statusreg_size() / 8;
|
||||
const uint8_t regcode = (_fpga_family == ECP3_FAMILY) ? ECP3_READ_STATUS_REGISTER : READ_STATUS_REGISTER;
|
||||
|
||||
/* valgrind warn */
|
||||
memset(tx, 0, 8);
|
||||
memset(rx, 0, 8);
|
||||
wr_rd(READ_STATUS_REGISTER, tx, reg_len, rx, reg_len);
|
||||
wr_rd(regcode, tx, reg_len, rx, reg_len);
|
||||
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(1000);
|
||||
reg = (uint64_t) rx[7] << 56 | (uint64_t) rx[6] << 48 | (uint64_t) rx[5] << 40 | (uint64_t) rx[4] << 32 | rx[3] << 24 | rx[2] << 16 | rx[1] << 8 | rx[0];
|
||||
|
|
@ -1052,8 +1198,75 @@ bool Lattice::wr_rd(uint8_t cmd,
|
|||
return true;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
std::string description;
|
||||
uint8_t offset;
|
||||
uint8_t size;
|
||||
std::map<int, std::string> reg_cnt;
|
||||
} reg_struct_t;
|
||||
|
||||
#define REG_ENTRY(_description, _offset, _size, ...) \
|
||||
{_description, _offset, _size, {__VA_ARGS__}}
|
||||
|
||||
static const std::map<std::string, std::list<reg_struct_t>> reg_content = {
|
||||
{"StatusRegister", std::list<reg_struct_t>{
|
||||
REG_ENTRY("CRC Error", 0, 1, {0, "OK"}, {1, "KO"}),
|
||||
REG_ENTRY("ID Verify failed", 1, 1, {0, "No"}, {1, "Yes"}),
|
||||
REG_ENTRY("Invalid Command", 2, 1, {0, "No"}, {1, "Yes"}),
|
||||
REG_ENTRY("SPIm Mode Error", 3, 1, {0, "No"}, {1, "Yes"}),
|
||||
REG_ENTRY("Device locked", 4, 1, {0, "No"}, {1, "Yes"}),
|
||||
REG_ENTRY("Key Fire", 5, 1, {0, "No"}, {1, "Yes"}),
|
||||
REG_ENTRY("Alignement Preamble", 6, 1, {0, "No"}, {1, "Yes"}),
|
||||
REG_ENTRY("Encryption Preamble", 7, 1, {0, "No"}, {1, "Yes"}),
|
||||
REG_ENTRY("Std Preamble", 8, 1, {0, "No"}, {1, "Yes"}),
|
||||
REG_ENTRY("HFC", 9, 1, {0, "No"}, {1, "Yes"}),
|
||||
REG_ENTRY("Memory Cleared", 15, 1, {0, "No"}, {1, "Yes"}),
|
||||
REG_ENTRY("Device Secured", 16, 1, {0, "No"}, {1, "Yes"}),
|
||||
REG_ENTRY("Device Configured", 17, 1, {0, "No"}, {1, "Yes"}),
|
||||
REG_ENTRY("Non-JTAG Mode", 18, 1, {0, "No"}, {1, "Yes"}),
|
||||
REG_ENTRY("ReadBack Mode", 19, 1, {0, "No"}, {1, "Yes"}),
|
||||
REG_ENTRY("Bypass Mode", 20, 1, {0, "No"}, {1, "Yes"}),
|
||||
REG_ENTRY("Flow Trough", 21, 1, {0, "No"}, {1, "Yes"}),
|
||||
REG_ENTRY("Configuration Mode", 22, 1, {0, "No"}, {1, "Yes"}),
|
||||
REG_ENTRY("Transparent Mode", 23, 1, {0, "No"}, {1, "Yes"}),
|
||||
}},
|
||||
};
|
||||
|
||||
void Lattice::displayReadReg(uint64_t dev)
|
||||
{
|
||||
if (_fpga_family == ECP3_FAMILY) {
|
||||
auto reg = reg_content.find("StatusRegister");
|
||||
if (reg == reg_content.end()) {
|
||||
printError("Unknown register StatusRegister");
|
||||
return;
|
||||
}
|
||||
|
||||
std::stringstream raw_val;
|
||||
raw_val << "0x" << std::hex << dev;
|
||||
printSuccess("Register raw value: " + raw_val.str());
|
||||
|
||||
const std::list<reg_struct_t> regs = reg->second;
|
||||
for (reg_struct_t r: regs) {
|
||||
uint8_t offset = r.offset;
|
||||
uint8_t size = r.size;
|
||||
uint32_t mask = (1 << size) - 1;
|
||||
uint32_t val = (dev >> offset) & mask;
|
||||
std::stringstream ss, desc;
|
||||
desc << r.description;
|
||||
ss << std::setw(20) << std::left << r.description;
|
||||
if (r.reg_cnt.size() != 0) {
|
||||
ss << r.reg_cnt[val];
|
||||
} else {
|
||||
std::stringstream hex_val;
|
||||
hex_val << "0x" << std::hex << val;
|
||||
ss << hex_val.str();
|
||||
}
|
||||
|
||||
printInfo(ss.str());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t err;
|
||||
printf("displayReadReg\n");
|
||||
if (dev & 1<<0) printf("\tTRAN Mode\n");
|
||||
|
|
@ -1247,6 +1460,12 @@ bool Lattice::flashErase(uint32_t mask)
|
|||
(uint8_t)((mask >> 16) & 0xff)
|
||||
};
|
||||
wr_rd(FLASH_ERASE, tx, 2, NULL, 0);
|
||||
} else if (_fpga_family == ECP3_FAMILY) {
|
||||
wr_rd(ECP3_ISC_ERASE, NULL, 0, NULL, 0);
|
||||
_jtag->set_state(Jtag::RUN_TEST_IDLE);
|
||||
_jtag->toggleClk(5, 1);
|
||||
usleep_ecp3(2000000); // 2s
|
||||
return true;
|
||||
} else {
|
||||
uint8_t tx[1] = {(uint8_t)(mask & 0xff)};
|
||||
wr_rd(FLASH_ERASE, tx, 1, NULL, 0);
|
||||
|
|
@ -1540,6 +1759,14 @@ int Lattice::spi_wait(uint8_t cmd, uint8_t mask, uint8_t cond,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*************************** MODS FOR ECP3 ************************************/
|
||||
|
||||
void Lattice::usleep_ecp3(uint64_t us_time)
|
||||
{
|
||||
const uint32_t clk_period = 1e9/static_cast<float>(_jtag->getClkFreq());
|
||||
const uint32_t clk_len = (us_time * 1000) / clk_period;
|
||||
_jtag->toggleClk(clk_len, 0); // 0.5s
|
||||
}
|
||||
|
||||
/*************************** MODS FOR MacXO3D *********************************/
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ class Lattice: public Device, SPIInterface {
|
|||
int8_t verbose, bool skip_load_bridge, bool skip_reset);
|
||||
uint32_t idCode() override;
|
||||
int userCode();
|
||||
bool write_userCode(uint32_t usercode);
|
||||
void reset() override;
|
||||
void program(unsigned int offset, bool unprotect_flash) override;
|
||||
bool program_mem();
|
||||
|
|
@ -74,6 +75,7 @@ class Lattice: public Device, SPIInterface {
|
|||
MACHXO3D_FAMILY = 2,
|
||||
ECP5_FAMILY = 3,
|
||||
NEXUS_FAMILY = 4,
|
||||
ECP3_FAMILY,
|
||||
UNKNOWN_FAMILY = 999
|
||||
};
|
||||
|
||||
|
|
@ -122,6 +124,9 @@ class Lattice: public Device, SPIInterface {
|
|||
/* test */
|
||||
bool checkID();
|
||||
|
||||
/************************* MODS for ECP3 ******************************/
|
||||
void usleep_ecp3(uint64_t us_time);
|
||||
|
||||
/*********************** MODS FOR MacXO3D *****************************/
|
||||
enum lattice_flash_sector_t {
|
||||
LATTICE_FLASH_UNDEFINED = 0,
|
||||
|
|
|
|||
|
|
@ -296,6 +296,9 @@ static std::map <uint32_t, fpga_model> fpga_list = {
|
|||
|
||||
{0x012e3043, {"lattice", "MachXO3D", "LCMX03D-9400HC", 8}},
|
||||
|
||||
/* Lattice ECP3 */
|
||||
{0x01014043, {"lattice", "ECP3", "LFE3-70E", 8}},
|
||||
|
||||
/* Lattice ECP5 */
|
||||
{0x21111043, {"lattice", "ECP5", "LFE5U-12", 8}},
|
||||
{0x41111043, {"lattice", "ECP5", "LFE5U-25", 8}},
|
||||
|
|
|
|||
Loading…
Reference in New Issue