cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H743 external loader

PFlor.2
Senior

I went through the ST External QSPI Loader how to and have verified my quadspi.c/h driver is good with the Winbond W25Q64 flash part we are using.  However after I create the .stldr and load it into the STM32 Programmer it will not read from the flash...get Error:data read failed.  I've attached the project if someone could help me out with what could be wrong?

13 REPLIES 13

Are you sure the Winbond W25Q64 part you're using supports Writing Status Register 2 with it's own command?

The 0x6B 1-1-4 command for memory mapping is apt to fail if QE isn't set.

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

Most of the Winbond W25Qxx series support a Write Status Registers command that writes both SR1 and SR2 as a pair of bytes. A smaller subset support WRSR1 and WRSR2 as individual commands.

Given the broad array of stock / parts, and the difficulty in determining which you have mounted, its probably inadvisable to use WRSR2 unless you know it works. You could probably read-back to check, but these and other QSPI frequently don't have a status reporting BAD / INVALID command.

Probably supported on the W25Q64JV, but is that what you are using? Please indicate part marking.

https://community.st.com/t5/stm32-mcus-products/what-are-all-the-things-i-need-to-create-my-external-loader/td-p/158844

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
// sourcer32@gmail.com   https://paypal.me/cliveone
//****************************************************************************

static uint8_t QSPI_ReadStatus(QSPI_HandleTypeDef *hqspi, uint32_t Reg, uint32_t *pStatusReg) // WINBOND
{
  QSPI_CommandTypeDef sCommand = {0};
  uint32_t data = 0;

  sCommand.NbData            = 1;

  switch(Reg)
  {
    case 0 : sCommand.Instruction = READ_STATUS_REG1_CMD; break; // REG1
    case 1 : sCommand.Instruction = READ_STATUS_REG2_CMD; break; // REG2
    case 2 : sCommand.Instruction = READ_STATUS_REG3_CMD; break; // REG3
    case 0x9F : // READID
      sCommand.Instruction   = 0x9F;
      sCommand.NbData        = 3;
      break;
    default:
      return(QSPI_ERROR);
  }

  sCommand.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
  sCommand.AddressMode       = QSPI_ADDRESS_NONE;
  sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
  sCommand.DataMode          = QSPI_DATA_1_LINE;
  sCommand.DummyCycles       = 0;

  sCommand.DdrMode           = QSPI_DDR_MODE_DISABLE;
  sCommand.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;
  sCommand.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;

  if (HAL_QSPI_Command(hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return(QSPI_ERROR);
  }

  if (HAL_QSPI_Receive(hqspi, (void *)&data, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return(QSPI_ERROR);
  }

  if (pStatusReg)
    *pStatusReg = data;

  return(QSPI_OK);
}

//****************************************************************************

static uint8_t QSPI_WriteStatus(QSPI_HandleTypeDef *hqspi, uint32_t Reg, uint32_t StatusReg)
{
  QSPI_CommandTypeDef sCommand = {0};

  sCommand.NbData            = 1;

  switch(Reg)
  {
#if 1 // WINBOND
    case 0 : // REG1
      sCommand.Instruction   = WRITE_STATUS_REG1_CMD;
      sCommand.NbData        = 2; // REG1 and REG2 as 16-bit
      break;
#else
    case 0 : sCommand.Instruction = WRITE_STATUS_REG1_CMD; break; // REG1
    case 1 : sCommand.Instruction = WRITE_STATUS_REG2_CMD; break; // REG2
    case 2 : sCommand.Instruction = WRITE_STATUS_REG3_CMD; break; // REG3
#endif
    default:
      return(QSPI_ERROR);
  }

  sCommand.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
  sCommand.AddressMode       = QSPI_ADDRESS_NONE;
  sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
  sCommand.DataMode          = QSPI_DATA_1_LINE;
  sCommand.DummyCycles       = 0;

  sCommand.DdrMode           = QSPI_DDR_MODE_DISABLE;
  sCommand.DdrHoldHalfCycle  = QSPI_DDR_HHC_ANALOG_DELAY;
  sCommand.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;

  /* Enable write operations */
  if (QSPI_WriteEnable(hqspi) != QSPI_OK)
  {
    return(QSPI_ERROR);
  }

  /* Send the command */
  if (HAL_QSPI_Command(hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return(QSPI_ERROR);
  }

  /* Transmission of the data */
  if (HAL_QSPI_Transmit(hqspi, (void *)&StatusReg, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
  {
    return(QSPI_ERROR);
  }

  return(QSPI_OK);
}

//****************************************************************************

static uint8_t QSPI_ConfigQuad(QSPI_HandleTypeDef *hqspi) // WINBOND
{
  uint32_t Id, Reg1, Reg2;

  if (QSPI_ReadStatus(hqspi, 0x9F, &Id) == QSPI_OK)
  {
    Id &= 0xFFFF;

    if ((Id != 0x40EF) && (Id != 0x60EF)) // WINBOND CHECK
      return(QSPI_ERROR);
  }
  else
  {
    return(QSPI_ERROR);
  }

  if (QSPI_ReadStatus(hqspi, 0, &Reg1) != QSPI_OK)
    return(QSPI_ERROR);

  if (QSPI_ReadStatus(hqspi, 1, &Reg2) != QSPI_OK)
    return(QSPI_ERROR);

#ifdef DEBUG_UART //printf("REG1:%02X REG2:%02X\n", Reg1, Reg2);
  OutString("REG1:");
  OutString(DumpHexEx(Reg1, 2));
  OutString(" REG2:");
  OutString(DumpHexEx(Reg2, 2));
  OutChar('\n');
#endif // DEBUG_UART

  // Unfudge parts where invalid MICRON, MACRONIX and WINBOND sequences have been registered
#if defined(USE_W25Q256) || defined(USE_W25Q512) || defined(USE_W25Q01) || defined(USE_W25Q02)
  if (((Reg2 & 0x42) != 0x02) || ((Reg1 & 0x3C) != 0))
  {
    Reg2 |= 2;      // Set QE
    Reg2 &= ~0x40;  // COM=0
    Reg1 &= ~0x3C;  // Clear BP0,BP1,BP2,BP3
#else
  if (((Reg2 & 0x42) == 0) || ((Reg1 & 0x1C) != 0)) // W25Q64 / W25Q128
  {
    Reg2 |= 2;      // Set QE
    Reg2 &= ~0x40;  // COM=0
    Reg1 &= ~0x1C;  // Clear BP0,BP1,BP2
#endif

    if (QSPI_WriteStatus(hqspi, 0, (Reg2 << 😎 | Reg1) != QSPI_OK) // WINBOND WRITES BOTH
      return(QSPI_ERROR);

    /* Configure automatic polling mode to wait the memory is ready */
    if (QSPI_AutoPollingMemReady(hqspi, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != QSPI_OK)
    {
      //puts("Fail Quad");
      return(QSPI_ERROR);
    }

    if (QSPI_ReadStatus(hqspi, 0, &Reg1) != QSPI_OK)
      return(QSPI_ERROR);

    if (QSPI_ReadStatus(hqspi, 1, &Reg2) != QSPI_OK)
      return(QSPI_ERROR);

#ifdef DEBUG_UART //printf("REG1:%02X REG2:%02X\n", Reg1, Reg2);
  OutString("REG1:");
  OutString(DumpHexEx(Reg1, 2));
  OutString(" REG2:");
  OutString(DumpHexEx(Reg2, 2));
  OutChar('\n');
#endif // DEBUG_UART
  }

  return(QSPI_OK);
}

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

sorry I replied on the wrong post earlier...tried to copy it here but wouldn't take.  Couldn't delete the reply on the other post either, ugh...

https://community.st.com/t5/stm32-mcus-products/what-are-all-the-things-i-need-to-create-my-external-loader/td-p/158844

Yes, the part is the W25Q64JVSIQ.  As I mentioned in the other reply, I've been using this in memory mapped mode with TouchGFX in my application for some time now so I'm pretty confident the CSP_QSPI_EnableMemoryMappedMode() is working.  If I'm understanding you correctly, I believe the memory mapped mode would not work if this was the issue.

In NOR_FLASH mode the Read is done via a memory mapped read of the memory via the ST-LINK

In SPI_FLASH mode the Read is done via Read() functionality of the External Loader.

The reading could give a failure/error if Init() returns an error code, perhaps review that via Verbose Logging 3, or output diagnostics / telemetry from the loader. Or if the memory mapping isn't doing it's job properly. See data returned / dumped.

I don't use CubeMX, main() or vector tables / interrupts in my loaders.

 

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

Do I need something like this?  Should QSPI_Config_Quad() called from CSP_QUADSPI_Init() or where is it used?

This is what I'm using, it is provided as an example.

Not sure this is where your issue is, or not.

Either your Init() code is failing, or the memory mapping is failing, per your description of the fault.

Log the output from STM32 Cube Programmer, with level three setting, and attach that, might be easier to diagnose.

From within the Loader you can provide telemetry via a UART to understand the interactions from that perspective.

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

I did have something wrong in my initialization...so now when I connect the programmer the loader will read the flash at 0x90000000 successfully!  However when I press the READ button after that it fails again and when I try to program it fails on the erase external flash.   See attached log.  So it seems it enter the memory mapped mode after initialization ok but then maybe pressing READ causes it to exit memory mapped mode and it fails. 

Watch how Init() handles a second call. Memory Mapping needs a QSPI Abort or DeInit to terminate, and then restart

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