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 Model: LFE3-70E
URL: https://www.latticesemi.com/Products/FPGAandCPLD/LatticeECP3 URL: https://www.latticesemi.com/Products/FPGAandCPLD/LatticeECP3
Memory: OK Memory: OK
Flash: TBD Flash: OK
- Description: ECP5 - Description: ECP5
Model: Model:

View File

@ -746,6 +746,11 @@ bool Lattice::prepare_flash_access()
/* clear SRAM before SPI access */ /* clear SRAM before SPI access */
if (!clearSRAM()) if (!clearSRAM())
return false; return false;
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. /*IR = 0h3A, DR=0hFE,0h68. Enter RUNTESTIDLE.
* thank @GregDavill * thank @GregDavill
* https://twitter.com/GregDavill/status/1251786406441086977 * https://twitter.com/GregDavill/status/1251786406441086977
@ -753,6 +758,7 @@ bool Lattice::prepare_flash_access()
_jtag->shiftIR(0x3A, 8, Jtag::EXIT1_IR); _jtag->shiftIR(0x3A, 8, Jtag::EXIT1_IR);
uint8_t tmp[2] = {0xFE, 0x68}; uint8_t tmp[2] = {0xFE, 0x68};
_jtag->shiftDR(tmp, NULL, 16); _jtag->shiftDR(tmp, NULL, 16);
}
return true; return true;
} }
@ -763,6 +769,10 @@ bool Lattice::post_flash_access()
printInfo("Skip resetting device"); printInfo("Skip resetting device");
return true; return true;
} }
if (_fpga_family == ECP3_FAMILY) {
_jtag->shiftIR(0xFF, 8, Jtag::RUN_TEST_IDLE);
_jtag->shiftIR(0x23, 8, Jtag::RUN_TEST_IDLE);
} else {
/* ISC REFRESH 0x79 */ /* ISC REFRESH 0x79 */
if (loadConfiguration() == false) { if (loadConfiguration() == false) {
/* when flash is blank status displays failure: /* when flash is blank status displays failure:
@ -802,11 +812,12 @@ bool Lattice::post_flash_access()
/* bypass */ /* bypass */
wr_rd(0xff, NULL, 0, NULL, 0); wr_rd(0xff, NULL, 0, NULL, 0);
_jtag->go_test_logic_reset(); _jtag->go_test_logic_reset();
}
return true; return true;
} }
void Lattice::reset() void Lattice::reset()
{ {
if (_fpga_family == ECP5_FAMILY) if (_fpga_family == ECP5_FAMILY || _fpga_family == ECP3_FAMILY)
post_flash_access(); post_flash_access();
else else
printError("Lattice Reset only tested on ECP5 Family."); 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 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 jtx[xfer_len];
uint8_t jrx[xfer_len]; uint8_t jrx[xfer_len];
memset(jrx, 0, xfer_len);
memset(jtx, 0, xfer_len);
jtx[0] = LatticeBitParser::reverseByte(cmd); jtx[0] = LatticeBitParser::reverseByte(cmd);
if (tx) { if (tx) {
@ -1685,12 +1700,19 @@ int Lattice::spi_put(uint8_t cmd, const uint8_t *tx, uint8_t *rx, uint32_t len)
* in the same time store each byte * in the same time store each byte
* to next * to next
*/ */
_jtag->shiftDR(jtx, (rx == NULL)? NULL: jrx, 8*xfer_len); _jtag->shiftDR(jtx, (!rx)? NULL: jrx, xfer_bit_len);
if (rx != NULL) { 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++) for (uint32_t i=0; i < len; i++)
rx[i] = LatticeBitParser::reverseByte(jrx[i+1]); rx[i] = LatticeBitParser::reverseByte(jrx[i+1]);
} }
}
return 0; 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, int Lattice::spi_wait(uint8_t cmd, uint8_t mask, uint8_t cond,
uint32_t timeout, bool verbose) uint32_t timeout, bool verbose)
{ {
uint8_t rx; uint8_t rx[2];
uint8_t dummy[2] = {0xff}; uint8_t dummy[2] = {0xff};
uint8_t tmp; uint8_t tmp;
uint8_t tx = LatticeBitParser::reverseByte(cmd); uint8_t tx = LatticeBitParser::reverseByte(cmd);
uint32_t count = 0; uint32_t count = 0;
uint32_t nb_byte = (_fpga_family == ECP3_FAMILY) ? 2 : 1;
/* CS is low until state goes to EXIT1_IR /* CS is low until state goes to EXIT1_IR
* so manually move to state machine to stay is this * 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); _jtag->shiftDR(&tx, NULL, 8, Jtag::SHIFT_DR);
do { do {
_jtag->shiftDR(dummy, &rx, 8, Jtag::SHIFT_DR); _jtag->shiftDR(dummy, rx, 8 * nb_byte, Jtag::SHIFT_DR);
tmp = (LatticeBitParser::reverseByte(rx)); if (_fpga_family == ECP3_FAMILY)
tmp = LatticeBitParser::reverseByte(rx[0] >> 1) | (rx[1] & 0x01);
else
tmp = (LatticeBitParser::reverseByte(rx[0]));
count++; count++;
if (count == timeout){ if (count == timeout){
printf("timeout: %x %x %u\n", tmp, rx, count); printf("timeout: %x %x %u\n", tmp, rx[0], count);
break; 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); printf("%x %x %x %u\n", tmp, mask, cond, count);
} }
} while ((tmp & mask) != cond); } 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) { if (count == timeout) {
printf("%x\n", tmp); printf("%x\n", tmp);
std::cout << "wait: Error" << std::endl; std::cout << "wait: Error" << std::endl;