2024-02-13 11:31 AM
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?
Solved! Go to Solution.
2024-12-11 02:34 PM
Just to wrap up the solution to this LONG thread....
Initially I was using TIM6 for the timebase (based on application using FreeRTOS that cannot use SysTick) but the NVIC priority was set to 15. I'm not sure if the priority was the problem or some timeout with the Loader_Src.c Init() function, but the ST Support solution to comment out __set_PRIMASK(0); that enabled interrupts just masked the issue and since the interrupts were no longer running the HAL_GetTick() never incremented so timeout values made no difference and I had HAL_Delay() overridden to use a loop of NOP.
By using SysTick for the Timebase and setting it's NVIC priority to 0 instead of 15 (default) I was able to use enable interrupts in Init() and use the standard HAL_Delay() and HAL_GetTick() just fine and almost everything worked just fine. However, the Chip Erase still failed....Tesla was correct that this was a timeout issue. Basically, I had a timeout value of 20000 (HAL_QSPI_TIMEOUT_DEFAULT_VALUE) I was using for most the QSPI functions. I started using a larger value for erasing the chip, but I was using this larger timeout for issuing the erase chip command when in fact the HAL_QSPI_AutoPolling() is what needed the larger timeout value since that is what is waiting for the erase to complete. Once I modified my AutoPolling function to use a larger timeout when doing a chip erase then that method started working as well. Just multiple misunderstanding of how this loader works on my part on multiple fronts.
#define CSP_QPSI_TIMEOUT_DEFAULT_VALUE 20000
#define CSP_QPSI_MASSERASE_TIMEOUT_VALUE 65000
uint8_t CSP_QSPI_Erase_Chip(void) {
QSPI_CommandTypeDef sCommand = { 0 };
if (QSPI_WriteEnable() != HAL_OK) {
return HAL_ERROR;
}
/* Erasing Sequence --------------------------------- */
sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
sCommand.Instruction = CHIP_ERASE_CMD;
sCommand.AddressMode = QSPI_ADDRESS_NONE;
sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
sCommand.DataMode = QSPI_DATA_NONE;
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, CSP_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
return HAL_ERROR;
}
if (QSPI_AutoPollingMemReady(CSP_QPSI_MASSERASE_TIMEOUT_VALUE) != HAL_OK) {
return HAL_ERROR;
}
return HAL_OK;
}
2024-02-13 11:45 AM
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.
2024-02-13 12:19 PM - edited 2024-02-13 03:53 PM
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.
2024-02-13 12:29 PM - edited 2024-12-09 12:56 PM
// 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);
}
//****************************************************************************
if (QSPI_WriteStatus(hqspi, 0, ((Reg2 << 8) | Reg1) != QSPI_OK) // WINBOND WRITES BOTH
2024-02-13 12:32 PM
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...
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.
2024-02-13 12:39 PM
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.
2024-02-13 01:22 PM
Do I need something like this? Should QSPI_Config_Quad() called from CSP_QUADSPI_Init() or where is it used?
2024-02-13 03:58 PM
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.
2024-02-15 04:12 PM
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.
2024-02-15 05:05 PM
Watch how Init() handles a second call. Memory Mapping needs a QSPI Abort or DeInit to terminate, and then restart