altera: added sectors erase and UFM write

- updated max10_mem_t to have sectors erase addresses
- updated max10_flow_erase to perform a full erase or sectors per
  sectors
- added a dedicated method to perform UFM erase and write without full
  internal flash erase and with arbitrary file instead of POF
This commit is contained in:
Gwenhael Goavec-Merou 2025-03-15 07:41:57 +01:00
parent 703af08c91
commit 1abb1c2453
2 changed files with 138 additions and 35 deletions

View File

@ -38,6 +38,15 @@ Altera::Altera(Jtag *jtag, const std::string &filename,
_device_package(device_package), _spiOverJtagPath(spiOverJtagPath), _device_package(device_package), _spiOverJtagPath(spiOverJtagPath),
_vir_addr(0x1000), _vir_length(14), _clk_period(1) _vir_addr(0x1000), _vir_length(14), _clk_period(1)
{ {
/* check device family */
_idcode = _jtag->get_target_device_id();
string family = fpga_list[_idcode].family;
if (family == "MAX 10") {
_fpga_family = MAX10_FAMILY;
} else {
_fpga_family = CYCLONE_MISC; // FIXME
}
if (prg_type == Device::RD_FLASH) { if (prg_type == Device::RD_FLASH) {
_mode = Device::READ_MODE; _mode = Device::READ_MODE;
} else { } else {
@ -52,6 +61,8 @@ Altera::Altera(Jtag *jtag, const std::string &filename,
_mode = Device::SPI_MODE; _mode = Device::SPI_MODE;
} else if (_file_extension == "pof") { // MAX10 } else if (_file_extension == "pof") { // MAX10
_mode = Device::FLASH_MODE; _mode = Device::FLASH_MODE;
} else if (_fpga_family == MAX10_FAMILY) {
_mode = Device::FLASH_MODE;
} else { // unknown type -> sanity check } else { // unknown type -> sanity check
if (prg_type == Device::WR_SRAM) { if (prg_type == Device::WR_SRAM) {
printError("file has an unknown type:"); printError("file has an unknown type:");
@ -65,15 +76,6 @@ Altera::Altera(Jtag *jtag, const std::string &filename,
} }
} }
} }
/* check device family */
_idcode = _jtag->get_target_device_id();
string family = fpga_list[_idcode].family;
if (family == "MAX 10") {
_fpga_family = MAX10_FAMILY;
} else {
_fpga_family = CYCLONE_MISC; // FIXME
}
} }
Altera::~Altera() Altera::~Altera()
@ -235,7 +237,7 @@ void Altera::program(unsigned int offset, bool unprotect_flash)
/* Specific case for MAX10 */ /* Specific case for MAX10 */
if (_fpga_family == MAX10_FAMILY) { if (_fpga_family == MAX10_FAMILY) {
max10_program(); max10_program(offset);
return; return;
} }
@ -296,41 +298,101 @@ uint32_t Altera::idCode()
#define MAX10_ISC_ENABLE {0xcc, 0x02} #define MAX10_ISC_ENABLE {0xcc, 0x02}
#define MAX10_ISC_DISABLE {0x01, 0x02} #define MAX10_ISC_DISABLE {0x01, 0x02}
#define MAX10_ISC_ADDRESS_SHIFT {0x03, 0x02} #define MAX10_ISC_ADDRESS_SHIFT {0x03, 0x02}
#define MAX10_ISC_ERASE {0xf2, 0x02}
#define MAX10_ISC_PROGRAM {0xf4, 0x02} #define MAX10_ISC_PROGRAM {0xf4, 0x02}
#define MAX10_DSM_ICB_PROGRAM {0xF4, 0x03} #define MAX10_DSM_ICB_PROGRAM {0xF4, 0x03}
#define MAX10_DSM_VERIFY {0x07, 0x03} #define MAX10_DSM_VERIFY {0x07, 0x03}
#define MAX10_DSM_CLEAR {0xf2, 0x03} #define MAX10_DSM_CLEAR {0xf2, 0x03}
#define MAX10_BYPASS {0xFF, 0x03} #define MAX10_BYPASS {0xFF, 0x03}
typedef struct { struct Altera::max10_mem_t {
uint32_t check_addr0; // something to check before sequence uint32_t check_addr0; // something to check before sequence
uint32_t dsm_addr; uint32_t dsm_addr;
uint32_t dsm_len; // 32bits uint32_t dsm_len; // 32bits
uint32_t ufm_addr; // UFM1 addr uint32_t ufm_addr; // UFM1 addr
uint32_t ufm_len[2]; uint32_t ufm_len[2]; // 32bits
uint32_t cfm_addr; // CFM2 addr uint32_t cfm_addr; // CFM2 addr
uint32_t cfm_len[3]; uint32_t cfm_len[3]; // 32bits
uint32_t sectors_erase_addr[5]; // UFM1, UFM0, CFM2, CFM1, CFM0
uint32_t done_bit_addr; uint32_t done_bit_addr;
uint32_t pgm_success_addr; uint32_t pgm_success_addr;
} max10_mem_t; };
static const std::map<uint32_t, max10_mem_t> max10_memory_map = { const std::map<uint32_t, Altera::max10_mem_t> Altera::max10_memory_map = {
{0x031820dd, { {0x031820dd, { // 10M08SAU
0x80005, // check_addr0 .check_addr0 = 0x80005, // check_addr0
0x0000, 512, // DSM .dsm_addr = 0x0000, 512, // DSM
0x0200, {4096, 4096}, // UFM .ufm_addr = 0x0200, .ufm_len = {4096, 4096}, // UFM
0x2200, {35840, 14848, 20992}, // CFM .cfm_addr = 0x2200, .cfm_len = {35840, 14848, 20992}, // CFM
0x0009, // done bit .sectors_erase_addr = {0x17ffff, 0x27ffff, 0x37ffff, 0x47ffff, 0x57ffff}, // sectors erase address
0x000b} // program success addr .done_bit_addr = 0x0009, // done bit
.pgm_success_addr = 0x000b} // program success addr
}, },
}; };
void Altera::max10_program() /* Write an arbitrary file in UFM1 and UFM0
* FIXME: in some mode its also possible to uses CFM2 & CFM1
*/
bool Altera::max10_program_ufm(const Altera::max10_mem_t *mem, unsigned int offset)
{ {
POFParser _bit(_filename, _verbose); RawParser _bit(_filename, true);
_bit.parse(); _bit.parse();
_bit.displayHeader(); _bit.displayHeader();
const uint8_t *data = _bit.getData();
const uint32_t length = _bit.getLength() / 8;
const uint32_t base_addr = mem->ufm_addr + offset;
uint8_t *buff = (uint8_t *)malloc(length);
if (!buff) {
printError("max10_program_ufm: Failed to allocate buffer");
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++) {
buff[i + b] = data[i + (3-b)];
}
}
// Start!
max10_flow_enable();
/* Erase UFM1 & UFM0 */
printInfo("Erase UFM ", false);
max10_flow_erase(mem, 0x3);
printInfo("Done");
/* Program UFM1 & UFM0 */
// Simplify code:
// UFM0 follows UFM1, so we don't need to iterate
printInfo("Write UFM");
writeXFM(buff, base_addr, 0, length / 4);
/* Verify */
if (_verify)
verifyxFM(buff, base_addr, 0, length / 4);
max10_flow_disable();
free(buff);
return true;
}
void Altera::max10_program(unsigned int offset)
{
uint32_t base_addr; uint32_t base_addr;
/* Needs to have some specifics informations about internal flash size/organisation /* Needs to have some specifics informations about internal flash size/organisation
@ -341,7 +403,16 @@ void Altera::max10_program()
printError("Model not supported. Please update max10_memory_map."); printError("Model not supported. Please update max10_memory_map.");
throw std::runtime_error("Model not supported. Please update max10_memory_map."); throw std::runtime_error("Model not supported. Please update max10_memory_map.");
} }
const max10_mem_t mem = mem_map->second; const Altera::max10_mem_t mem = mem_map->second;
if (_file_extension != "pof") {
max10_program_ufm(&mem, offset);
return;
}
POFParser _bit(_filename, _verbose);
_bit.parse();
_bit.displayHeader();
/* /*
* MAX10 memory map differs according to internal configuration mode * MAX10 memory map differs according to internal configuration mode
@ -402,7 +473,7 @@ void Altera::max10_program()
// Start! // Start!
max10_flow_enable(); max10_flow_enable();
max10_flow_erase(); max10_flow_erase(&mem, 0x1F);
max10_dsm_verify(); max10_dsm_verify();
/* Write */ /* Write */
@ -464,9 +535,18 @@ static void word_to_array(uint32_t in, uint8_t *out) {
out[3] = (in >> 24) & 0xff; out[3] = (in >> 24) & 0xff;
} }
void Altera::max10_flow_erase() /* Two possibilities:
* - full internal flash must be erased (to write a POF file): DSM_CLEAR must be used to
* perform a full erase
* - only a sub-set of sectors must be erased (to update a CPU firmware): only specified
* sectors have to be erased using MAX10_ISC_ERASE instruction after moving to a specific
* erase address
*/
void Altera::max10_flow_erase(const Altera::max10_mem_t *mem, const uint8_t erase_sectors)
{ {
const uint32_t dsm_clear_delay = 350000120 / _clk_period; const uint32_t dsm_clear_delay = 350000120 / _clk_period;
/* All sectors must be erased: DSM_CLEAR is better/faster */
if (erase_sectors == 0x1f) {
const uint8_t dsm_clear[2] = MAX10_DSM_CLEAR; const uint8_t dsm_clear[2] = MAX10_DSM_CLEAR;
max10_addr_shift(0x000000); max10_addr_shift(0x000000);
@ -474,6 +554,20 @@ void Altera::max10_flow_erase()
_jtag->shiftIR((unsigned char *)dsm_clear, NULL, IRLENGTH); _jtag->shiftIR((unsigned char *)dsm_clear, NULL, IRLENGTH);
_jtag->set_state(Jtag::RUN_TEST_IDLE); _jtag->set_state(Jtag::RUN_TEST_IDLE);
_jtag->toggleClk(dsm_clear_delay); _jtag->toggleClk(dsm_clear_delay);
} else {
//const uint32_t isc_clear_delay = 5120 / _clk_period;
const uint8_t isc_erase[2] = MAX10_ISC_ERASE;
/* each bit is a sector to erase */
for (int sect = 0; sect < 5; sect++) {
if ((erase_sectors >> sect) & 0x01) {
max10_addr_shift(mem->sectors_erase_addr[sect]);
_jtag->shiftIR((unsigned char *)isc_erase, NULL, IRLENGTH);
_jtag->set_state(Jtag::RUN_TEST_IDLE);
_jtag->toggleClk(dsm_clear_delay);
}
}
}
} }
void Altera::writeXFM(const uint8_t *cfg_data, uint32_t base_addr, uint32_t offset, uint32_t len) void Altera::writeXFM(const uint8_t *cfg_data, uint32_t base_addr, uint32_t offset, uint32_t len)

View File

@ -96,8 +96,16 @@ class Altera: public Device, SPIInterface {
/*************************/ /*************************/
/* max10 specific */ /* max10 specific */
/*************************/ /*************************/
void max10_program(); struct max10_mem_t;
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);
/* 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 */
uint32_t verifyxFM(const uint8_t *cfg_data, uint32_t base_addr, uint32_t offset, uint32_t verifyxFM(const uint8_t *cfg_data, uint32_t base_addr, uint32_t offset,
uint32_t len); uint32_t len);
void max10_dsm_program_success(const uint32_t pgm_success_addr); void max10_dsm_program_success(const uint32_t pgm_success_addr);
@ -105,7 +113,8 @@ class Altera: public Device, SPIInterface {
void max10_addr_shift(uint32_t addr); void max10_addr_shift(uint32_t addr);
void max10_flow_enable(); void max10_flow_enable();
void max10_flow_disable(); void max10_flow_disable();
void max10_flow_erase(); /* Performs a full internal flash erase or sectors per sectors erase */
void max10_flow_erase(const max10_mem_t *mem, const uint8_t erase_sectors=0x1f);
void max10_dsm_program(const uint8_t *dsm_data, const uint32_t dsm_len); void max10_dsm_program(const uint8_t *dsm_data, const uint32_t dsm_len);
bool max10_dsm_verify(); bool max10_dsm_verify();
bool max10_dump(); bool max10_dump();