cancel
Showing results for 
Search instead for 
Did you mean: 

Undetermined Nonvolatile configuration register of MT25QL01GBBB NOR Flash Memory using STM32F765VGT6

Waynsday
Associate II
I have an MT25QL01GBBB8ESF-0SIT controlled by an STM32F765VGT6 microcontroller. This was coded using C and the STM32 HAL in the STM32CubeIDE. It was verified working using different SPI commands such as read JEDEC ID, page program, etc. However, after writing to the nonvolatile configuration register, all the tested code stopped working. 
 
Now, I am trying to fix this issue using the power loss recovery function and interface rescue functions of the flash memory. But the flash memory still does not work properly. Would appreciate help in fixing this issue. Let me know if any more information is needed.
 
I have tried resetting the chip using software and hardware reset pin. I also have an exact duplicate of the chip connected to the same SPI line and verified all the code to be working properly except for the write nonvolatile configuration register function as I did not want to corrupt that chip as well.
 
Currently, I am trying to implement a power loss recovery and interface rescue function based on the documentation of the flash memory. However, results are still not as expected. 
 
Results:
|Results| JEDEC ID | Flag SR  |
|--------| -------- | -------- |
|Expected| 0x20BA21 | 0x80     |
|Actual  | 0xFFFFFF | 0xFF     |
 
ISSUE:
Nonvolatile Configuration Register was set to an undetermined state due to a logical error in the code after sending a write nonvolatile configuration register command. 
SOLUTION: 
Use the power loss recovery sequence in the documentation of the flash memory and reset the nonvolatile configuration register. Big thanks to @Tesla DeLorean for pointing out the issue and providing a solution.
  1. Disable SPI.
  2. Set pin SPI_SCK, SPI_MOSI (DQ0), and HOLD (DQ3) as GPIO pins at very high max output speed.
  3. Insert the clock cycle function in your main.c. Run the GPIO sequence as shown in the code below.
  4. Power Loss Recovery Sequence is completed and extended SPI is now enabled.
  5. Verify working by reading the JEDEC ID of the flash memory.
  6. Reset the Nonvolatile Configuration Register by writing 0xFFFF to the nonvolatile configuration register. Ignore LSB requirement.
//Clock cycle function.
void clock_cycle(uint8_t clocks){
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);
	for(uint8_t i = 0; i<clocks; i++){
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
		HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
	}
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);
}

//Insert this in main function
// DO NOT USE BREAKPOINTS. SEQUENCE MUST BE COMPLETED WITHOUT INTERRUPTION
//Step 1
  printf("Starting step 1 power loss recovery sequence");
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); //DQ3
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET); //DQ0
  clock_cycle(7);
  clock_cycle(9);
  clock_cycle(13);
  clock_cycle(17);
  clock_cycle(25);
  clock_cycle(33);
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); //DQ3
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET); //DQ0

  //Step 2
  printf("Starting step 2 power loss recovery sequence");
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); //DQ3
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET); //DQ0
  clock_cycle(8);
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); //DQ3
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET); //DQ0

  printf("Completed power loss recovery sequence");

 

 

 

 

 

 

 

 

 

Novice
1 ACCEPTED SOLUTION

Accepted Solutions

You're sending a magic sequence of all ONEs to MOSI

You might have to do this sequencing void direct GPIO interaction, not using the SPI peripheral.

And you need a brief break time between sequences with the chip select.

Perhaps use a scope or logic analyzer to confirm the correct pattern if having trouble.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

View solution in original post

10 REPLIES 10

Well reverse_bits() is broken.

Is there a slight delay in csHIGH(FM) so next HIGH/LOW has separation.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Thank you for taking the time.

That seems to be the case. The write nonvolatile register command requires input data of LSB first so I've corrected the function here. This has not been tested yet.

Additionally, there is no delay in csHIGH(FM). I will implement a 1ms delay. Thank you

static uint8_t reverse_bits(uint8_t byte)
{
    byte = (byte & 0xF0) >> 4 | (byte & 0x0F) << 4;
    byte = (byte & 0xCC) >> 2 | (byte & 0x33) << 2;
    byte = (byte & 0xAA) >> 1 | (byte & 0x55) << 1;
    return byte;
}

void MT25QL_WriteNONVOL(SPI_HandleTypeDef *hspi, MT25QL_FM_NO_t FM, uint16_t reg_val)
{
	uint8_t tData[3];
	tData[0] = WRITE_NONVOL_CFG_REG_CMD;
	tData[1] = reverse_bits((reg_val >>8) & 0xFF);
	tData[2] = reverse_bits(reg_val & 0xFF);

	write_enable(hspi, FM);
	csLOW(FM);
	SPI_Write(hspi, tData, 3);
	csHIGH(FM);

	//write enable reset is automatic
}

 

 

Novice

>>I will implement a 1ms delay. 

That's likely to be slow and excessive.

Could you use DWT CYCCNT or a TIM counting at 1 MHz / 1 us (counting, not interrupting)? Or honestly a 50-100 ns

Test by printing content rather than sending to the IC

What are you writing to WRITE_NONVOL_CFG_REG_CMD, and why?

 

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

I will take note of that. I'm still new to coding C so I haven't figured out timers yet.

I am trying to reset bit 0 of the nonvolatile cfg to allow 4-byte addressing in commands by default after next reset.

Screenshot 2024-06-16 195218.png

Novice

Ok, but what pattern did you write to the device?

Not sure that writing random values is advisable or recoverable, especially if you've now put the device in DTR, DUAL or QUAD modes, via a 1-bit SPI interface.

Also you shouldn't need to BIT reverse the vector. It should read/write Little Endian

You can check the 3/4-byte mode via the FLAG STATUS REGISTER

There's a simple command to get it in the right mode ENTER 4-BYTE ADDRESS MODE B7h 

Don't use NON-VOLATILE settings until you've got more experience, and have validated all the read methods.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

I don't know what pattern was written. I coded an initialization function (without testing which was a mistake) that would copy the current nonvolatile cfg, reset bit 0, then write to nonvolatile cfg using the initial code provided which is now the current issue.

There is a section in the documentation called the Power Loss and Interface Rescue functions to reset the device to a fixed state (extended SPI) which is what I'm currently trying to do. However, I am having trouble understanding the documentation and have implemented it as shown below.

However, when testing this code, the device is still inaccessible only returning 0xFF consistently in read ID and other read functions.

Power Loss Recovery and Interface Rescue functions

HAL_StatusTypeDef MT25QL_InterfaceRescue(SPI_HandleTypeDef *hspi, MT25QL_FM_NO_t FM)
{
    // Step 1: Recovery sequence
    recovery_sequence(hspi, FM, 7); // Example for 7 clock cycles
    recovery_sequence(hspi, FM, 9);
    recovery_sequence(hspi, FM, 13);
    recovery_sequence(hspi, FM, 17);
    recovery_sequence(hspi, FM, 25);
    recovery_sequence(hspi, FM, 33);

    // Step 2: Interface rescue
    interface_rescue(hspi, FM);

    return HAL_OK;
}

HAL_StatusTypeDef MT25QL_PowerLossRecovery(SPI_HandleTypeDef *hspi, MT25QL_FM_NO_t FM)
{
    // Step 1: Recovery sequence
    recovery_sequence(hspi, FM, 7); // Example for 7 clock cycles
    recovery_sequence(hspi, FM, 9);
    recovery_sequence(hspi, FM, 13);
    recovery_sequence(hspi, FM, 17);
    recovery_sequence(hspi, FM, 25);
    recovery_sequence(hspi, FM, 33);

    // Step 2: Power Loss Recovery
    power_loss_recovery(hspi, FM);

    return HAL_OK;
}

Helper functions

static void recovery_sequence(SPI_HandleTypeDef *hspi, MT25QL_FM_NO_t FM, uint8_t clock_cycles) {
    uint8_t dummy_data[34] = {DUMMY_CLOCKS}; // Max needed cycles is 34
    csLOW(FM);
    SPI_Write(hspi, dummy_data, clock_cycles);
    csHIGH(FM);
}

static void power_loss_recovery(SPI_HandleTypeDef *hspi, MT25QL_FM_NO_t FM) {
    uint8_t dummy_data[8] = {DUMMY_CLOCKS};
    csLOW(FM);
    SPI_Write(hspi, dummy_data, 8);
    csHIGH(FM);
    //HAL_Delay(1); // Ensure tSHSL2 >= 50ns
}

static void interface_rescue(SPI_HandleTypeDef *hspi, MT25QL_FM_NO_t FM) {
    uint8_t dummy_data[16] = {DUMMY_CLOCKS};
    csLOW(FM);
    SPI_Write(hspi, dummy_data, 16);
    csHIGH(FM);
    //HAL_Delay(1); // Ensure tSHSL2 >= 50ns
}

 

Novice

What's DUMMY_CLOCK in this context? One byte in the array.

Says to set DQ0 Bit HIGH on the pin interface, and DQ3/HOLD pin HIGH, and then clocking

Do you even connect the DQ3/HOLD pin in your design? Use a pull-up?

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

DUMMY_CLOCK is 0xFF in my code. Yes, DQ0 is connected to my MOSI line and DQ3 is held high by default using a GPIO pin.

Question, if I'm understanding this correctly, I should be sending 7 bits, cs HIGH, csLOW, 9 bits, cs HIGH, csLOW, then 13 bits, and so on?

My current implementation sends bytes currently so 7 bytes * 8 clocks / byte = 56 clock cycles.

Waynsday_0-1718550926933.png

 

Novice

You're sending a magic sequence of all ONEs to MOSI

You might have to do this sequencing void direct GPIO interaction, not using the SPI peripheral.

And you need a brief break time between sequences with the chip select.

Perhaps use a scope or logic analyzer to confirm the correct pattern if having trouble.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..