Skip to content

Commit 803bdfe

Browse files
committed
altera: MAX10: added --flash-sector support with arbitrary binary file
1 parent 10fbb8a commit 803bdfe

File tree

4 files changed

+192
-68
lines changed

4 files changed

+192
-68
lines changed

doc/figs/max10_flash-memory.png

18.5 KB
Loading

doc/vendors/intel.rst

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,32 @@ Supported Boards:
9393
Supported File Types:
9494

9595
* ``svf``
96-
* ``svf``
96+
* ``pof``
9797
* ``bin`` (arbitrary binary files)
9898

99+
Internal Flash Organization
100+
---------------------------
101+
102+
The internal flash is divided into five sections:
103+
104+
- ``UFM1`` and ``UFM0`` for user data
105+
- ``CFM2``, ``CFM1``, and ``CFM0`` for storing one or two bitstreams
106+
107+
.. image:: ../figs/max10_flash-memory.png
108+
:alt: max10 internal flash memory structure
109+
110+
Flash usage depends on the configuration mode. In all modes:
111+
112+
- ``CFM0`` is used to store a bitstream
113+
- ``UFM0`` and ``UFM1`` are available for user data
114+
- The remaining ``CFMx`` sections (``CFM1``, ``CFM2``) can be used for
115+
additional bitstreams or user data
116+
99117
Using ``svf``
100118
-------------
101119

120+
This method is the **simplest** (and slowest) way to load or write a bitstream.
121+
102122
.. note::
103123

104124
This method is required to load a bitstream into *SRAM*.
@@ -107,61 +127,77 @@ Using ``svf``
107127
108128
openFPGALoader [-b boardname] -c cablename the_svf_file.svf
109129
110-
Parameters:
130+
**Parameters:**
111131

112132
* ``boardname``: One of the boards supported by ``openFPGALoader`` (optional).
113133
* ``cablename``: One of the supported cables (see ``--list-cables``).
114134

115135
Using ``pof``
116136
-------------
117137

118-
When writing the bitstream to internal flash, using a ``pof`` file is the fastest approach.
138+
To write a bitstream into the internal flash, using a ``pof`` file is the
139+
**fastest** approach.
119140

120141
.. code-block:: bash
121142
122143
openFPGALoader [-b boardname] [--flash-sector] -c cablename the_pof_file.pof
123144
124-
Parameters:
145+
**Parameters:**
125146

126-
* ``boardname``: One of the boards supported by ``openFPGALoader`` (optional).
147+
* ``boardname``: A board supported by ``openFPGALoader`` (optional).
127148
* ``cablename``: One of the supported cables (see ``--list-cables``).
128-
* ``--flash-sector``: Specifies which internal flash sectors to erase/update instead of modifying the entire flash. One
129-
or more section may be provided, with ``,`` as separator. When this option isn't provided a full internal flash erase/
130-
update is performed
149+
* ``--flash-sector``: Optional. Comma-separated list of sectors to update.
150+
If omitted, the entire flash is erased and reprogrammed.
131151

132152
Accepted Flash Sectors:
133153

134154
* ``UFM0``, ``UFM1``: User Flash Memory sections.
135155
* ``CFM0``, ``CFM1``, ``CFM2``: Configuration Flash Memory sectors.
136156

137-
Example:
157+
**Example:**
138158

139159
.. code-block:: bash
140160
141161
openFPGALoader -c usb-blaster --flash-sector UFM1,CFM0,CFM2 the_pof_file.pof
142162
143-
This command updates ``UFM1``, ``CFM0``, and ``CFM2``, while leaving other sectors unchanged.
163+
This command updates ``UFM1``, ``CFM0``, and ``CFM2``, leaving all other
164+
sectors unchanged.
144165

145166
Using an arbitrary binary file
146167
------------------------------
147168

148-
This command updates only *User Flash Memory* sectors without modifying ``CFMx``. Unlike Altera Quartus, it supports
149-
any binary format without limitations (not limited to a ``.bin``.
169+
Unlike Altera Quartus, it supports any binary format without limitations
170+
(not limited to a ``.bin``).
171+
With this feature, it's not required to provides the file at gateware build
172+
time: it may be updated at any time without gateware modification/rebuild.
150173

151174
.. note:: This approach is useful to updates, for example, a softcore CPU firmware.
152175

176+
**Basic usage:**
177+
153178
.. code-block:: bash
154179
155-
openFPGALoader [-b boardname] -c cablename the_bin_file.bin
180+
openFPGALoader [-b boardname] -c cablename [--offset $OFFSET] the_bin_file.bin
156181
157-
* ``boardname``: One of the boards supported by ``openFPGALoader`` (optional).
182+
* ``boardname``: a boards supported by ``openFPGALoader`` (optional).
158183
* ``cablename``: One of the supported cables (see ``--list-cables``).
159-
160-
Behavior:
161-
162-
``UFM0`` and ``UFM1`` will be erased before writing the binary file.
163-
164-
.. note:: Depending on the internal flash configuration, ``CFM1`` and ``CFM2`` may also store arbitrary data. However, currently, ``openFPGALoader`` only supports writing to ``UFMx``.
184+
* ``$OFFSET``: To start writing ``$OFFSET`` bytes after *User Flash memory*
185+
start address (optional, default: 0x00).
186+
187+
This command erases and writes the contents of ``the_bin_file.bin`` into
188+
``UFM1`` and ``UFM0``. If ``--offset`` is specified, the binary content is
189+
written starting from that offset.
190+
191+
Depending on the max10 configuration mode (see picture), it's possible to
192+
extend *User Flash Memory* area by using `CFM2` and `CFM1`. This is not the
193+
default behavior and user must explictly change this by using
194+
`--flash-sector` argument:
195+
196+
* ``--flash-sector UFMx`` or ``--flash-sector CFMy`` (with x= 1 or 0 and
197+
y = 2 or 1) to specify only one sector
198+
* ``--flash-sector UFM1,UFM0`` is equivalent to the default behavior
199+
* ``--flash-sector UFM1,CFM2`` to erase and update ``UFM1``, ``UFM0``
200+
and ``CFM2`` (equivalent to ``--flash-sector UFM1,UFM0,CFM2``)
165201

166202
Intel/Altera (Old Boards)
167203
=========================

src/altera.cpp

Lines changed: 125 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -341,32 +341,51 @@ const std::map<uint32_t, Altera::max10_mem_t> Altera::max10_memory_map = {
341341
},
342342
};
343343

344-
/* Write an arbitrary file in UFM1 and UFM0
345-
* FIXME: in some mode its also possible to uses CFM2 & CFM1
344+
/* Write an arbitrary file in UFM1, UFM0 by default and also CFM2 and CFM1 if
345+
* requested.
346346
*/
347-
bool Altera::max10_program_ufm(const Altera::max10_mem_t *mem, unsigned int offset)
347+
bool Altera::max10_program_ufm(const Altera::max10_mem_t *mem, uint32_t offset,
348+
uint8_t update_sectors)
348349
{
350+
uint32_t start_addr = 0; // 32bit align
351+
uint32_t end_addr = 0; // 32bit align
352+
uint8_t erase_sectors_mask;
353+
354+
/* check CFM0 is not mentionned */
355+
if (update_sectors & (1 << 4))
356+
std::runtime_error("Error: CFM0 cant't be used to store User Binary");
357+
358+
/* First task: search for the first and the last sector to use */
359+
sectors_mask_start_end_addr(mem, update_sectors,
360+
&start_addr, &end_addr, &erase_sectors_mask);
361+
349362
RawParser _bit(_filename, true);
350363
_bit.parse();
351-
_bit.displayHeader();
364+
if (_verbose)
365+
_bit.displayHeader();
366+
352367
const uint8_t *data = _bit.getData();
353368
const uint32_t length = _bit.getLength() / 8;
354-
const uint32_t base_addr = mem->ufm_addr + offset;
369+
const uint32_t base_addr = start_addr + offset / 4; // 32bit align
370+
const uint32_t flash_len = (end_addr - base_addr) * 4; // Byte align
355371

356-
uint8_t *buff = (uint8_t *)malloc(length);
357-
if (!buff) {
358-
printError("max10_program_ufm: Failed to allocate buffer");
372+
/* check */
373+
if (base_addr > end_addr) { // wrong offset
374+
printError("Error: start offset is out of xFM region");
359375
return false;
360376
}
361-
362-
/* check */
363-
const uint32_t ufmx_len = 4 * (mem->ufm_len[0] + mem->ufm_len[1]);
364-
if (base_addr > length) {
365-
printError("Error: start offset is out of UFM region");
377+
if (flash_len < length) { // too big file
378+
printError("Error: no enough space to write\n");
379+
return false;
380+
}
381+
if (base_addr + (length / 4) > end_addr) {
382+
printError("Error: end address is out of xFM region");
366383
return false;
367384
}
368-
if (base_addr + length > ufmx_len) {
369-
printError("Error: end address is out of UFM region");
385+
386+
uint8_t *buff = (uint8_t *)malloc(length);
387+
if (!buff) {
388+
printError("max10_program_ufm: Failed to allocate buffer");
370389
return false;
371390
}
372391

@@ -377,18 +396,23 @@ bool Altera::max10_program_ufm(const Altera::max10_mem_t *mem, unsigned int offs
377396
}
378397
}
379398

399+
printf("%x %x %x %x\n", update_sectors, erase_sectors_mask,
400+
base_addr, end_addr);
401+
380402
// Start!
381403
max10_flow_enable();
382404

383-
/* Erase UFM1 & UFM0 */
384-
printInfo("Erase UFM ", false);
385-
max10_flow_erase(mem, 0x3);
405+
/* Erase xFM sectors */
406+
printInfo("Erase xFM ", false);
407+
max10_flow_erase(mem, erase_sectors_mask);
386408
printInfo("Done");
387409

388-
/* Program UFM1 & UFM0 */
410+
/* Program xFM */
389411
// Simplify code:
390-
// UFM0 follows UFM1, so we don't need to iterate
391-
printInfo("Write UFM");
412+
// UFM0 follows UFM1,
413+
// CFM2 follow UFM0, etc...
414+
// so we don't need to iterate
415+
printInfo("Write xFM");
392416
writeXFM(buff, base_addr, 0, length / 4);
393417

394418
/* Verify */
@@ -417,8 +441,12 @@ void Altera::max10_program(unsigned int offset)
417441
}
418442
const Altera::max10_mem_t mem = mem_map->second;
419443

444+
/* Check for a full update or only for a subset */
445+
update_sectors = max10_flash_sectors_to_mask(_flash_sectors);
446+
420447
if (_file_extension != "pof") {
421-
max10_program_ufm(&mem, offset);
448+
max10_program_ufm(&mem, offset,
449+
(_flash_sectors.size() == 0) ? 0 : update_sectors);
422450
return;
423451
}
424452

@@ -471,39 +499,17 @@ void Altera::max10_program(unsigned int offset)
471499

472500
// UFM Mapping
473501
ufm_data[1] = _bit.getData("UFM");
474-
ufm_data[0] = &ufm_data[1][mem.ufm_len[0] * 4]; // Just after UFM1 (but size may differs
502+
ufm_data[0] = &ufm_data[1][mem.ufm_len[1] * 4]; // Just after UFM1 (but size may differs
475503

476504
// CFM Mapping
477-
cfm_data[2] = &ufm_data[0][mem.ufm_len[1] * 4]; // First CFM section in FPGA internal flash
505+
cfm_data[2] = &ufm_data[0][mem.ufm_len[0] * 4]; // First CFM section in FPGA internal flash
478506
cfm_data[1] = &cfm_data[2][mem.cfm_len[2] * 4]; // Second CFM section but just after CFM2
479507
cfm_data[0] = &cfm_data[1][mem.cfm_len[1] * 4]; // last CFM section but just after CFM1
480508

481509
// DSM Mapping
482510
const uint8_t *dsm_data = _bit.getData("ICB");
483511
const int dsm_len = _bit.getLength("ICB") / 32; // getLength (bits) dsm_len in 32bits word
484512

485-
/* Check for a full update or only for a subset */
486-
if (_flash_sectors.size() > 0) {
487-
const std::vector<std::string> sectors = splitString(_flash_sectors, ',');
488-
update_sectors = 0;
489-
for (const auto &sector: sectors) {
490-
if (sector == "UFM1")
491-
update_sectors |= (1 << 0);
492-
else if (sector == "UFM0")
493-
update_sectors |= (1 << 1);
494-
else if (sector == "CFM2")
495-
update_sectors |= (1 << 2);
496-
else if (sector == "CFM1")
497-
update_sectors |= (1 << 3);
498-
else if (sector == "CFM0")
499-
update_sectors |= (1 << 4);
500-
else
501-
throw std::runtime_error("Unknown sector " + sector);
502-
}
503-
} else { // full update
504-
update_sectors = 0x1F;
505-
}
506-
507513
// Start!
508514
max10_flow_enable();
509515

@@ -913,6 +919,79 @@ bool Altera::max10_dump()
913919
return true;
914920
}
915921

922+
uint8_t Altera::max10_flash_sectors_to_mask(std::string flash_sectors)
923+
{
924+
uint8_t mask = 0;
925+
926+
if (flash_sectors.size() > 0) {
927+
const std::vector<std::string> sectors = splitString(flash_sectors, ',');
928+
for (const auto &sector: sectors) {
929+
if (sector == "UFM1")
930+
mask |= (1 << 0);
931+
else if (sector == "UFM0")
932+
mask |= (1 << 1);
933+
else if (sector == "CFM2")
934+
mask |= (1 << 2);
935+
else if (sector == "CFM1")
936+
mask |= (1 << 3);
937+
else if (sector == "CFM0")
938+
mask |= (1 << 4);
939+
else
940+
throw std::runtime_error("Unknown sector " + sector);
941+
}
942+
} else { // full update
943+
mask = 0x1F;
944+
}
945+
946+
return mask;
947+
}
948+
949+
bool Altera::sectors_mask_start_end_addr(const Altera::max10_mem_t *mem,
950+
const uint8_t update_sectors, uint32_t *start, uint32_t *end,
951+
uint8_t *sectors_mask)
952+
{
953+
uint32_t saddr = mem->ufm_addr;
954+
uint32_t eaddr = saddr;
955+
uint8_t start_bit = 0, end_bit = 0;
956+
/* For sake of simplicity: create an array with all length aligned
957+
* as it in MAX10 devices
958+
*/
959+
const uint32_t mem_map_length[] = {
960+
mem->ufm_len[1], mem->ufm_len[0],
961+
mem->cfm_len[2], mem->cfm_len[1]
962+
};
963+
964+
if (update_sectors == 0) {
965+
eaddr = mem->ufm_addr + (mem->ufm_len[0] + mem->ufm_len[1]);
966+
*sectors_mask = 0x3;
967+
} else {
968+
/* eaddr start with full memory size */
969+
for (uint8_t i = 0; i < 4; i++)
970+
eaddr += mem_map_length[i];
971+
/* search first bit == 1 and increment start address */
972+
for (uint8_t i = 0; i < 4; i++) {
973+
if (update_sectors & (1 << i)) {
974+
start_bit = i;
975+
break;
976+
}
977+
saddr += mem_map_length[i];
978+
}
979+
980+
/* decrement eaddr until last bit == 1 found */
981+
for (uint8_t i = 3; i >= 0; i--) {
982+
if (update_sectors & (1 << i)) {
983+
end_bit = i + 1;
984+
break;
985+
}
986+
eaddr -= mem_map_length[i];
987+
}
988+
*sectors_mask = ((1 << end_bit) - 1) - ((1 << start_bit) - 1);
989+
}
990+
*start = saddr;
991+
*end = eaddr;
992+
return true;
993+
}
994+
916995
/* SPI interface */
917996

918997
int Altera::spi_put(uint8_t cmd, const uint8_t *tx, uint8_t *rx, uint32_t len)

src/altera.hpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,16 @@ class Altera: public Device, SPIInterface {
9797
/*************************/
9898
/* max10 specific */
9999
/*************************/
100+
public:
100101
struct max10_mem_t;
102+
private:
101103
static const std::map<uint32_t, Altera::max10_mem_t> max10_memory_map;
102104

103105
/* Write a full POF file, or updates UFM with an arbitrary binary file */
104106
void max10_program(uint32_t offset);
105107
/* Write something in UFMx sections after erase */
106-
bool max10_program_ufm(const max10_mem_t *mem, uint32_t offset);
108+
bool max10_program_ufm(const max10_mem_t *mem, uint32_t offset,
109+
uint8_t update_sectors);
107110
/* Write len Word from cfg_data at a specific address */
108111
void writeXFM(const uint8_t *cfg_data, uint32_t base_addr, uint32_t offset, uint32_t len);
109112
/* Compare cfg_data with data stored at base_addr */
@@ -120,7 +123,13 @@ class Altera: public Device, SPIInterface {
120123
bool max10_dsm_verify();
121124
bool max10_dump();
122125
bool max10_read_section(FILE *fd, const uint32_t base_addr, const uint32_t addr);
123-
126+
/* Utils methods */
127+
public:
128+
static uint8_t max10_flash_sectors_to_mask(std::string flash_sectors);
129+
static bool sectors_mask_start_end_addr(const Altera::max10_mem_t *mem,
130+
const uint8_t update_sectors, uint32_t *start, uint32_t *end,
131+
uint8_t *sectors_mask);
132+
private:
124133
/*!
125134
* \brief with intel devices SPI flash direct access is not possible
126135
* so a bridge must be loaded in RAM to access flash

0 commit comments

Comments
 (0)