cancel
Showing results for 
Search instead for 
Did you mean: 

Nor Flash

vishy
New Contributor

I am interfacing the S25FL512S NOR Flash memory with the STM32f4 microcontroller. I have written my own device driver implementation, supporting the FAST_READ (0x0C), PROGRAM (0x12), and SECTOR_ERASE (0xDC) commands.

All of the three commands have been demonstrated to work. However, SECTOR_ERASE command would successfully erase the sector only about 50% of the time in the current estimation.  

The biggest issue is the fact that when the SECTOR_ERASE command fails, it fails silently, without raising the E_ERR flag in the SR1 register. That means that there is no way to differentiate between the successful SECTOR_ERASE and non-successful SECTOR_ERASE without reading the whole sector and checking if all the byte locations equal to 0xFF.

To show precisely how this silent failure looks like, I am providing you with a screenshot (2 parts, since the signal capture was too long to fit into one) of the signals (captured with a logic analyzer) during the execution of the sector erase sequence. The sequence for erasing the sector is the following:

- RDSR1
- WREN
- RDSR1
- SECTOR_ERASE
- Issue RDSR1 and continuously check if WIP is deasserted

sector_erase_failure_part1.PNG

sector_erase_failure_part2.PNG

 One peculiar thing that stands out each time when SECTOR_ERASE fails is that, the first RDSR1 command (issued directly after SECTOR_ERASE) returns 0xFF, and only the second RDSR1 command returns 0x03 (WEL and WIP bits asserted). After a few RDSR1 commands, the value returned is 0x00, which suggests successful sector erase has been performed. However, after reading the sector, it is obvious that it has not been erased. Also, RDSR1 returns 0x00 only a milisecond or so after the SECTOR_ERASE has been issued, which automatically suggests there is something wrong since SECTOR_ERASE normally takes a few hundred miliseconds to erase the whole sector.

Normally, SECTOR_ERASE is successful every time after power-on-reset. However, it usually fails every second time or so for erasing the same sector after that.

My configuration register is always set to 0xC0 (only latency bits set to 11, indicating that there is no need for the dummy cycles during fast read command). SR1 register is configured to 0x00.

Could you please provide me with any hints of why could this be happening?

Thank you for your time.

0 Likes
1 Solution
BushraH_91
Moderator
Moderator

Hello

From the  trace, the first SR read delivers 0xFF as if the command was somehow ignored and the status register was not really read.

At this point, we are suspecting a signal integrity issue where spikes or unclean clock or IO signals may cause commands to be ignored.

For instance, we see some irregularities on the  clock signal itself where the clock high time and low time are very different. Allowed here is a max discrepancy of 10% not more, e.g: clock low time resp. clock high time should be in the range of [45% - 55%] of the whole clock period.

Thank you

Regards,

Bushra

View solution in original post

0 Likes
14 Replies
BushraH_91
Moderator
Moderator

Hello,

Thank you for contacting Cypress Technical Support, an Infineon Technologies Company. Can you please share the code if possible?

Thank you

Regards,

Bushra

0 Likes
BushraH_91
Moderator
Moderator

Hello,

Can you please see if the issue gets resolved by removing RDSR1 after WREN?

Thank you

Regards,

Bushra

0 Likes
vishy
New Contributor

Hi Bushra,

Thank you for your suggestion. Unfortunately I can confirm that removing the RDSR1 command (to check if WEL bit is set after WREN has been issued) does not resolve the issue.

If that is of any relevance, I can also say that the programming sequence also includes RDSR1 after WREN, and still it seems to work every time (unlike Sector Erase command).

I am looking forward to hearing more from you.

Thank you for your time.

0 Likes
BushraH_91
Moderator
Moderator

Hello,

Thank you for your feedback. Let me internally discuss this issue. Please bear with me.

Regards,

Bushra

BushraH_91
Moderator
Moderator

Further questions:

  • Can you add debug messages in the SW driver so that we determine the exact execution flow of the code? At which point is the failing erase operation exiting? Which line and which error code?
  • Can you share the header file as well for the complete definition of the used macros in the code?
  • What the hex value of the erase command “SECTOR_ERASE_4BYTEADDR_CMD”?
  • Can you share the full captured traces?

 

Thank you

Regards,

Bushra

0 Likes
vishy
New Contributor
  1. What the hex value of the erase command “SECTOR_ERASE_4BYTEADDR_CMD”?
    0xDC

  2. Can you share the header file as well for the complete definition of the used macros in the code?

    In order to make this debugging effort as simple as possible, I have decided to strip-down my sector erase routine from all the code parts that are not relevant to reproducing this problematic behavior. This version of the code is given below:
    #define SECTOR_ERASE_4BYTEADDR_CMD 0xDC
    #define WREN_CMD                   0x06
    #define READ_SR1_CMD               0x05
    
    /* This macro takes Bit position [0-31] as an argument and generates 32-bit mask for the corresponding
    bit position */
    #define M_MASK_BIT(Pos)   (1 << (Pos))
    
    /* Status Register 1 bit fields */
    #define SR1_WIP_Pos   0
    #define SR1_WEL_Pos   1
    #define SR1_E_ERR_Pos 5
    
    teExitResult drv_S25FL512S_SectorErase(SPI_TypeDef * SPIx, uint32_t u32_FlashMemAddr)
    {
    	teExitResult e_exitResult = E_EXIT_FAILURE;
    	uint8_t au8_FlashMemAddrMSB[4];
    	uint8_t u8_command = 0xDC;           /*< Sector Erase Command Code */
    	volatile uint8_t u8_SR1 = 0;
    
    	/* Order Flash memory address from LSB to MSB, since it has to be sent-out MSB */
    	au8_FlashMemAddrMSB[0] = ((uint8_t *)&u32_FlashMemAddr)[3];
    	au8_FlashMemAddrMSB[1] = ((uint8_t *)&u32_FlashMemAddr)[2];
    	au8_FlashMemAddrMSB[2] = ((uint8_t *)&u32_FlashMemAddr)[1];
    	au8_FlashMemAddrMSB[3] = ((uint8_t *)&u32_FlashMemAddr)[0];
    
    	/* Read Status Register 1*/
    	drv_S25FL512S_readSR1(SPIx, &u8_SR1);
    
    	/* If WIP is on, reset Status Register 1 */
    	if(M_MASK_BIT(SR1_WIP_Pos) & u8_SR1)
    	{
    		/* Clear Status Register 1 */
    		drv_S25FL512S_CLSR(SPIx);
    
    		/* Check the new status */
    		drv_S25FL512S_readSR1(SPIx, &u8_SR1);
    	}
    
    	/* Check if Write Enable Latch is enabled */
    	if((M_MASK_BIT(SR1_WEL_Pos) & u8_SR1) == 0)
    	{
    		/* Enable WEL */
    		drv_S25FL512S_WREN(SPIx);
    	}
    
    	/* WEL is enabled, issue the SECTOR ERASE command now */
    	//-------------------------------------------------------
    	/* Drive #CS to low */
    	drv_S25FL512S_CStoLow();
    
    	/* Send the 4-byte command to the external flash device */
    	DRV_SPI_Tx(SPIx, &u8_command, 1);
    
    	/* Send the memory address of the starting byte to read out */
    	DRV_SPI_Tx(SPIx, au8_FlashMemAddrMSB, sizeof(au8_FlashMemAddrMSB));
    	
    	/* Drive #CS to high */
    	drv_S25FL512S_CStoHigh();
    	//-------------------------------------------------------
    	
    	/*DEBUGGING: Read SR1 once, because sometimes the flash device returns 0xFF for the
    	first READ SR1 command that is issued directly after SECTOR ERASE command, as it 
    	can also be seen on the bit trace recodings */
    	drv_S25FL512S_readSR1(SPIx, &u8_SR1);
    
    	/* Wait until WIP deasserted or until Erase Error Flag is raised */
    	while(1)
    	{
    		drv_S25FL512S_readSR1(SPIx, &u8_SR1);
    
    		if((M_MASK_BIT(SR1_WIP_Pos) & u8_SR1) == 0)
    		{
    			/* WIP bit deasserted -> sector erase was successful */
    			e_exitResult = E_EXIT_SUCCESS;
    			break;
    		}
    		else if((M_MASK_BIT(SR1_E_ERR_Pos) & u8_SR1) != 0)
    		{
    			/* Erase Error flag was raised - Attempt resetting the SR1
    			and exit with a failure status */
    			drv_S25FL512S_CLSR(SPIx);
    			e_exitResult = E_EXIT_FAILURE;
    			break;
    		}
    	}
    
    	return e_exitResult;
    }

     

     
  3. Can you share the full captured traces?


    After stripping down the sector erase code, I have went on to capture 2 trace recordings: one of the sector erase sequence where the sector erase routine silently fails, and another one where the sector erase routine was successful. Both recordings were captured with the new version of the code presented above. They have been recorded with the Saleae Logic 2.3.29 software, which can be downloaded from the official Saleae web page. I would recommend inspecting the recorded captures with it. The recordings are attached to this post as trace_recordings.zip

  4. Can you add debug messages in the SW driver so that we determine the exact execution flow of the code? At which point is the failing erase operation exiting? Which line and which error code?

    With the simplified version of the code presented above and the trace recordings provided, it should be pretty straightforward to follow the software execution flow. As it can be seen from the trace recordings, first a command 0x05 (RDSR1) is issued, which corresponds to the  drv_S25FL512S_readSR1(SPIx, &u8_SR1) function in the code. After that, a 0x06 (WREN) is issued, corresponding to the 
    drv_S25FL512S_WREN(SPIx) function. Then, the SECTOR ERASE command 0xDC, following with the address argument 0x00000000 is issued to erase the first flash sector (that contains the address 0x00000000). After that, the software waits in the while(1){} loop, continuously issuing RSDR1 command 0x05 and checking if the WIP bit has been deasserted or E_ERR bit has been asserted. This is the last code section that is always reached. In the case when there is a silent sector erase failure, first RDSR1 command issued directly after SECTOR ERASE command would return 0xFF (as it can be seen on the recorded traces). After that, RDSR1 would return 0x03 for a few times, indicating that the sector erase operation is in progress. Then, after a few RDSR1 operations have been issued, RDSR1 would return 0x00, indicating that the SECTOR ERASE has been successful (when it actually failed). I have to say that even waiting for a few seconds after this happens and then reading the sector does not make a difference (it can be clearly seen that the sector has not been erased). In the case when the SECTOR ERASE command is successful, looping in the while(1){} loop takes around 400ms-500ms until RDSR1 finally returns 0x00. On those occassions, reading the sector reveals that it has indeed been successfully erased.
     
    If you require any further information, please do not hesitate to contact me. I am looking forward to hearing more from you.
0 Likes
BushraH_91
Moderator
Moderator

Hello,

Are you able to monitor Vcc during erase?

Thank you

Regards,

Bushra

0 Likes
BushraH_91
Moderator
Moderator

Hello,

Please send full OPN of the device, schematics and # of Flash devices you tested?

Is it possible to test another Flash device?

Thank you

Regards,

Bushra

0 Likes
vishy
New Contributor

Hi,

The device tested is FL512SDIFG1847QQ140 A.

This is the only device I have tested so far, and at the moment I am not in a possesion of additional units.

The VCC stays constantly high during the whole process. It is actually also recorded in the trace recordings I have provided you with in the last post.

Thank you for your time.

0 Likes
BushraH_91
Moderator
Moderator

Hello,

Please look at the VCC signal from an analog perspective not a digital one. The one you sent is a logic analyzer capture based on a threshold to show two digital signal states only (1 or 0).

More importantly, we’re interested in power drops here which can only be captured only using the analog VCC signal using an oscilloscope.

Thank you

Regards,

Bushra

 

0 Likes
BushraH_91
Moderator
Moderator

Hello

From the  trace, the first SR read delivers 0xFF as if the command was somehow ignored and the status register was not really read.

At this point, we are suspecting a signal integrity issue where spikes or unclean clock or IO signals may cause commands to be ignored.

For instance, we see some irregularities on the  clock signal itself where the clock high time and low time are very different. Allowed here is a max discrepancy of 10% not more, e.g: clock low time resp. clock high time should be in the range of [45% - 55%] of the whole clock period.

Thank you

Regards,

Bushra

View solution in original post

0 Likes
BushraH_91
Moderator
Moderator

Hello, 

There is a big break in the middle of sending the erase command in the failing case while the pass case doesn’t show this huge break.

 

Pass case:

BushraH_91_0-1625151769619.jpeg

Fail case:

 

BushraH_91_1-1625151769624.png

 

 

 

Can you try again with eliminating this huge break?

Can you check the Power-up sequence and any drop on VCC?

Thank you

Regards,

Bushra

0 Likes
vishy
New Contributor

Thank you very much for your time, Bushra.

I will try to analyze the VCC signal from the analogue perspective and get back to you when I do so. 

In the meantime, I have looked into the idea that the silent sector erase failure might be caused by this huge break that happened in the middle of issuing the sector erase command. Unfortunately, it seems like that is not the reason for the observed behavior. I have captured 2 additional counter examples that show:

  1. Sector Erase command succeeding, even though there is a huge break in the middle of issuing the command.
  2. Sector Erase silently failing, even though there is no observed break when issuing the command

Those captures are attached to this post as counter_example_captures.zip

For your convenience, I have also made a snap shot of the example in which the sector erase command was issued without the break, but still failed:

soft-fail-sector-erase.PNG

Regarding the possible VCC drops during the power-up sequence or during the sector erase command, I will try to carefully analyze those and get back to you asap.

Thank you once again for your help.

 

0 Likes
vishy
New Contributor

Yes, sure. So, the sector erase routine currently looks like this:

teExitResult drv_S25FL512S_SectorErase(SPI_TypeDef * SPIx, uint32_t u32_FlashMemAddr)
{
    teExitResult e_exitResult = E_EXIT_FAILURE;
    DRV_SPI_RETURN_VALUES e_spiRetVal = DRV_SPI_OK;
    uint8_t au8_FlashMemAddrMSB[4];
    uint8_t u8_command = SECTOR_ERASE_4BYTEADDR_CMD;
    volatile uint8_t u8_SR1 = 0;
    RCC_ClocksTypeDef RCC_Clocks;
    uint8_t u8_clkCyclesPerMicroSecond = 0;

    /* Check the validity of the input arguments */
    if(IS_SPI_ALL_PERIPH(SPIx) && (u32_FlashMemAddr < FLASH_ADDR_END))
    {
        /* Query the SYSCLK frequency to roughly configure the timeouts used in this function */
        RCC_GetClocksFreq(&RCC_Clocks);
        u8_clkCyclesPerMicroSecond = RCC_Clocks.SYSCLK_Frequency / us_PER_SEC;

        /* Order Flash memory address from LSB to MSB, since it has to be sent-out MSB */
        au8_FlashMemAddrMSB[0] = ((uint8_t *)&u32_FlashMemAddr)[3];
        au8_FlashMemAddrMSB[1] = ((uint8_t *)&u32_FlashMemAddr)[2];
        au8_FlashMemAddrMSB[2] = ((uint8_t *)&u32_FlashMemAddr)[1];
        au8_FlashMemAddrMSB[3] = ((uint8_t *)&u32_FlashMemAddr)[0];

        /* Read Status Register 1*/
        e_exitResult = drv_S25FL512S_readSR1(SPIx, &u8_SR1);
        if(E_EXIT_SUCCESS == e_exitResult)
        {
            /* If programming or erase errors occured before entering this function, clear them */
            if((M_MASK_BIT(SR1_P_ERR_Pos) & u8_SR1)
            || (M_MASK_BIT(SR1_E_ERR_Pos) & u8_SR1))
            {
                /* Clear Status Register 1 */
                e_exitResult = drv_S25FL512S_CLSR(SPIx);
                if(E_EXIT_SUCCESS == e_exitResult)
                {
                    /* Check the new status */
                    e_exitResult = drv_S25FL512S_readSR1(SPIx, &u8_SR1);
                }
            }

            if(E_EXIT_SUCCESS == e_exitResult)
            {
                if(M_MASK_BIT(SR1_WIP_Pos) & u8_SR1)
                {
                    /* Write is currently in progress - return from this function call */
                    e_exitResult = E_EXIT_WAITING;
                }
                else
                {
                    /* Check if Write Enable Latch is enabled */
                    if((M_MASK_BIT(SR1_WEL_Pos) & u8_SR1) == 0)
                    {
                        /* Enable WEL */
                        /* Issue Write Enable command to enable writing to the device */
                        e_exitResult = drv_S25FL512S_WREN(SPIx);

                        if(E_EXIT_SUCCESS == e_exitResult)
                        {
                            /* Wait for the WEL to be enabled */
                            uint8_t u8_timeout = 10;
                            do
                            {
                                e_exitResult = drv_S25FL512S_readSR1(SPIx, &u8_SR1);
                            } while (((M_MASK_BIT(SR1_WEL_Pos) & u8_SR1) == 0) && --u8_timeout);
                            if(0 == u8_timeout)
                            {
                                e_exitResult = E_EXIT_FAILURE;
                            }
                        }
                    }

                    if(E_EXIT_SUCCESS == e_exitResult)
                    {
                        /* WEL is enabled, command can be issued now */

                        /* Drive #CS to low */
                        drv_S25FL512S_CStoLow();

                        /* Send the 4-byte command to the external flash device */
                        e_spiRetVal |= DRV_SPI_Tx(SPIx, &u8_command, 1);

                        /* Send the memory address of the starting byte */
                        e_spiRetVal |= DRV_SPI_Tx(SPIx, au8_FlashMemAddrMSB, sizeof(au8_FlashMemAddrMSB));
                        
                        /* Drive #CS to high */
                        drv_S25FL512S_CStoHigh();
                    }
                }
            }
        }

        if(E_EXIT_SUCCESS == e_exitResult)  
        {
            /* u32_timeout: Absolutely upper limit for waiting for the WIP bit to deassert */
            uint32_t u32_timeout = u8_clkCyclesPerMicroSecond * MAX_SECTOR_ERASE_WAIT_TIME_us;

            /* Wait until WIP deasserted or until Erase Error Flag is raised */
            do
            {
                e_exitResult = drv_S25FL512S_readSR1(SPIx, &u8_SR1);
                if(E_EXIT_SUCCESS == e_exitResult)
                {
                    if((M_MASK_BIT(SR1_WIP_Pos) & u8_SR1) == 0)
                    {
                        /* NOTE: When P_ERR or E_ERR bits are set to one, the WIP bit will remain 
                        set to one indicating the device remains busy and unable to receive new 
                        operation commands */
                        break;
                    }
                    else if((M_MASK_BIT(SR1_E_ERR_Pos) & u8_SR1) != 0)
                    {
                        /* Erase Error flag was raised - Attempt resetting the device 
                        Status register and exit with a failure status */
                        drv_S25FL512S_CLSR(SPIx);
                        e_exitResult = E_EXIT_FAILURE;
                        break;
                    }
                    
                }
            } while (--u32_timeout);
            
            if(0 == u32_timeout)
            {
                e_exitResult = E_EXIT_FAILURE;
            }
        }
    }

    return e_exitResult;
}
 
All the register access functions referenced above are simple routines like:
- CS# to low
- Send command
- Send data, if applicable
- CS# to high
 
Please let me know if you need any additional information.
0 Likes