From d8b8afce994ba8f02d7123e47ca2a0e46c21ee02 Mon Sep 17 00:00:00 2001 From: Gwenhael Goavec-Merou Date: Mon, 18 Oct 2021 07:34:27 +0200 Subject: [PATCH 1/5] spiFlash: fix len_to_bp mask --- src/spiFlash.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spiFlash.cpp b/src/spiFlash.cpp index d7c5e1d..e2dd11c 100644 --- a/src/spiFlash.cpp +++ b/src/spiFlash.cpp @@ -436,7 +436,7 @@ uint8_t SPIFlash::len_to_bp(uint32_t len) /* reconstruct code based on each BPx bit */ uint8_t tmp = 0; for (int i = 0; i < 4; i++) - if (bp >> i) + if (bp & (1 << i)) tmp |= _flash_model->bp_offset[i]; return tmp; From 6714400909a0718fad83f72465dff9835cd4c53b Mon Sep 17 00:00:00 2001 From: Gwenhael Goavec-Merou Date: Tue, 19 Oct 2021 06:55:30 +0200 Subject: [PATCH 2/5] spiFlashdb: introduce tb_register to know tb location register --- src/spiFlashdb.hpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/spiFlashdb.hpp b/src/spiFlashdb.hpp index 37c6989..933483c 100644 --- a/src/spiFlashdb.hpp +++ b/src/spiFlashdb.hpp @@ -11,6 +11,11 @@ #include #include +typedef enum { + STATR = 0, /* status register */ + FUNCR = 1 /* function register */ +} tb_loc_t; + typedef struct { std::string manufacturer; /**< manufacturer name */ std::string model; /**< chip name */ @@ -21,6 +26,7 @@ typedef struct { bool has_extended; bool tb_otp; /**< TOP/BOTTOM One Time Programming */ uint8_t tb_offset; /**< TOP/BOTTOM bit offset */ + tb_loc_t tb_register; /**< TOP/BOTTOM location (register) */ uint8_t bp_len; /**< BPx length */ uint8_t bp_offset[4]; /**< BP[0:3] bit offset */ } flash_t; @@ -35,6 +41,7 @@ static std::map flash_list = { .has_extended = true, .tb_otp = false, .tb_offset = (1 << 5), + .tb_register = STATR, .bp_len = 4, .bp_offset = {(1 << 2), (1 << 3), (1 << 4), (1 << 6)}} }, @@ -47,6 +54,7 @@ static std::map flash_list = { .has_extended = true, .tb_otp = false, .tb_offset = (1 << 5), + .tb_register = STATR, .bp_len = 4, .bp_offset = {(1 << 2), (1 << 3), (1 << 4), (1 << 6)}} }, @@ -59,6 +67,7 @@ static std::map flash_list = { .has_extended = false, .tb_otp = true, .tb_offset = (1 << 5), + .tb_register = FUNCR, .bp_len = 4, .bp_offset = {(1 << 2), (1 << 3), (1 << 4), (1 << 5)}} }, @@ -71,6 +80,7 @@ static std::map flash_list = { .has_extended = false, .tb_otp = true, .tb_offset = (1 << 5), + .tb_register = FUNCR, .bp_len = 4, .bp_offset = {(1 << 2), (1 << 3), (1 << 4), (1 << 5)}} }, @@ -83,6 +93,7 @@ static std::map flash_list = { .has_extended = false, .tb_otp = true, .tb_offset = (1 << 5), + .tb_register = FUNCR, .bp_len = 4, .bp_offset = {(1 << 2), (1 << 3), (1 << 4), (1 << 5)}} }, @@ -95,6 +106,7 @@ static std::map flash_list = { .has_extended = false, .tb_otp = false, .tb_offset = (1 << 5), + .tb_register = STATR, .bp_len = 3, .bp_offset = {(1 << 2), (1 << 3), (1 << 4), 0}} }, From 3730e8189de8401a63bbf3a05248189ca64f268a Mon Sep 17 00:00:00 2001 From: Gwenhael Goavec-Merou Date: Tue, 19 Oct 2021 07:06:46 +0200 Subject: [PATCH 3/5] spiFlash: extract status register display from read_status_reg --- src/altera.cpp | 2 +- src/anlogic.cpp | 2 +- src/gowin.cpp | 2 +- src/lattice.cpp | 2 +- src/spiFlash.cpp | 29 +++++++++++++++++++++-------- src/spiFlash.hpp | 4 +++- src/xilinx.cpp | 2 +- 7 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/altera.cpp b/src/altera.cpp index d44dc04..3c911eb 100644 --- a/src/altera.cpp +++ b/src/altera.cpp @@ -218,7 +218,7 @@ void Altera::program(unsigned int offset) try { epcq.reset(); epcq.read_id(); - epcq.read_status_reg(); + epcq.display_status_reg(epcq.read_status_reg()); epcq.erase_and_prog(offset, data, length); } catch (std::exception &e) { printError(e.what()); diff --git a/src/anlogic.cpp b/src/anlogic.cpp index a992db9..41349e7 100644 --- a/src/anlogic.cpp +++ b/src/anlogic.cpp @@ -100,7 +100,7 @@ void Anlogic::program(unsigned int offset) flash.reset(); flash.read_id(); - flash.read_status_reg(); + flash.display_status_reg(flash.read_status_reg()); flash.erase_and_prog(offset, data, len); diff --git a/src/gowin.cpp b/src/gowin.cpp index 2eb5fa1..de87a53 100644 --- a/src/gowin.cpp +++ b/src/gowin.cpp @@ -238,7 +238,7 @@ void Gowin::program(unsigned int offset) SPIFlash spiFlash(this, (_verbose ? 1 : (_quiet ? -1 : 0))); spiFlash.reset(); spiFlash.read_id(); - spiFlash.read_status_reg(); + spiFlash.display_status_reg(spiFlash.read_status_reg()); if (spiFlash.erase_and_prog(offset, data, length / 8) != 0) throw std::runtime_error("Error: write to flash failed"); if (_verify) diff --git a/src/lattice.cpp b/src/lattice.cpp index ea6718c..c4c7fc8 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -444,7 +444,7 @@ bool Lattice::program_extFlash(unsigned int offset) SPIFlash flash(this, _verbose); flash.reset(); flash.read_id(); - flash.read_status_reg(); + flash.display_status_reg(flash.read_status_reg()); flash.erase_and_prog(offset, data, length); int ret = true; diff --git a/src/spiFlash.cpp b/src/spiFlash.cpp index e2dd11c..d5df104 100644 --- a/src/spiFlash.cpp +++ b/src/spiFlash.cpp @@ -321,18 +321,31 @@ void SPIFlash::read_id() } } +void SPIFlash::display_status_reg(uint8_t reg) +{ + uint8_t tb, bp; + if (!_flash_model) { + tb = (reg >> 5) & 0x01; + bp = (((reg >> 6) & 0x01) << 3) | ((reg >> 2) & 0x07); + } else { + tb = (reg & _flash_model->tb_offset) ? 1 : 0; + bp = 0; + for (int i = 0; i < _flash_model->bp_len; i++) + if (reg & _flash_model->bp_offset[i]) + bp |= 1 << i; + } + printf("RDSR : %02x\n", reg); + printf("WIP : %d\n", reg&0x01); + printf("WEL : %d\n", (reg>>1)&0x01); + printf("BP : %x\n", bp); + printf("TB : %d\n", tb); + printf("SRWD : %d\n", (((reg>>7)&0x01))); +} + uint8_t SPIFlash::read_status_reg() { uint8_t rx; _spi->spi_put(FLASH_RDSR, NULL, &rx, 1); - if (_verbose > 0) { - printf("RDSR : %02x\n", rx); - printf("WIP : %d\n", rx&0x01); - printf("WEL : %d\n", (rx>>1)&0x01); - printf("BP : %x\n", (((rx>>6)&0x01)<<3) | ((rx >> 2) & 0x07)); - printf("TB : %d\n", (((rx>>5)&0x01))); - printf("SRWD : %d\n", (((rx>>7)&0x01))); - } return rx; } diff --git a/src/spiFlash.hpp b/src/spiFlash.hpp index 44b6c43..24f73c1 100644 --- a/src/spiFlash.hpp +++ b/src/spiFlash.hpp @@ -70,8 +70,10 @@ class SPIFlash { */ bool verify(const int &base_addr, const uint8_t *data, const int &len, int rd_burst = 0); - /* display/info */ + /* return status register value */ uint8_t read_status_reg(); + /* display/info */ + void display_status_reg(uint8_t reg); virtual void read_id(); uint16_t readNonVolatileCfgReg(); uint16_t readVolatileCfgReg(); diff --git a/src/xilinx.cpp b/src/xilinx.cpp index f1832d6..0322cc3 100644 --- a/src/xilinx.cpp +++ b/src/xilinx.cpp @@ -276,7 +276,7 @@ void Xilinx::program_spi(ConfigBitstreamParser * bit, unsigned int offset) SPIFlash spiFlash(this, (_verbose ? 1 : (_quiet ? -1 : 0))); spiFlash.reset(); spiFlash.read_id(); - spiFlash.read_status_reg(); + spiFlash.display_status_reg(spiFlash.read_status_reg()); spiFlash.erase_and_prog(offset, data, length); /* verify write if required */ From c043da874c636b0631cdfea3abeaabcfbb760d1c Mon Sep 17 00:00:00 2001 From: Gwenhael Goavec-Merou Date: Wed, 20 Oct 2021 07:43:47 +0200 Subject: [PATCH 4/5] spiFlash: enable_protection method --- src/spiFlash.cpp | 118 +++++++++++++++++++++++++++++++++++++++++++++-- src/spiFlash.hpp | 12 +++++ 2 files changed, 126 insertions(+), 4 deletions(-) diff --git a/src/spiFlash.cpp b/src/spiFlash.cpp index d5df104..a44c2a1 100644 --- a/src/spiFlash.cpp +++ b/src/spiFlash.cpp @@ -26,6 +26,10 @@ /* write [en|dis]able : 0B addr + 0 dummy */ #define FLASH_WRDIS 0x04 #define FLASH_WREN 0x06 +/* write function register (at least ISSI) */ +#define FLASH_WRFR 0x42 +/* read function register (at least ISSI) */ +#define FLASH_RDFR 0x48 /* Read OTP : 3 B addr + 8 clk cycle*/ #define FLASH_ROTP 0x4B #define FLASH_POWER_UP 0xAB @@ -56,8 +60,9 @@ /* Global Block Protection unlock */ #define FLASH_ULBPR 0x98 -SPIFlash::SPIFlash(SPIInterface *spi, int8_t verbose):_spi(spi), - _verbose(verbose), _jedec_id(0), _flash_model(NULL) +SPIFlash::SPIFlash(SPIInterface *spi, int8_t verbose): + _spi(spi), _verbose(verbose), _jedec_id(0), + _flash_model(NULL) { read_id(); } @@ -200,8 +205,6 @@ int SPIFlash::erase_and_prog(int base_addr, uint8_t *data, int len) } else { uint8_t status = read_status_reg(); if ((status & 0x1c) !=0) { - if (write_enable() != 0) - return -1; if (disable_protection() != 0) return -1; } @@ -404,6 +407,8 @@ int SPIFlash::write_disable() int SPIFlash::disable_protection() { uint8_t data = 0x00; + if (write_enable() == -1) + return -1; _spi->spi_put(FLASH_WRSR, &data, NULL, 1); if (_spi->spi_wait(FLASH_RDSR, 0xff, 0, 1000) < 0) return -1; @@ -416,6 +421,111 @@ int SPIFlash::disable_protection() return 0; } +/* write protect code to status register + * no check for TB + */ +int SPIFlash::enable_protection(uint8_t protect_code) +{ + /* enable write (required to access WRSR) */ + if (write_enable() == -1) { + printError("Error: can't enable write"); + return -1; + } + + /* write status register and wait until Flash idle */ + _spi->spi_put(FLASH_WRSR, &protect_code, NULL, 1); + if (_spi->spi_wait(FLASH_RDSR, 0xff, protect_code, 1000) < 0) { + printError("Error: enable protection failed\n"); + return -1; + } + + /* check status register update */ + if (read_status_reg() != protect_code) { + printError("disable protection failed"); + return -1; + } + if (_verbose > 0) + display_status_reg(read_status_reg()); + + return 0; +} + +int SPIFlash::enable_protection(int length) +{ + /* flash device is not listed: can't know BPx position, nor + * TB offset, nor TB non-volatile vs OTP */ + if (!_flash_model) { + printError("unknown spi flash model: can't lock sectors"); + return -1; + } + + /* convert number of sectors to bp[3:0] mask */ + uint8_t bp = len_to_bp(length); + + /* TB bit is OTP: this modification can't be revert! + * check if tb is already set and if not warn + * current (temporary) policy: do nothing + */ + if (_flash_model->tb_otp) { + uint8_t status; + /* red TB: not aloways in status register */ + switch (_flash_model->tb_register) { + case STATR: // status register + status = read_status_reg(); + break; + case FUNCR: // function register + _spi->spi_put(FLASH_RDFR, NULL, &status, 1); + break; + default: // unknown + printError("Unknown Top/Bottom register"); + return -1; + } + + /* check if TB is set */ + if (!(status & _flash_model->tb_otp)) { + printError("TOP/BOTTOM bit is OTP: can't write this bit"); + return -1; + } + } + + /* if TB is located in status register -> set to 1 */ + if (_flash_model->tb_register == STATR) + bp |= _flash_model->tb_offset; + + /* update status register */ + int ret = enable_protection(bp); + + /* tb is in different register */ + if (_flash_model->tb_register != STATR) { + if (ret == -1) // check if enable_protection has failed + return ret; + /* update register */ + uint8_t reg_wr, reg_rd, val; + if (_flash_model->tb_register == FUNCR) { + val = _flash_model->tb_offset; + reg_wr = FLASH_WRFR; + reg_rd = FLASH_RDFR; + } else { + printError("Unknown TOP/BOTTOM register"); + return -1; + } + + /* write status register and wait until Flash idle */ + _spi->spi_put(reg_wr, &val, NULL, 1); + if (_spi->spi_wait(FLASH_RDSR, 0x03, 0, 1000) < 0) { + printError("Error: enable protection failed\n"); + return -1; + } + _spi->spi_put(reg_rd, &val, NULL, 1); + if (reg_rd != val) { + printError("failed to update TB bit"); + return -1; + } + } + + return ret; +} + /* convert bp area (status register) to len in byte */ uint32_t SPIFlash::bp_to_len(uint8_t bp) { diff --git a/src/spiFlash.hpp b/src/spiFlash.hpp index 24f73c1..8beff85 100644 --- a/src/spiFlash.hpp +++ b/src/spiFlash.hpp @@ -26,6 +26,18 @@ class SPIFlash { * \return -1 if write enable or disabling failed */ int disable_protection(); + /*! + * \brief enable protection for selected blocks + * \param[in] protect_code: bp + tb combinaison + * \return -1 if write enable or enabling failed + */ + int enable_protection(uint8_t protect_code = 0x1c); + /*! + * \brief enable protection for specified area + * \param[in] length: TODO + * \return -1 if write enable or enabling failed + */ + int enable_protection(int len); /*! * \brief unlock all sectors: specific to * Microchip SST26VF032B / SST26VF032BA From 859c1a1a0fb11e47ba62adf2065f620997bb8c03 Mon Sep 17 00:00:00 2001 From: Gwenhael Goavec-Merou Date: Wed, 20 Oct 2021 08:00:21 +0200 Subject: [PATCH 5/5] spiFlash: erase using 4 or 64Kb --- src/spiFlash.cpp | 55 +++++++++++++++++++++++++++++++++++++++++------- src/spiFlash.hpp | 10 ++++++++- 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/src/spiFlash.cpp b/src/spiFlash.cpp index a44c2a1..ffafb93 100644 --- a/src/spiFlash.cpp +++ b/src/spiFlash.cpp @@ -26,12 +26,16 @@ /* write [en|dis]able : 0B addr + 0 dummy */ #define FLASH_WRDIS 0x04 #define FLASH_WREN 0x06 +/* sector (4Kb) erase */ +#define FLASH_SE 0x20 /* write function register (at least ISSI) */ #define FLASH_WRFR 0x42 /* read function register (at least ISSI) */ #define FLASH_RDFR 0x48 /* Read OTP : 3 B addr + 8 clk cycle*/ #define FLASH_ROTP 0x4B +/* block (32Kb) erase */ +#define FLASH_BE32 0x52 #define FLASH_POWER_UP 0xAB #define FLASH_POWER_DOWN 0xB9 /* read/write non volatile register: 0B addr + 0 dummy */ @@ -41,9 +45,9 @@ #define FLASH_RDVCR 0x85 #define FLASH_WRVCR 0x81 /* bulk erase */ -#define FLASH_BE 0xC7 -/* sector (64kb) erase */ -#define FLASH_SE 0xD8 +#define FLASH_CE 0xC7 +/* block (64kb) erase */ +#define FLASH_BE64 0xD8 /* read/write lock register : 3B addr + 0 dummy */ #define FLASH_WRLR 0xE5 #define FLASH_RDLR 0xE8 @@ -71,10 +75,11 @@ int SPIFlash::bulk_erase() { if (write_enable() == -1) return -1; - _spi->spi_put(FLASH_BE, NULL, NULL, 0); + _spi->spi_put(FLASH_CE, NULL, NULL, 0); return _spi->spi_wait(FLASH_RDSR, FLASH_RDSR_WIP, 0x00, 100000, true); } +/* sector -> subsector for micron */ int SPIFlash::sector_erase(int addr) { uint8_t tx[4]; @@ -86,20 +91,54 @@ int SPIFlash::sector_erase(int addr) return 0; } +int SPIFlash::block32_erase(int addr) +{ + uint8_t tx[4] = { + static_cast(FLASH_BE32 ), + static_cast(0xff & (addr >> 16)), + static_cast(0xff & (addr >> 8)), + static_cast(0xff & (addr ))}; + _spi->spi_put(tx, NULL, 4); + return 0; +} + +/* block64 -> sector for micron */ +int SPIFlash::block64_erase(int addr) +{ + uint8_t tx[4] = { + static_cast(FLASH_BE64 ), + static_cast(0xff & (addr >> 16)), + static_cast(0xff & (addr >> 8)), + static_cast(0xff & (addr ))}; + _spi->spi_put(tx, NULL, 4); + return 0; +} + int SPIFlash::sectors_erase(int base_addr, int size) { int ret = 0; int start_addr = base_addr; - int end_addr = (base_addr + size + 0xffff) & ~0xffff; + /* compute end_addr to be multiple of 4Kb */ + int end_addr = (base_addr + size + 0xfff) & ~0xfff; ProgressBar progress("Erasing", end_addr, 50, _verbose < 0); + /* start with block size (64Kb) */ + int step = 0x10000; - for (int addr = start_addr; addr < end_addr; addr += 0x10000) { + for (int addr = start_addr; addr < end_addr; addr += step) { if (write_enable() == -1) { ret = -1; break; } - if (sector_erase(addr) == -1) { - ret = -1; + + /* if block erase + addr end out of end_addr -> use sector_erase */ + if (addr + 0x10000 > end_addr) { + step = 0x1000; + ret = sector_erase(addr); + } else { + ret = block64_erase(addr); + } + + if (ret == -1) { break; } if (_spi->spi_wait(FLASH_RDSR, FLASH_RDSR_WIP, 0x00, 100000, false) == -1) { diff --git a/src/spiFlash.hpp b/src/spiFlash.hpp index 8beff85..590890b 100644 --- a/src/spiFlash.hpp +++ b/src/spiFlash.hpp @@ -47,9 +47,17 @@ class SPIFlash { /* erase */ int bulk_erase(); /*! - * \brief erase one sector (64Kb) + * \brief erase one sector (4Kb) */ int sector_erase(int addr); + /*! + * \brief erase one 32Kb block + */ + int block32_erase(int addr); + /*! + * \brief erase one 64Kb block + */ + int block64_erase(int addr); /*! * \brief erase n sectors starting at base_addr */