cancel
Showing results for 
Search instead for 
Did you mean: 

STM32G473 QSPI indirect read times out at higher frequencies

ethanleep
Visitor

I have a custom board with an STM32G473 running at 170MHz. I have the QSPI peripheral running correctly in indirect mode when the pre-scaler has a value of 5 (so ~23MHz) but when I decrease the pre-scaler any further all HAL_QSPI_Receive calls end up failing silently. I've tracked it down to the QSPI_WaitFlagStateUntilTimeout returning an error due to what seems like a timeout. Here's a snippet of my initialization code:

 

	flashSPI.Instance = QUADSPI;
	flashSPI.Init.ClockPrescaler = 2;
	flashSPI.Init.FifoThreshold = 1;
	flashSPI.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_NONE;
	flashSPI.Init.FlashSize = 27;
	flashSPI.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_1_CYCLE;
	flashSPI.Init.ClockMode = QSPI_CLOCK_MODE_0;
	flashSPI.Init.FlashID = QSPI_FLASH_ID_1;
	flashSPI.Init.DualFlash = QSPI_DUALFLASH_DISABLE;

	 __HAL_RCC_QSPI_CLK_ENABLE();
	 __HAL_RCC_GPIOA_CLK_ENABLE();
	 __HAL_RCC_GPIOB_CLK_ENABLE();

	// Initialize pins
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Alternate = GPIO_AF10_QUADSPI;

	// Configure NCS pin
	GPIO_InitStruct.Pin = QSPI_NCS_Pin;
	GPIO_InitStruct.Pull = GPIO_PULLUP;
	HAL_GPIO_Init(QSPI_NCS_Port, &GPIO_InitStruct);

	GPIO_InitStruct.Pin = QSPI_CLK_Pin;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	HAL_GPIO_Init(QSPI_CLK_Port, &GPIO_InitStruct);

	GPIO_InitStruct.Pin = QSPI_IO3_Pin;
	HAL_GPIO_Init(QSPI_IO3_Port, &GPIO_InitStruct);

	GPIO_InitStruct.Pin = QSPI_IO2_Pin;
	HAL_GPIO_Init(QSPI_IO2_Port, &GPIO_InitStruct);

	GPIO_InitStruct.Pin = QSPI_IO1_Pin;
	HAL_GPIO_Init(QSPI_IO1_Port, &GPIO_InitStruct);

	GPIO_InitStruct.Pin = QSPI_IO0_Pin;
	HAL_GPIO_Init(QSPI_IO0_Port, &GPIO_InitStruct);

	__HAL_RCC_QSPI_FORCE_RESET();
	__HAL_RCC_QSPI_RELEASE_RESET();
	if(HAL_QSPI_Init(&flashSPI) != HAL_OK) return FC_ERROR;

	// Enable interrupts
	HAL_NVIC_SetPriority(QUADSPI_IRQn, 1, 0);
	HAL_NVIC_EnableIRQ(QUADSPI_IRQn);

	// Check device ID and manufacturer ID
	uint8_t jedec[3] = {0};
	getJEDEC(jedec);
	uint16_t deviceID = (jedec[1] << 8) | (jedec[2]);
	if(jedec[0] != WINBOND_MFR_ID || deviceID != W25N02KV_DEVICE_ID) return FC_ERROR;

 

I tried implementing the "QUADSPI internal timing criticality" fix in the errata but haven't had any luck. Are there certain parameters I'm missing?

5 REPLIES 5

Show the actual code that's failing, and ignoring the error codes.

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

This is the function that fails:

 

// Get the JEDEC ID with a 3 byte array
FC_STATUS flashDriver::getJEDEC(uint8_t *jedec) {
	QSPI_CommandTypeDef command = {0};

	command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
	command.Instruction = READ_JEDC_CMD;
	command.DataMode = QSPI_DATA_1_LINE;
	command.DummyCycles = 8;
	command.NbData = 3;

	if(HAL_QSPI_Command(&flashSPI, &command, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) return FC_ERROR;

	if(HAL_QSPI_Receive(&flashSPI, jedec, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) return FC_ERROR;

	return FC_OK;
}

 

 HAL_QSPI_Command executes as expected but HAL_QSPI_Receive returns all zeroes and takes the whole timeout duration. 

Does it return an error?

    hqspi->State     = HAL_QSPI_STATE_ERROR; 
    hqspi->ErrorCode |= HAL_QSPI_ERROR_TIMEOUT;  

 

Can't say I've fiddled with the clock prescaler, but this has worked for me on the G4's

 

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

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

  sCommand.NbData            = 1;

  switch(Reg)
  {
    case 0 : sCommand.Instruction = READ_STATUS_REG1_CMD; break; // REG1 RDSR 0x05
    case 1 : sCommand.Instruction = READ_STATUS_REG2_CMD; break; // REG2 EQIO 0x35 !1
    case 2 : sCommand.Instruction = READ_STATUS_REG3_CMD; break; // REG3 RDCR
    case 0x9F : // READID
      sCommand.Instruction  = 0x9F;
      sCommand.NbData       = 3;
      break;
  }

  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;

  /* Send the command */
  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);
}

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

 

 

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

HAL_QSPI_Receive returns HAL_ERROR, which I missed before. Sorry about that. Looking at the debugger, the QSPI peripheral exits with an ErrorCode of 1 but State is still HAL_QSPI_STATE_READY.

 

I gave your code a shot and I'm hitting the same issue, still works when the pre-scaler is 5 however. 

TBH I don't know what mechanical dependencies it has on the speed. It just clocks the bus, it doesn't really have any expectations on the response, if you clock too fast you'll just get gibberish back, or it won't accept the command, in which case you'll just clock back pin states.

At higher speeds, and high slew rates, you're apt to get a lot of ringing.

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