Skip to main content
Klassic
Associate II
June 2, 2023
Solved

Unable to erase external NOR flash memory while read, write works.

I am using STM32L496ZG to interface ISSI IS29GL064-70TLET nor flash by using HAL library. I used CUBE MX to generate the code and running code on Keil compiler. I am able to detect device ID, write and read block. But unable to erase data. I have used example code provided in application note AN4761.

Erase code snippet is below:

WRITE_READ_ADDR 0x8000

NOR_BANK_ADDR 0x64000000

HAL_NOR_ReturnToReadMode(&hnor1);

/* Erase the NOR memory block to write on */

HAL_NOR_Erase_Block(&hnor1, WRITE_READ_ADDR, NOR_BANK_ADDR);

/* Return the NOR memory status */

if(HAL_NOR_GetStatus(&hnor1, NOR_BANK_ADDR, NOR_TIMEOUT_VALUE) !=

HAL_NOR_STATUS_SUCCESS)

{

/* Erase Error */

HAL_UART_Transmit(&huart2, (uint8_t *) "Erase error" , 12, 100);

  return NOR_STATUS_ERASE_ERROR;

}

The status returns erase success. But the bits are still the same, they don't turn to ones at all.

Can any one please help me out on this?

This topic has been closed for replies.
KMINH.1
KMINH.1Best answer
Associate II
November 8, 2023

Thanks everyone for your answers.
The erase operation worked when I shortened the long wires I was using between stm board and flash IC. Perhaps the signal transmission was failing or weak signals.

Anyways, am grateful for the support I got from u all.

29 replies

LLily.2
Associate II
July 8, 2023

I'm not sure if my answer is correct.
You said unable to erase data, maybe you should first erase data -> write data -> read data

Klassic
KlassicAuthor
Associate II
July 25, 2023

I am doing this way Detect -> Erase -> Write -> Read. I am able to write to any location only once. After which, I cannot write different data. After I send erase command, the read cmd does not return FFs but the data that was written first time.
   I am also able to detect device ID correctly.
Thanks for replying. 

Tesla DeLorean
Guru
July 8, 2023

More likely the wrong command, block boundary or protected block. Not sure you're going to be able to diagnose at the top-level .

Pull a data-sheet for the part and review the commands, status in context.

Not many will have this board or part combo.

Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
Klassic
KlassicAuthor
Associate II
July 25, 2023

Thanks Tesla for responding. I am still unable to resolve it. I will try my best to follow suggested comments.
I have used sector addresses as 8000h, 18000h, 10000h to erase and write/read operations.
Below is the code I am using;

HAL_StatusTypeDef HAL_NOR_Erase_Block(NOR_HandleTypeDef *hnor, uint32_t BlockAddress, uint32_t Address)
{
 uint32_t deviceaddress;
 HAL_StatusTypeDef status = HAL_OK;

 /* Check the NOR controller state */
 if (hnor->State == HAL_NOR_STATE_BUSY)
 {
 return HAL_BUSY;
 }
 else if (hnor->State == HAL_NOR_STATE_READY)
 {
 /* Process Locked */
 __HAL_LOCK(hnor);
 /* Update the NOR controller state */
 hnor->State = HAL_NOR_STATE_BUSY;
 /* Select the NOR device address */
 if (hnor->Init.NSBank == FMC_NORSRAM_BANK1)
 {
 deviceaddress = NOR_MEMORY_ADRESS1;
 }
 else if (hnor->Init.NSBank == FMC_NORSRAM_BANK2)
 {
			HAL_UART_Transmit(&huart2, (uint8_t *) "\r\nNOR_MEMORY_ADRESS2 in Erase Block" , 34, 100);
 deviceaddress = NOR_MEMORY_ADRESS2;
 }
 else if (hnor->Init.NSBank == FMC_NORSRAM_BANK3)
 {
 deviceaddress = NOR_MEMORY_ADRESS3;
 }
 else /* FMC_NORSRAM_BANK4 */
 {
 deviceaddress = NOR_MEMORY_ADRESS4;
 }
 /* Send block erase command sequence */
 // if (hnor->CommandSet == NOR_AMD_FUJITSU_COMMAND_SET)
 if(1)
 {
 HAL_UART_Transmit(&huart2, (uint8_t *) "\r\nNOR_AMD_FUJITSU_COMMAND_SET in Erase Block" , 44, 100);
 NOR_WRITE(NOR_ADDR_SHIFT(((uint32_t)deviceaddress), uwNORMemoryDataWidth, NOR_CMD_ADDRESS_FIRST), NOR_CMD_DATA_FIRST);
 NOR_WRITE(NOR_ADDR_SHIFT(((uint32_t)deviceaddress), uwNORMemoryDataWidth, NOR_CMD_ADDRESS_SECOND), NOR_CMD_DATA_SECOND);
 NOR_WRITE(NOR_ADDR_SHIFT(((uint32_t)deviceaddress), uwNORMemoryDataWidth, NOR_CMD_ADDRESS_THIRD),
 NOR_CMD_DATA_CHIP_BLOCK_ERASE_THIRD);
 NOR_WRITE(NOR_ADDR_SHIFT(((uint32_t)deviceaddress), uwNORMemoryDataWidth, NOR_CMD_ADDRESS_FOURTH),
 NOR_CMD_DATA_CHIP_BLOCK_ERASE_FOURTH);
 NOR_WRITE(NOR_ADDR_SHIFT(((uint32_t)deviceaddress), uwNORMemoryDataWidth, NOR_CMD_ADDRESS_FIFTH),
 NOR_CMD_DATA_CHIP_BLOCK_ERASE_FIFTH);
 NOR_WRITE((uint32_t)(BlockAddress + (deviceaddress)), NOR_CMD_DATA_BLOCK_ERASE);
 }
 else if (hnor->CommandSet == NOR_INTEL_SHARP_EXT_COMMAND_SET)
 {
 NOR_WRITE((BlockAddress + Address), NOR_CMD_BLOCK_UNLOCK);
 NOR_WRITE((BlockAddress + Address), NOR_CMD_CONFIRM);
 NOR_WRITE((BlockAddress + Address), NOR_CMD_BLOCK_ERASE);
 NOR_WRITE((BlockAddress + Address), NOR_CMD_CONFIRM);
 }
 else
 {
 /* Primary command set not supported by the driver */
 HAL_UART_Transmit(&huart2, (uint8_t *) "HAL_ERROR in Erase Block" , 24, 100);
 status = HAL_ERROR;
 }
 /* Check the NOR memory status and update the controller state */
 hnor->State = HAL_NOR_STATE_READY;
 /* Process unlocked */
 __HAL_UNLOCK(hnor);
 }
 else
 {
 return HAL_ERROR;
 }
 return status;
}

  I made sure that correct NOR bank address is selected while erasing. 

#define NOR_CMD_ADDRESS_FIRST (uint16_t)0x0555
#define NOR_CMD_ADDRESS_FIRST_CFI (uint16_t)0x0055
#define NOR_CMD_ADDRESS_SECOND (uint16_t)0x02AA
#define NOR_CMD_ADDRESS_THIRD (uint16_t)0x0555
#define NOR_CMD_ADDRESS_FOURTH (uint16_t)0x0555
#define NOR_CMD_ADDRESS_FIFTH (uint16_t)0x02AA
#define NOR_CMD_ADDRESS_SIXTH (uint16_t)0x0555

/* Constants to define data to program a command */
#define NOR_CMD_DATA_READ_RESET (uint16_t)0x00F0
#define NOR_CMD_DATA_FIRST (uint16_t)0x00AA
#define NOR_CMD_DATA_SECOND (uint16_t)0x0055
#define NOR_CMD_DATA_AUTO_SELECT (uint16_t)0x0090
#define NOR_CMD_DATA_PROGRAM (uint16_t)0x00A0
#define NOR_CMD_DATA_CHIP_BLOCK_ERASE_THIRD (uint16_t)0x0080
#define NOR_CMD_DATA_CHIP_BLOCK_ERASE_FOURTH (uint16_t)0x00AA
#define NOR_CMD_DATA_CHIP_BLOCK_ERASE_FIFTH (uint16_t)0x0055

  Also, the commands used match with that of datasheet. I have attached the datasheet.
Thanks

 

Tesla DeLorean
Guru
July 25, 2023

If you think that is correct, then you're going to need to look more critically at the hardware side. The pin connectivity, and the FMC peripheral and pin associativity. Perhaps start by going over you schematic and PCB very carefully. And then checking all the initialization code, and registers set into RCC, GPIO and FMC. Perhaps wire up a logic analyzer so you can verify the signal level expectations.

Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
RhSilicon
Lead
July 25, 2023

In both Flash technologies, data can be written to a block only if the block is empty. The already slow erase operation of NOR Flash makes the write operation even slower. In NAND Flash, similar to read, data is often written or programmed in pages (typically 2KB). For example, a page write alone with S34ML04G2 NAND Flash takes 300µS.

https://www.embedded.com/flash-101-nand-flash-vs-nor-flash/

Pavel A.
July 25, 2023

Looks like you are using outdated NOR driver. Look at the latest one, the erase code has been changed. 

https://github.com/STMicroelectronics/stm32l4xx_hal_driver/blob/17273a9afa668b1ebd899f611dbfb0460b70ba27/Src/stm32l4xx_hal_nor.c#L1097

/* even then, can't attest that their driver works. I wrote my own, for a different ISSI NOR model */

 

Klassic
KlassicAuthor
Associate II
July 26, 2023

HI Pavel,   

  Thanks for replying. 

The new code you sent in the link is same as old one except that the new code adds option for 8 bit data.

I am already using 16 bit data. So for me, no change in code. Can you please show me how did you write own erase code please? The only challenge I see is writing send address/data function like NOR_WRITE(). Sorry for simple question. I am new to memory programming.

Pavel A.
July 26, 2023

Well, found my old project, for STM32H7. The flash there is IS29GL256 (32 MB), also in 16-bit mode. IIRC it has only one size of erase block, 128K (no sub-blocks).

The erase sector function looks pretty same as the ST's one.... but devil is in details.

 

 

#define EXTFL_BASE_ADDR (0x60000000)
#define EXTFL_ERASE_BLK_SIZE (128*1024U)
#define EXTFL_ERASE_TMO 600 /*ms, erase one block, see [1 pg.58]*/

unsigned EXTFL_Block_erase(unsigned nblk)
{
 uint16_t volatile *p = EXTFL_block_and_offset_to_addr(nblk, 0);
 if (!p)
 return 1;
 unsigned start_tick = HAL_GetTick();

 // Procedure: [1 pg.25]
 // Write unlock
 extfl_write_unlock3_(0x80);
 extfl_write_unlock_();
 // Write start erase
 p[0] = 0x30;
 // Poll for completion - simplified. Rely on data toggling.
 bool done = false;
 do {
 if (FL_ReadStatus(nblk, 0) == 0) {
 done = true;
 break;
 }
 } while ((HAL_GetTick() - start_tick) <= EXTFL_ERASE_TMO);

 extfl_cmd_reset_(); // return to read state
 if (!done) {
 if (FL_ReadStatus(0, 0) != 0) {
 return (2|8); // bad status after reset
 }
 return 2; // timeout
 }

 // Verify that all bytes are 0xFF
 // ......
 return 0;
}

// Block#, offset in block -> physical address casted to u16 ptr
// Offset must be even.
static inline uint16_t *EXTFL_block_and_offset_to_addr(unsigned blk, unsigned offs)
{
 return (uint16_t *) (EXTFL_BASE_ADDR + blk * EXTFL_ERASE_BLK_SIZE + offs);
}


// send RESET cmd [1, pg.50]
static void extfl_cmd_reset_(void)
{
 uint16_t volatile *p = EXTFL_block_and_offset_to_addr(0, 0);
 p[0] = 0xF0;
 __DMB();
 __DSB();
}

// write unlock cmd 555/AA,2AA/55
static void extfl_write_unlock_(void)
{
 uint16_t volatile *p = EXTFL_block_and_offset_to_addr(0, 0);
 p[0x555] = 0xAA;
 __DSB();
 p[0x2AA] = 0x55;
 __DSB();
}

// write unlock+cmd: 555/AA,2AA/55, 555/cmd
static void extfl_write_unlock3_(uint8_t cmd3)
{
 uint16_t volatile *p = EXTFL_block_and_offset_to_addr(0, 0);
 extfl_write_unlock_();
 p[0x555] = cmd3;
 __DSB();
}

 

Klassic
KlassicAuthor
Associate II
July 26, 2023

Thanks pavel for the code. I will try this and let you know.

ST Technical Moderator
July 26, 2023

Hello @Klassic, 

Would you check if the blocks are protected? If all blocks are protected, it is possible that they are not erased.  the CHIP ERASE operation appears to start but will leave the data unchanged. No error is reported when protected blocks are not erased.

To give better visibility on the answered topics, please click on "Best answer" on the reply which solved your issue or answered your question.Best regards,FBL
Klassic
KlassicAuthor
Associate II
July 26, 2023

Hi @FBL,

        The current erase operation behaves same way as you said. The erased location has same contents as before, and if I re write on them, the data gets jumbled up due to unsuccessful erase operation.  The erase operation returns success. But how to check whether all sectors are protected?  

Pavel A.
July 29, 2023

Chip erase takes a long time. For my 32 MB chip it was ~ two minutes at optimal voltage.

Below is mass erase function

 

#define EXTFL_ERASE_CHIP_TMO (110*1000) /*ms, erase chip, see [1 pg.58]*/

// Mass erase - poll for completion for <timeout> ms
unsigned EXTFL_Mass_erase_poll(unsigned timeout)
{
 uint16_t volatile *p = EXTFL_block_and_offset_to_addr(0, 0);
 uint32_t start_tick = HAL_GetTick();
 if (timeout == 0 || timeout > EXTFL_ERASE_CHIP_TMO)
 {
 timeout = EXTFL_ERASE_CHIP_TMO;
 }
 // Poll for completion - simplified
 uint16_t w1, w2;
 char fs;
 do {
 fs = FL_ReadStatus(0, 0);
 if (fs == (char)0) {
 break; // finished
 }
 if (fs == 'B') {
 HAL_Delay(5);
 continue; // still busy...
 }
 break;
 } while ((HAL_GetTick() - start_tick) <= timeout);

 if (fs != 0) {
 if (fs != 'B') {
 printf("NOR: After mass erase bad status %c\n", fs);
 }
 return fs;
 }
 w1 = p[0];
 __DMB(); __DSB();
 w2 = p[0];
 if ((w1 & w2) != 0xFFFF)
 return 2; // timeout or other error

 // Verify erase
 // TODO ........... read all?
 return 0;
}

 

 

 

unsigned EXTFL_Mass_erase_begin(void)
{
 // Procedure: [1 pg.25]
 extfl_write_unlock3_(0x80);
 extfl_write_unlock3_(0x10);
 return 0;
}

 

 

No idea why it fails with different base address. And how exactly it fails? Poll timeout? 

Klassic
KlassicAuthor
Associate II
July 31, 2023

@Pavel A. , Can you send code for FL_ReadStatus()? I used HAL library status function for block erase:

if(HAL_NOR_GetStatus(&hnor1, ((uint32_t)0x60000000U), NOR_TIMEOUT_VALUE) !=
HAL_NOR_STATUS_SUCCESS).

                            But I think I need FL_ReadStatus() code for chip erase.
   Another thing, the block erase code worked at least twice. But doesn't work afterwards. I tried many times. I suspect the reading & writing location address is different from erase location. Can u plz check;

#define EXTFL_ERASE_BLK_SIZE 0x1000U
#define NOR_BANK_ADDR ((uint32_t)0x60000000U)

static inline uint16_t *EXTFL_block_and_offset_to_addr(unsigned blk, unsigned offs)
{
 return (uint16_t *) (NOR_BANK_ADDR + blk * EXTFL_ERASE_BLK_SIZE + offs);
}

 Can you break down this; what should be the value of blk & offs to erase at address 0x8000?
Read function;

if(HAL_NOR_ReadBuffer(&hnor1, ((uint32_t)0x60000000U + pAddress), aRxBuffer,BUFFER_SIZE) != HAL_OK)

   pAddress I am using is 0x8000, 0x10000, 0x18000. 
  Poll timeout seems ok for different base address, but read cmd simply returns old data instead of FFs.
Thanks

Pavel A.
July 31, 2023

 

char FL_ReadStatus(unsigned nblk, unsigned int offset)
{
 uint16_t status[3];
 uint16_t toggle_6;
 uint16_t volatile *p = EXTFL_block_and_offset_to_addr(nblk, offset);

 status[0] = p[0];
 __DMB();
 status[1] = p[0];
 __DMB();
 status[2] = p[0];
 toggle_6 = ((status[0] ^ status[1]) & (status[1] ^ status[2])) & (1<<6);

 if (toggle_6) {
 if(status[0] & (1<<1))
 return 'A'; //WRITE_BUFFER_ABORT
 if(status[0] & (1<<5))
 return 'T'; //EXCEEDED_TIME_LIMITS;
 return 'B'; //BUSY;
 } else {
 uint16_t toggle_2;
 toggle_2 = ((status[0])^(status[1])) & (1<<2);

 if (toggle_2)
 return 'S'; //SUSPEND;
 else
 return (char)0; //READY;
 }
}

 

The erase block size of my flash (ISSI IS29GL128/256)  is only 128K, not 4K. and address 0x8000 is within block 0 (0-0x1FFFF). Maybe my code is not compatible with your ISSI flash. Unfortunately I don't have this board now.

On other 'H753 board (designed by the same engineer) I've seen that the ST NOR driver freezes during sector  and mass erase (systick interrupts do not arrive) so simple detection of timeout by counting systick fails. I guessed then that it is effect of CM7 "speculative execution". But you have L4, it should not be subject to this...

 

 

Klassic
KlassicAuthor
Associate II
August 1, 2023

Thanks again Pavel. Will try it out and let you know.
Also, I had to change write_cycle of block erase function according to 64Mb. Or is it same for both? Can u plz check if its ok:

unsigned EXTFL_Block_erase(uint8_t nblk)
{
 uint16_t volatile *p = EXTFL_block_and_offset_to_addr(nblk, 0);
 if (!p)
 return 1;
 unsigned start_tick = HAL_GetTick();

 // Write unlock
 extfl_write_unlock_();
 // Write start erase
 p[0] = 0x30;
 // Poll for completion - simplified. Rely on data toggling.
 /* bool done = false; //omitted polling since HAL lib GetStatus() used after eraseBlock func
 do {
 if (FL_ReadStatus(nblk, 0) == 0) {
 done = true;
 break;
 }
 } while ((HAL_GetTick() - start_tick) <= EXTFL_ERASE_TMO); */
 Reset_Seq();
 extfl_cmd_reset_(); // return to read state
 /* if (!done) {
 if (FL_ReadStatus(0, 0) != 0) {
 return (2|8); // bad status after reset
 }
 return 2; // timeout
 }*/

 // Verify that all bytes are 0xFF
 // ......
 return 0;
}

// Block#, offset in block -> physical address casted to u16 ptr
// Offset must be even.
static inline uint16_t *EXTFL_block_and_offset_to_addr(uint8_t blk, unsigned offs)
{
 return (uint16_t *) (NOR_BANK_ADDR + blk * EXTFL_ERASE_BLK_SIZE + offs);
}


// send RESET cmd [1, pg.50]
static void extfl_cmd_reset_(void)
{
 uint16_t volatile *p = EXTFL_block_and_offset_to_addr(0, 0);
 p[0] = 0xF0;
 __DMB();
 __DSB();
}

static void Reset_Seq(void)
{
 uint16_t volatile *p = EXTFL_block_and_offset_to_addr(0, 0);
 p[0x555] = 0xAA;
 __DSB();
 p[0x2AA] = 0x55;
 __DSB();
}

// write unlock cmd 555/AA,2AA/55
static void extfl_write_unlock_(void)
{
 uint16_t volatile *p = EXTFL_block_and_offset_to_addr(0, 0);
 p[0x555] = 0xAA;
 __DSB();
 p[0x2AA] = 0x55;
 __DSB();
 
 p[0x555] = 0x80;
 __DSB();
 p[0x555] = 0xAA;
 __DSB();
 p[0x2AA] = 0x55;
 __DSB();
}
Pavel A.
August 1, 2023

I no longer have a board with such flash. Just test, step with debugger... Especially check if there are unexplainable long "hangs" when the MCU is totally unresponsible.

 

Klassic
KlassicAuthor
Associate II
August 2, 2023

@Pavel A. actually both the codes (HAL lib and the one you provided) looks same and the problem of erase still remains. 
              The polling sub routine returned ok in Erase block, and there are no hangs. 
I feel there is something else I need to check which I am unable to trace due to lack of my knowledge. 
Plz help.

Pavel A.
August 2, 2023

I could help if I had the board. This kind of problems are hard to debug remotely. For sure you've scanned the flash documentation, checked the commands. Try to request example code from ISSI support.

What about mass erase, did it work?

Klassic
KlassicAuthor
Associate II
August 3, 2023

Mass erase did not work either. No error message to check.
The datasheet says there are 128 sectors with 32KW, meaning 37627 (8000 in hex) locations for each sector. 

Klassic_0-1691036939540.png

  Is it ok to set address location as multiples of 0x8000 in erase block function for sector erase? Or  something else need to be included in code ?

Thanks