altera: MAX10: added --flash-sector support with arbitrary binary file
This commit is contained in:
parent
10fbb8a153
commit
803bdfecce
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
|
|
@ -93,12 +93,32 @@ Supported Boards:
|
||||||
Supported File Types:
|
Supported File Types:
|
||||||
|
|
||||||
* ``svf``
|
* ``svf``
|
||||||
* ``svf``
|
* ``pof``
|
||||||
* ``bin`` (arbitrary binary files)
|
* ``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``
|
Using ``svf``
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
This method is the **simplest** (and slowest) way to load or write a bitstream.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
This method is required to load a bitstream into *SRAM*.
|
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
|
openFPGALoader [-b boardname] -c cablename the_svf_file.svf
|
||||||
|
|
||||||
Parameters:
|
**Parameters:**
|
||||||
|
|
||||||
* ``boardname``: One of the boards supported by ``openFPGALoader`` (optional).
|
* ``boardname``: One of the boards supported by ``openFPGALoader`` (optional).
|
||||||
* ``cablename``: One of the supported cables (see ``--list-cables``).
|
* ``cablename``: One of the supported cables (see ``--list-cables``).
|
||||||
|
|
@ -115,53 +135,69 @@ Parameters:
|
||||||
Using ``pof``
|
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
|
.. code-block:: bash
|
||||||
|
|
||||||
openFPGALoader [-b boardname] [--flash-sector] -c cablename the_pof_file.pof
|
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``).
|
* ``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
|
* ``--flash-sector``: Optional. Comma-separated list of sectors to update.
|
||||||
or more section may be provided, with ``,`` as separator. When this option isn't provided a full internal flash erase/
|
If omitted, the entire flash is erased and reprogrammed.
|
||||||
update is performed
|
|
||||||
|
|
||||||
Accepted Flash Sectors:
|
Accepted Flash Sectors:
|
||||||
|
|
||||||
* ``UFM0``, ``UFM1``: User Flash Memory sections.
|
* ``UFM0``, ``UFM1``: User Flash Memory sections.
|
||||||
* ``CFM0``, ``CFM1``, ``CFM2``: Configuration Flash Memory sectors.
|
* ``CFM0``, ``CFM1``, ``CFM2``: Configuration Flash Memory sectors.
|
||||||
|
|
||||||
Example:
|
**Example:**
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
openFPGALoader -c usb-blaster --flash-sector UFM1,CFM0,CFM2 the_pof_file.pof
|
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
|
Using an arbitrary binary file
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
This command updates only *User Flash Memory* sectors without modifying ``CFMx``. Unlike Altera Quartus, it supports
|
Unlike Altera Quartus, it supports any binary format without limitations
|
||||||
any binary format without limitations (not limited to a ``.bin``.
|
(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.
|
.. note:: This approach is useful to updates, for example, a softcore CPU firmware.
|
||||||
|
|
||||||
|
**Basic usage:**
|
||||||
|
|
||||||
.. code-block:: bash
|
.. 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``).
|
* ``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)
|
Intel/Altera (Old Boards)
|
||||||
=========================
|
=========================
|
||||||
|
|
|
||||||
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
|
/* Write an arbitrary file in UFM1, UFM0 by default and also CFM2 and CFM1 if
|
||||||
* FIXME: in some mode its also possible to uses CFM2 & CFM1
|
* 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);
|
RawParser _bit(_filename, true);
|
||||||
_bit.parse();
|
_bit.parse();
|
||||||
_bit.displayHeader();
|
if (_verbose)
|
||||||
|
_bit.displayHeader();
|
||||||
|
|
||||||
const uint8_t *data = _bit.getData();
|
const uint8_t *data = _bit.getData();
|
||||||
const uint32_t length = _bit.getLength() / 8;
|
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);
|
uint8_t *buff = (uint8_t *)malloc(length);
|
||||||
if (!buff) {
|
if (!buff) {
|
||||||
|
|
@ -359,17 +389,6 @@ bool Altera::max10_program_ufm(const Altera::max10_mem_t *mem, unsigned int offs
|
||||||
return false;
|
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 */
|
/* data needs to be re-ordered */
|
||||||
for (uint32_t i = 0; i < length; i+=4) {
|
for (uint32_t i = 0; i < length; i+=4) {
|
||||||
for (int b = 0; b < 4; b++) {
|
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!
|
// Start!
|
||||||
max10_flow_enable();
|
max10_flow_enable();
|
||||||
|
|
||||||
/* Erase UFM1 & UFM0 */
|
/* Erase xFM sectors */
|
||||||
printInfo("Erase UFM ", false);
|
printInfo("Erase xFM ", false);
|
||||||
max10_flow_erase(mem, 0x3);
|
max10_flow_erase(mem, erase_sectors_mask);
|
||||||
printInfo("Done");
|
printInfo("Done");
|
||||||
|
|
||||||
/* Program UFM1 & UFM0 */
|
/* Program xFM */
|
||||||
// Simplify code:
|
// Simplify code:
|
||||||
// UFM0 follows UFM1, so we don't need to iterate
|
// UFM0 follows UFM1,
|
||||||
printInfo("Write UFM");
|
// CFM2 follow UFM0, etc...
|
||||||
|
// so we don't need to iterate
|
||||||
|
printInfo("Write xFM");
|
||||||
writeXFM(buff, base_addr, 0, length / 4);
|
writeXFM(buff, base_addr, 0, length / 4);
|
||||||
|
|
||||||
/* Verify */
|
/* Verify */
|
||||||
|
|
@ -417,8 +441,12 @@ void Altera::max10_program(unsigned int offset)
|
||||||
}
|
}
|
||||||
const Altera::max10_mem_t mem = mem_map->second;
|
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") {
|
if (_file_extension != "pof") {
|
||||||
max10_program_ufm(&mem, offset);
|
max10_program_ufm(&mem, offset,
|
||||||
|
(_flash_sectors.size() == 0) ? 0 : update_sectors);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -471,10 +499,10 @@ void Altera::max10_program(unsigned int offset)
|
||||||
|
|
||||||
// UFM Mapping
|
// UFM Mapping
|
||||||
ufm_data[1] = _bit.getData("UFM");
|
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 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[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
|
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 uint8_t *dsm_data = _bit.getData("ICB");
|
||||||
const int dsm_len = _bit.getLength("ICB") / 32; // getLength (bits) dsm_len in 32bits word
|
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!
|
// Start!
|
||||||
max10_flow_enable();
|
max10_flow_enable();
|
||||||
|
|
||||||
|
|
@ -913,6 +919,79 @@ bool Altera::max10_dump()
|
||||||
return true;
|
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 */
|
/* SPI interface */
|
||||||
|
|
||||||
int Altera::spi_put(uint8_t cmd, const uint8_t *tx, uint8_t *rx, uint32_t len)
|
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 */
|
/* max10 specific */
|
||||||
/*************************/
|
/*************************/
|
||||||
|
public:
|
||||||
struct max10_mem_t;
|
struct max10_mem_t;
|
||||||
|
private:
|
||||||
static const std::map<uint32_t, Altera::max10_mem_t> max10_memory_map;
|
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 */
|
/* Write a full POF file, or updates UFM with an arbitrary binary file */
|
||||||
void max10_program(uint32_t offset);
|
void max10_program(uint32_t offset);
|
||||||
/* Write something in UFMx sections after erase */
|
/* 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 */
|
/* 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);
|
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 */
|
/* Compare cfg_data with data stored at base_addr */
|
||||||
|
|
@ -120,7 +123,13 @@ class Altera: public Device, SPIInterface {
|
||||||
bool max10_dsm_verify();
|
bool max10_dsm_verify();
|
||||||
bool max10_dump();
|
bool max10_dump();
|
||||||
bool max10_read_section(FILE *fd, const uint32_t base_addr, const uint32_t addr);
|
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
|
* \brief with intel devices SPI flash direct access is not possible
|
||||||
* so a bridge must be loaded in RAM to access flash
|
* so a bridge must be loaded in RAM to access flash
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue