lattice: added SPI Flash access support for ECP3 family

This commit is contained in:
Gwenhael Goavec-Merou 2025-12-08 16:24:18 +01:00
parent b76840b20d
commit 432cdc2dd7
2 changed files with 82 additions and 56 deletions

View File

@ -183,7 +183,7 @@ Lattice:
Model: LFE3-70E
URL: https://www.latticesemi.com/Products/FPGAandCPLD/LatticeECP3
Memory: OK
Flash: TBD
Flash: OK
- Description: ECP5
Model:

View File

@ -746,13 +746,19 @@ bool Lattice::prepare_flash_access()
/* clear SRAM before SPI access */
if (!clearSRAM())
return false;
/*IR = 0h3A, DR=0hFE,0h68. Enter RUNTESTIDLE.
* thank @GregDavill
* https://twitter.com/GregDavill/status/1251786406441086977
*/
_jtag->shiftIR(0x3A, 8, Jtag::EXIT1_IR);
uint8_t tmp[2] = {0xFE, 0x68};
_jtag->shiftDR(tmp, NULL, 16);
if (_fpga_family == ECP3_FAMILY) {
if (!wr_rd(0x3A, 0, 0, 0, 0, false))
return false;
_jtag->set_state(Jtag::RUN_TEST_IDLE);
} else {
/*IR = 0h3A, DR=0hFE,0h68. Enter RUNTESTIDLE.
* thank @GregDavill
* https://twitter.com/GregDavill/status/1251786406441086977
*/
_jtag->shiftIR(0x3A, 8, Jtag::EXIT1_IR);
uint8_t tmp[2] = {0xFE, 0x68};
_jtag->shiftDR(tmp, NULL, 16);
}
return true;
}
@ -763,50 +769,55 @@ bool Lattice::post_flash_access()
printInfo("Skip resetting device");
return true;
}
/* ISC REFRESH 0x79 */
if (loadConfiguration() == false) {
/* when flash is blank status displays failure:
* try to check flash first sector
*/
_skip_reset = true; // avoid infinite loop
/* read flash 0 -> 255 */
uint8_t buffer[256];
ret = SPIInterface::read(buffer, 0, 256);
loadConfiguration(); // reset again
/* read ok? check if everything == 0xff */
if (ret) {
for (int i = 0; i < 256; i++) {
/* not blank: fail */
if (buffer[i] != 0xFF) {
ret = false;
break;
}
}
/* to add a note */
flash_blank = true;
}
}
printInfo("Refresh: ", false);
if (!ret) {
printError("FAIL");
displayReadReg(readStatusReg());
return false;
if (_fpga_family == ECP3_FAMILY) {
_jtag->shiftIR(0xFF, 8, Jtag::RUN_TEST_IDLE);
_jtag->shiftIR(0x23, 8, Jtag::RUN_TEST_IDLE);
} else {
printSuccess("DONE");
if (flash_blank)
printWarn("Flash is blank");
}
/* ISC REFRESH 0x79 */
if (loadConfiguration() == false) {
/* when flash is blank status displays failure:
* try to check flash first sector
*/
_skip_reset = true; // avoid infinite loop
/* read flash 0 -> 255 */
uint8_t buffer[256];
ret = SPIInterface::read(buffer, 0, 256);
loadConfiguration(); // reset again
/* bypass */
wr_rd(0xff, NULL, 0, NULL, 0);
_jtag->go_test_logic_reset();
/* read ok? check if everything == 0xff */
if (ret) {
for (int i = 0; i < 256; i++) {
/* not blank: fail */
if (buffer[i] != 0xFF) {
ret = false;
break;
}
}
/* to add a note */
flash_blank = true;
}
}
printInfo("Refresh: ", false);
if (!ret) {
printError("FAIL");
displayReadReg(readStatusReg());
return false;
} else {
printSuccess("DONE");
if (flash_blank)
printWarn("Flash is blank");
}
/* bypass */
wr_rd(0xff, NULL, 0, NULL, 0);
_jtag->go_test_logic_reset();
}
return true;
}
void Lattice::reset()
{
if (_fpga_family == ECP5_FAMILY)
if (_fpga_family == ECP5_FAMILY || _fpga_family == ECP3_FAMILY)
post_flash_access();
else
printError("Lattice Reset only tested on ECP5 Family.");
@ -1670,10 +1681,14 @@ uint16_t Lattice::getUFMStartPageFromJEDEC(JedParser *_jed, int id)
int Lattice::spi_put(uint8_t cmd, const uint8_t *tx, uint8_t *rx, uint32_t len)
{
int xfer_len = len + 1;
const uint32_t xfer_len = len + 1 + ((rx != NULL) && ((_fpga_family == ECP3_FAMILY)) ? 1 : 0);
const uint32_t xfer_bit_len = (len + 1) * 8 + ((rx != NULL) && ((_fpga_family == ECP3_FAMILY)) ? 1 : 0);
uint8_t jtx[xfer_len];
uint8_t jrx[xfer_len];
memset(jrx, 0, xfer_len);
memset(jtx, 0, xfer_len);
jtx[0] = LatticeBitParser::reverseByte(cmd);
if (tx) {
@ -1685,11 +1700,18 @@ int Lattice::spi_put(uint8_t cmd, const uint8_t *tx, uint8_t *rx, uint32_t len)
* in the same time store each byte
* to next
*/
_jtag->shiftDR(jtx, (rx == NULL)? NULL: jrx, 8*xfer_len);
_jtag->shiftDR(jtx, (!rx)? NULL: jrx, xfer_bit_len);
if (rx != NULL) {
for (uint32_t i=0; i < len; i++)
rx[i] = LatticeBitParser::reverseByte(jrx[i+1]);
if (rx) {
if (_fpga_family == ECP3_FAMILY) {
for (uint32_t i = 0; i < len; ++i) {
const uint8_t tmp = LatticeBitParser::reverseByte((jrx[i+1] >> 1) & 0x7f);
rx[i] = tmp | (jrx[i + 2] & 0x01);
}
} else {
for (uint32_t i=0; i < len; i++)
rx[i] = LatticeBitParser::reverseByte(jrx[i+1]);
}
}
return 0;
}
@ -1725,11 +1747,12 @@ int Lattice::spi_put(const uint8_t *tx, uint8_t *rx, uint32_t len)
int Lattice::spi_wait(uint8_t cmd, uint8_t mask, uint8_t cond,
uint32_t timeout, bool verbose)
{
uint8_t rx;
uint8_t rx[2];
uint8_t dummy[2] = {0xff};
uint8_t tmp;
uint8_t tx = LatticeBitParser::reverseByte(cmd);
uint32_t count = 0;
uint32_t nb_byte = (_fpga_family == ECP3_FAMILY) ? 2 : 1;
/* CS is low until state goes to EXIT1_IR
* so manually move to state machine to stay is this
@ -1738,11 +1761,14 @@ int Lattice::spi_wait(uint8_t cmd, uint8_t mask, uint8_t cond,
_jtag->shiftDR(&tx, NULL, 8, Jtag::SHIFT_DR);
do {
_jtag->shiftDR(dummy, &rx, 8, Jtag::SHIFT_DR);
tmp = (LatticeBitParser::reverseByte(rx));
_jtag->shiftDR(dummy, rx, 8 * nb_byte, Jtag::SHIFT_DR);
if (_fpga_family == ECP3_FAMILY)
tmp = LatticeBitParser::reverseByte(rx[0] >> 1) | (rx[1] & 0x01);
else
tmp = (LatticeBitParser::reverseByte(rx[0]));
count++;
if (count == timeout){
printf("timeout: %x %x %u\n", tmp, rx, count);
printf("timeout: %x %x %u\n", tmp, rx[0], count);
break;
}
@ -1750,7 +1776,7 @@ int Lattice::spi_wait(uint8_t cmd, uint8_t mask, uint8_t cond,
printf("%x %x %x %u\n", tmp, mask, cond, count);
}
} while ((tmp & mask) != cond);
_jtag->shiftDR(dummy, &rx, 8, Jtag::RUN_TEST_IDLE);
_jtag->shiftDR(dummy, rx, 8, Jtag::RUN_TEST_IDLE);
if (count == timeout) {
printf("%x\n", tmp);
std::cout << "wait: Error" << std::endl;