cancel
Showing results for 
Search instead for 
Did you mean: 

Optimizer problem with USBPD

RomThi
Associate III

Hi,

I have an USBPD sink application created with CubeMX that can be found here:
https://github.com/ThiRom/H563NucleoUSBPDSink

 

The project can be build using cmake. If the project is build using Debug configuration, it works. But using Release it shows timeouts on requests. As you can see below during one millisecond the firmware sends 3 retries and a soft reset.  

USBPD.png

This problem can be seen with -O2 and -O3 option. It seems that the file usbpd_timersserver.c is not working correctly. I found that function:

uint32_t USBPD_TIM_IsExpired(TIM_identifier Id)
{
  uint32_t _expired = 1u;
  switch (Id)
  {
    case TIM_PORT0_CRC:
      _expired = TIMX_CHANNEL1_GETFLAG(TIMX);
      break;
    case TIM_PORT0_RETRY:
      _expired = TIMX_CHANNEL2_GETFLAG(TIMX);
      break;
    case TIM_PORT1_CRC:
      _expired = TIMX_CHANNEL3_GETFLAG(TIMX);
      break;
    case TIM_PORT1_RETRY:
      _expired = TIMX_CHANNEL4_GETFLAG(TIMX);
      break;
    default:
      break;
  }
  return _expired;
}

 When I change it to this:

uint32_t USBPD_TIM_IsExpired(TIM_identifier Id)
{
  uint32_t _expired = 1u;
  switch (Id)
  {
    case TIM_PORT0_CRC:
      _expired = TIMX_CHANNEL1_GETFLAG(TIMX);
      break;
    case TIM_PORT0_RETRY:
      _expired = 0;
      break;
    case TIM_PORT1_CRC:
      _expired = TIMX_CHANNEL3_GETFLAG(TIMX);
      break;
    case TIM_PORT1_RETRY:
      _expired = TIMX_CHANNEL4_GETFLAG(TIMX);
      break;
    default:
      break;
  }
  return _expired;
}

It works.

Any idea how to fix it correctly?

Best regards

Roman

 

7 REPLIES 7
FBL
ST Employee

Hi @RomThi 

I think the issue is how the return value _expired is used and how the optimizer sees it, especially inside tight loops. _expired should be either 0 or 1. So I propose the following, 

case TIM_PORT0_RETRY:
      _expired = (TIMX_CHANNEL2_GETFLAG(TIMX) != 0u) ? 1u : 0u;
      break;

_expired initialized to 0 instead of 1 (default should be not expired). Let me know if this does help. 

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.




Best regards,
FBL
RomThi
Associate III

Hi,

thx for your fast response. I have tried what you send without any change.

I also tried this:

    case TIM_PORT0_RETRY:
      return ((READ_BIT(TIMx->SR, TIM_SR_CC1IF) == (TIM_SR_CC1IF)) ? 1UL : 0UL);

Still no change.

Best regards,

Roman

@RomThi Still waiting for dev team feedback, 

You can instrument USBPD_TIM_IsExpired as noinline or lowering optimization on this function as a safety measure

__attribute__((noinline, optimize("O0")))
uint32_t USBPD_TIM_IsExpired(TIM_identifier Id)

 Which compiler are you using? The issue is being tracked internally under CDM0060354 for internal reference only.

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.




Best regards,
FBL
gbm
Principal

@FBL Really?...

how about

bool expired = TIMX_CHANNEL2_GETFLAG(TIMX);

 I think it's the high time for ST folks to learn about boolean type finally. ;)

And even without boolean, the expression would be:

expired = READ_BIT(TIMx->SR, TIM_SR_CC1IF) == (TIM_SR_CC1IF);

No ternary conditional is need to convert 0 to 0 or 1 to 1. ;)

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice
FBL
ST Employee

Hi  @gbm 

You’re absolutely right that using a boolean is clearer here, and that the comparison already produces a 0/1 semantics without needing a ternary.

My earlier suggestion was trying to make the 0/1 normalization explicit based on the existing uint32_t API and to emphasize that we want strict boolean behavior instead of propagating raw bitmasks.

Thank you for the heads up ! ;) 

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.




Best regards,
FBL
RomThi
Associate III

Hi, I have checked this:

/**
  * @brief  check timer expiration
  *   timer id _identifier
  * @retval None
  */
 __attribute__((noinline, optimize("O0")))
uint32_t USBPD_TIM_IsExpired(TIM_identifier Id)
{
  bool _expired = false;
  switch (Id)
  {
    case TIM_PORT0_CRC:
      _expired = TIMX_CHANNEL1_GETFLAG(TIMX);
      break;
    case TIM_PORT0_RETRY:
      _expired = TIMX_CHANNEL2_GETFLAG(TIMX);
      break;
    case TIM_PORT1_CRC:
      _expired = TIMX_CHANNEL3_GETFLAG(TIMX);
      break;
    case TIM_PORT1_RETRY:
      _expired = TIMX_CHANNEL4_GETFLAG(TIMX);
      break;
    default:
      break;
  }
  return _expired;
}

But there was no change. My compiler is:

C:\ST\STM32CubeCLT_1.17.0\GNU-tools-for-STM32\bin>arm-none-eabi-gcc.exe --version
arm-none-eabi-gcc.exe (GNU Tools for STM32 12.3.rel1.20240926-1715) 12.3.1 20230626
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE

It seems the function is optimized out as it is called from the library only.


Best regards

Roman

 

Hi,

there is still no solution for my problem. How can I fix it?

 

Best regards

Roman