From a7b56c373228ac55be5384a72cd67d241aadfb85 Mon Sep 17 00:00:00 2001 From: Gwenhael Goavec-Merou Date: Wed, 18 Jun 2025 10:15:59 +0200 Subject: [PATCH] lattice: added ECP3 family support (SRAM only) --- src/lattice.cpp | 329 ++++++++++++++++++++++++++++++++++++++++-------- src/lattice.hpp | 5 + 2 files changed, 283 insertions(+), 51 deletions(-) diff --git a/src/lattice.cpp b/src/lattice.cpp index 4c2a17b..26d415c 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -12,7 +12,9 @@ #include #include +#include #include +#include #include #include "jtag.hpp" @@ -141,6 +143,17 @@ using namespace std; #define PUBKEY_LENGTH_BYTES 64 /* length of the public key (MachXO3D) in bytes */ +/* ECP3 */ +#define ECP3_LSCC_BITSTREAM_BURST 0x02 +#define ECP3_ISC_ERASE 0x03 /* ISC_ERASE */ +#define ECP3_ISC_ENABLE 0x15 +#define ECP3_IDCODE 0x16 +#define ECP3_READ_USERCODE 0x17 +#define ECP3_ISC_PROGRAM_USERCODE 0x1A +#define ECP3_RESET_ADDRESS 0x21 +#define ECP3_LSCC_REFRESH 0x23 +#define ECP3_READ_STATUS_REGISTER 0x53 + Lattice::Lattice(Jtag *jtag, const string filename, const string &file_type, Device::prog_type_t prg_type, std::string flash_sector, bool verify, int8_t verbose, bool skip_load_bridge, bool skip_reset): Device(jtag, filename, file_type, verify, verbose), @@ -203,6 +216,8 @@ Lattice::Lattice(Jtag *jtag, const string filename, const string &file_type, printError("Unknown flash sector"); throw std::exception(); } + } else if (family == "ECP3") { + _fpga_family = ECP3_FAMILY; } else if (family == "ECP5") { _fpga_family = ECP5_FAMILY; } else if (family == "CrosslinkNX") { @@ -270,6 +285,7 @@ bool Lattice::checkStatus(uint64_t val, uint64_t mask) bool Lattice::program_mem() { bool err; + LatticeBitParser _bit(_filename, false, _verbose); printInfo("Open file: ", false); @@ -308,14 +324,21 @@ bool Lattice::program_mem() * PRELOAD/SAMPLE 0x1C * For NEXUS family fpgas, the Bscan register is 362 bits long or * 45.25 bytes => 46 bytes + * For ECP3 family fpgas, the Bscan register is 1077 bits long or + * 134.62 bytes => 135 bytes */ - uint8_t tx_buf[46]; - memset(tx_buf, 0xff, 46); + uint8_t tx_buf[135]; + memset(tx_buf, 0xff, 135); int tx_len; - if(_fpga_family == NEXUS_FAMILY){ - tx_len = 46; - } else { - tx_len = 26; + switch (_fpga_family) { + case NEXUS_FAMILY: + tx_len = 46; + break; + case ECP3_FAMILY: + tx_len = 135; + break; + default: + tx_len = 26; } wr_rd(PRELOAD_SAMPLE, tx_buf, tx_len, NULL, 0); @@ -326,32 +349,41 @@ bool Lattice::program_mem() */ /*flag to understand if we refreshed or not*/ bool was_refreshed; - if (_fpga_family == NEXUS_FAMILY) { - if (!checkStatus(0, REG_STATUS_PRV_CNF_CHK_MASK)) { - printInfo("Error in previous bitstream execution. REFRESH: ", false); - wr_rd(REFRESH, NULL, 0, NULL, 0); - _jtag->set_state(Jtag::RUN_TEST_IDLE); - _jtag->toggleClk(1000); - /* In Lattice FPGA-TN-02099 document in a note it's reported that there - is a delay time after LSC_REFRESH where "Duration could be in - seconds". Without whis waiting time, busy flag can't be cleared.*/ - sleep(5); - was_refreshed = true; + switch (_fpga_family) { + case NEXUS_FAMILY: if (!checkStatus(0, REG_STATUS_PRV_CNF_CHK_MASK)) { - printError("FAIL"); - displayReadReg(readStatusReg()); - return false; + printInfo("Error in previous bitstream execution. REFRESH: ", false); + wr_rd(REFRESH, NULL, 0, NULL, 0); + _jtag->set_state(Jtag::RUN_TEST_IDLE); + _jtag->toggleClk(1000); + /* In Lattice FPGA-TN-02099 document in a note it's reported that there + is a delay time after LSC_REFRESH where "Duration could be in + seconds". Without whis waiting time, busy flag can't be cleared.*/ + sleep(5); + was_refreshed = true; + if (!checkStatus(0, REG_STATUS_PRV_CNF_CHK_MASK)) { + printError("FAIL"); + displayReadReg(readStatusReg()); + return false; + } else { + printSuccess("DONE"); + } } else { - printSuccess("DONE"); + was_refreshed = false; + if (_verbose){ + printInfo("No error in previous bitstream execution.", true); + } } - } else { + break; + case ECP3_FAMILY: + wr_rd(ECP3_LSCC_REFRESH, NULL, 0, NULL, 0); + _jtag->set_state(Jtag::RUN_TEST_IDLE); + _jtag->toggleClk(5, 1); + usleep_ecp3(500000); // 0.5s + was_refreshed = false; + break; + default: was_refreshed = false; - if (_verbose){ - printInfo("No error in previous bitstream execution.", true); - } - } - } else { - was_refreshed = false; } /* ISC Enable 0xC6 */ @@ -387,6 +419,11 @@ bool Lattice::program_mem() printSuccess("DONE"); } + if (_fpga_family == ECP3_FAMILY) { + if (!write_userCode(0xffffffff)) + return false; + } + /* ISC ERASE * For Nexus family (from svf file): 1 byte to tx 0x00 */ @@ -404,15 +441,43 @@ bool Lattice::program_mem() } /* LSC_INIT_ADDRESS */ - wr_rd(0x46, NULL, 0, NULL, 0); - _jtag->set_state(Jtag::RUN_TEST_IDLE); - _jtag->toggleClk(1000); + if (_fpga_family == ECP3_FAMILY) { + wr_rd(ECP3_RESET_ADDRESS, NULL, 0, NULL, 0); + _jtag->set_state(Jtag::RUN_TEST_IDLE); + _jtag->toggleClk(5); + usleep_ecp3(2000); // 2ms + + /* read user code (User Code register must have all bit set cleared) */ + const uint32_t dummy = 0xffffffff; + uint32_t rx = 0; + wr_rd(ECP3_READ_USERCODE, (uint8_t *)&dummy, 4, (uint8_t *)&rx, 4); + if (rx != 0x00000000) { + char message[256]; + snprintf(message, 256, "failed: 0x%08x instead of 0xffffffff", rx); + printError(message); + return false; + } + } else { + wr_rd(0x46, NULL, 0, NULL, 0); + _jtag->set_state(Jtag::RUN_TEST_IDLE); + _jtag->toggleClk(1000); + } const uint8_t *data = _bit.getData(); int length = _bit.getLength()/8; - wr_rd(0x7A, NULL, 0, NULL, 0); - _jtag->set_state(Jtag::RUN_TEST_IDLE); - _jtag->toggleClk(2); + if (_fpga_family == ECP3_FAMILY) { + /* Reset Address */ + wr_rd(ECP3_RESET_ADDRESS, NULL, 0, NULL, 0); + _jtag->set_state(Jtag::RUN_TEST_IDLE); + _jtag->toggleClk(5, 1); + usleep_ecp3(2000); // 2ms + + wr_rd(ECP3_LSCC_BITSTREAM_BURST, NULL, 0, NULL, 0); + } else { + wr_rd(0x7A, NULL, 0, NULL, 0); + _jtag->set_state(Jtag::RUN_TEST_IDLE); + _jtag->toggleClk(2); + } uint8_t tmp[1024]; int size = 1024; @@ -431,27 +496,45 @@ bool Lattice::program_mem() for (int ii = 0; ii < size; ii++) tmp[ii] = ConfigBitstreamParser::reverseByte(data[i+ii]); - _jtag->shiftDR(tmp, NULL, size*8, next_state); + _jtag->shiftDR(tmp, NULL, size * 8, next_state); } _jtag->set_state(Jtag::RUN_TEST_IDLE); - _jtag->toggleClk(1000); - uint32_t status_mask; - if (_fpga_family == MACHXO3D_FAMILY) - status_mask = REG_STATUS_MACHXO3D_CNF_CHK_MASK; - else - status_mask = REG_STATUS_CNF_CHK_MASK; + if (_fpga_family == ECP3_FAMILY) { + _jtag->toggleClk(256, 1); + usleep_ecp3(2000); - if (checkStatus(0, status_mask)) { + /* Verifiy User Code register */ + uint32_t rx = userCode(); + if (rx != 0x00000000) { + progress.fail(); + char message[256]; + snprintf(message, 256, "failed: 0x%08x instead of 0x00000000", rx); + printError(message); + return false; + } progress.done(); } else { - progress.fail(); - displayReadReg(readStatusReg()); - return false; - } + _jtag->toggleClk(1000); - wr_rd(0xff, NULL, 0, NULL, 0); + uint32_t status_mask; + if (_fpga_family == MACHXO3D_FAMILY) + status_mask = REG_STATUS_MACHXO3D_CNF_CHK_MASK; + else + status_mask = REG_STATUS_CNF_CHK_MASK; + + if (checkStatus(0, status_mask)) { + progress.done(); + } else { + progress.fail(); + displayReadReg(readStatusReg()); + return false; + } + + /* bypass */ + wr_rd(0xff, NULL, 0, NULL, 0); + } if (_verbose) printf("userCode: %08x\n", userCode()); @@ -468,6 +551,25 @@ bool Lattice::program_mem() printSuccess("DONE"); } + if (_fpga_family == ECP3_FAMILY) { + /* Bypass */ + wr_rd(0xff, NULL, 0, NULL, 0); + _jtag->set_state(Jtag::RUN_TEST_IDLE); + _jtag->toggleClk(100, 1); + usleep_ecp3(1000); + + // Verify STATUS Register + uint64_t status = readStatusReg(); + _jtag->toggleClk(5, 1); + if ((status & 0x60007) != 0x20000) { + char message[256]; + snprintf(message, 256, "Programming failed: status 0x%" PRIx64 "instead of 0x20000", status); + printError(message); + displayReadReg(status); + return false; + } + } + if (_verbose) displayReadReg(readStatusReg()); @@ -898,6 +1000,14 @@ void Lattice::program(unsigned int offset, bool unprotect_flash) */ bool Lattice::EnableISC(uint8_t flash_mode) { + if (_fpga_family == ECP3_FAMILY) { + wr_rd(ECP3_ISC_ENABLE, NULL, 0, NULL, 0); + _jtag->set_state(Jtag::RUN_TEST_IDLE); + _jtag->toggleClk(5, 1); + usleep_ecp3(20000); // 0.20s + return true; + } + wr_rd(ISC_ENABLE, &flash_mode, 1, NULL, 0); _jtag->set_state(Jtag::RUN_TEST_IDLE); @@ -911,6 +1021,13 @@ bool Lattice::EnableISC(uint8_t flash_mode) bool Lattice::DisableISC() { + if (_fpga_family == ECP3_FAMILY) { + /** Shift in ISC DISABLE instruction */ + _jtag->shiftIR(0x1E, 8, Jtag::RUN_TEST_IDLE); + _jtag->toggleClk(5, 1); + usleep_ecp3(200000); + return true; + } wr_rd(ISC_DISABLE, NULL, 0, NULL, 0); _jtag->set_state(Jtag::RUN_TEST_IDLE); _jtag->toggleClk(1000); @@ -942,7 +1059,8 @@ bool Lattice::DisableCfg() uint32_t Lattice::idCode() { uint8_t device_id[4]; - wr_rd(READ_DEVICE_ID_CODE, NULL, 0, device_id, 4); + const uint8_t idcode = (_fpga_family == ECP3_FAMILY) ? ECP3_IDCODE : READ_DEVICE_ID_CODE; + wr_rd(idcode, NULL, 0, device_id, 4); return device_id[3] << 24 | device_id[2] << 16 | device_id[1] << 8 | @@ -952,13 +1070,40 @@ uint32_t Lattice::idCode() int Lattice::userCode() { uint8_t usercode[4]; - wr_rd(0xC0, NULL, 0, usercode, 4); + wr_rd((_fpga_family == ECP3_FAMILY) ? ECP3_READ_USERCODE : 0xC0, + NULL, 0, usercode, 4); return usercode[3] << 24 | usercode[2] << 16 | usercode[1] << 8 | usercode[0]; } +bool Lattice::write_userCode(uint32_t usercode) +{ + // ! Shift in ISC PROGRAM USERCODE(0x1A) instruction + // SIR 8 TDI (1A); + // SDR 32 TDI (FFFFFFFF); + // RUNTEST IDLE 5 TCK 2,00E-03 SEC; + // ! Shift in READ USERCODE(0x17) instruction + // SIR 8 TDI (17); + // SDR 32 TDI (FFFFFFFF) + // TDO (FFFFFFFF); + wr_rd(ECP3_ISC_PROGRAM_USERCODE, (uint8_t *)&usercode, 4, NULL, 0); + _jtag->set_state(Jtag::RUN_TEST_IDLE); + _jtag->toggleClk(5, 1); + usleep_ecp3(2000); // 2ms + + uint32_t rd_usercode = userCode(); + if (rd_usercode != usercode) { + char message[256]; + snprintf(message, 256, "failed: 0x%08x instead of 0x%08x", + rd_usercode, usercode); + printError(message); + return false; + } + return true; +} + bool Lattice::checkID() { printf("\n"); @@ -1005,12 +1150,13 @@ uint64_t Lattice::readStatusReg() uint64_t reg; uint8_t rx[8], tx[8]; - int reg_len = get_statusreg_size() / 8; + const int reg_len = get_statusreg_size() / 8; + const uint8_t regcode = (_fpga_family == ECP3_FAMILY) ? ECP3_READ_STATUS_REGISTER : READ_STATUS_REGISTER; /* valgrind warn */ memset(tx, 0, 8); memset(rx, 0, 8); - wr_rd(READ_STATUS_REGISTER, tx, reg_len, rx, reg_len); + wr_rd(regcode, tx, reg_len, rx, reg_len); _jtag->set_state(Jtag::RUN_TEST_IDLE); _jtag->toggleClk(1000); reg = (uint64_t) rx[7] << 56 | (uint64_t) rx[6] << 48 | (uint64_t) rx[5] << 40 | (uint64_t) rx[4] << 32 | rx[3] << 24 | rx[2] << 16 | rx[1] << 8 | rx[0]; @@ -1052,8 +1198,75 @@ bool Lattice::wr_rd(uint8_t cmd, return true; } +typedef struct { + std::string description; + uint8_t offset; + uint8_t size; + std::map reg_cnt; +} reg_struct_t; + +#define REG_ENTRY(_description, _offset, _size, ...) \ + {_description, _offset, _size, {__VA_ARGS__}} + +static const std::map> reg_content = { + {"StatusRegister", std::list{ + REG_ENTRY("CRC Error", 0, 1, {0, "OK"}, {1, "KO"}), + REG_ENTRY("ID Verify failed", 1, 1, {0, "No"}, {1, "Yes"}), + REG_ENTRY("Invalid Command", 2, 1, {0, "No"}, {1, "Yes"}), + REG_ENTRY("SPIm Mode Error", 3, 1, {0, "No"}, {1, "Yes"}), + REG_ENTRY("Device locked", 4, 1, {0, "No"}, {1, "Yes"}), + REG_ENTRY("Key Fire", 5, 1, {0, "No"}, {1, "Yes"}), + REG_ENTRY("Alignement Preamble", 6, 1, {0, "No"}, {1, "Yes"}), + REG_ENTRY("Encryption Preamble", 7, 1, {0, "No"}, {1, "Yes"}), + REG_ENTRY("Std Preamble", 8, 1, {0, "No"}, {1, "Yes"}), + REG_ENTRY("HFC", 9, 1, {0, "No"}, {1, "Yes"}), + REG_ENTRY("Memory Cleared", 15, 1, {0, "No"}, {1, "Yes"}), + REG_ENTRY("Device Secured", 16, 1, {0, "No"}, {1, "Yes"}), + REG_ENTRY("Device Configured", 17, 1, {0, "No"}, {1, "Yes"}), + REG_ENTRY("Non-JTAG Mode", 18, 1, {0, "No"}, {1, "Yes"}), + REG_ENTRY("ReadBack Mode", 19, 1, {0, "No"}, {1, "Yes"}), + REG_ENTRY("Bypass Mode", 20, 1, {0, "No"}, {1, "Yes"}), + REG_ENTRY("Flow Trough", 21, 1, {0, "No"}, {1, "Yes"}), + REG_ENTRY("Configuration Mode", 22, 1, {0, "No"}, {1, "Yes"}), + REG_ENTRY("Transparent Mode", 23, 1, {0, "No"}, {1, "Yes"}), + }}, +}; + void Lattice::displayReadReg(uint64_t dev) { + if (_fpga_family == ECP3_FAMILY) { + auto reg = reg_content.find("StatusRegister"); + if (reg == reg_content.end()) { + printError("Unknown register StatusRegister"); + return; + } + + std::stringstream raw_val; + raw_val << "0x" << std::hex << dev; + printSuccess("Register raw value: " + raw_val.str()); + + const std::list regs = reg->second; + for (reg_struct_t r: regs) { + uint8_t offset = r.offset; + uint8_t size = r.size; + uint32_t mask = (1 << size) - 1; + uint32_t val = (dev >> offset) & mask; + std::stringstream ss, desc; + desc << r.description; + ss << std::setw(20) << std::left << r.description; + if (r.reg_cnt.size() != 0) { + ss << r.reg_cnt[val]; + } else { + std::stringstream hex_val; + hex_val << "0x" << std::hex << val; + ss << hex_val.str(); + } + + printInfo(ss.str()); + } + return; + } + uint8_t err; printf("displayReadReg\n"); if (dev & 1<<0) printf("\tTRAN Mode\n"); @@ -1247,6 +1460,12 @@ bool Lattice::flashErase(uint32_t mask) (uint8_t)((mask >> 16) & 0xff) }; wr_rd(FLASH_ERASE, tx, 2, NULL, 0); + } else if (_fpga_family == ECP3_FAMILY) { + wr_rd(ECP3_ISC_ERASE, NULL, 0, NULL, 0); + _jtag->set_state(Jtag::RUN_TEST_IDLE); + _jtag->toggleClk(5, 1); + usleep_ecp3(2000000); // 2s + return true; } else { uint8_t tx[1] = {(uint8_t)(mask & 0xff)}; wr_rd(FLASH_ERASE, tx, 1, NULL, 0); @@ -1540,6 +1759,14 @@ int Lattice::spi_wait(uint8_t cmd, uint8_t mask, uint8_t cond, return 0; } +/*************************** MODS FOR ECP3 ************************************/ + +void Lattice::usleep_ecp3(uint64_t us_time) +{ + const uint32_t clk_period = 1e9/static_cast(_jtag->getClkFreq()); + const uint32_t clk_len = (us_time * 1000) / clk_period; + _jtag->toggleClk(clk_len, 0); // 0.5s +} /*************************** MODS FOR MacXO3D *********************************/ diff --git a/src/lattice.hpp b/src/lattice.hpp index 0eef34b..02d7027 100644 --- a/src/lattice.hpp +++ b/src/lattice.hpp @@ -25,6 +25,7 @@ class Lattice: public Device, SPIInterface { int8_t verbose, bool skip_load_bridge, bool skip_reset); uint32_t idCode() override; int userCode(); + bool write_userCode(uint32_t usercode); void reset() override; void program(unsigned int offset, bool unprotect_flash) override; bool program_mem(); @@ -74,6 +75,7 @@ class Lattice: public Device, SPIInterface { MACHXO3D_FAMILY = 2, ECP5_FAMILY = 3, NEXUS_FAMILY = 4, + ECP3_FAMILY, UNKNOWN_FAMILY = 999 }; @@ -122,6 +124,9 @@ class Lattice: public Device, SPIInterface { /* test */ bool checkID(); + /************************* MODS for ECP3 ******************************/ + void usleep_ecp3(uint64_t us_time); + /*********************** MODS FOR MacXO3D *****************************/ enum lattice_flash_sector_t { LATTICE_FLASH_UNDEFINED = 0,