2026-02-04 4:08 AM
Heyho,
I had some really strange error, which went away when turning optimization from "fast" to "off".
The function EthPhyInit() always returned 166 = 0xA6 - it should return "HAL_StatusTypeDef", something between 0 .. 3.
So I changed all the return values to some integer between 1..8 to find out where things go wrong, then added the last line:
uart_printf("EthPhyInit() good ->return 0\n\r");
before returning 0 when all went well.
Still, always got 166 back, even when I got "EthPhyInit() good ->return 0\n\r" on the terminal.
Until I turned optimization OFF, now all is good.
WTF is happening there? Can optimization be that bad?
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/**
* @brief all PHY, ETH MAC & DMA reset and init:
* 1) EthBaseInit() GPIO, clocks, basic stuff
* 2) EthPhyInit() PHY
* 3) EthMacInit() MAC registers
* 4) EthDmaInit() DMA registers
* @PAram -
* @retval HAL status
*/
uint8_t EthAllInit(uint8_t u8SkipBase)
{
static uint32_t u32CallCount = 0;
uint8_t u8RetInit = 0xFF;
u32CallCount++;
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/* base init */
if( 0 == u8SkipBase )
{
u8RetInit = EthBaseInit();
if( u8RetInit != HAL_OK )
{
#if( 1 ) // DEBUG_ETHNETIF
uart_printf(SZC_TEXT_ERR "EthBaseInit() = %u\n\r", u8RetInit);
#endif /* DEBUG_ETHNETIF */
return u8RetInit;
}
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/* PHY initialization and configuration */
u8RetInit = EthPhyInit();
if( u8RetInit != HAL_OK )
{
#if( 1 ) // DEBUG_ETHNETIF
uart_printf(SZC_TEXT_ERR "EthPhyInit() = %u\n\r", u8RetInit); // ##### <- always 166 = 0xA6
uart_printf("u32CallCount = %lu\n\r", u32CallCount);
#endif /* DEBUG_ETHNETIF */
/* do NOT return error
* -> to NOT block other inits
*/
//return u8RetInit;
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/* MACCR: more MAC registers */
u8RetInit = EthMacInit();
...
return u8RetInit;
}
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/**
* @brief ETH PHY initialization and configuration
* @PAram none
* @retval HAL status
*/
uint8_t EthPhyInit(void)
{
/* KSZ8863RLL dual port PHY */
/* wait after reset */
HAL_Delay(50);
/* ++++++++++++++++++++++++++++++++++++++++++++++++ */
if( EthPhyKszAlive() != HAL_OK )
{
uart_printf(SZC_TEXT_ERR "EthPhyInit() 1\n\r");
return 1U; //HAL_ERROR;
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++ */
#if DEBUG_ETHNETIF
uart_printf("PHY KSZ set RMII clock to internal...\n\r");
#endif /* DEBUG_ETHNETIF */
if( EthPhyKszClockSource(1) != HAL_OK )
{
uart_printf(SZC_TEXT_ERR "EthPhyInit() 2\n\r");
return 2U; //HAL_ERROR;
}
/* PHY needs a little time after clock change */
HAL_Delay(50);
/* ++++++++++++++++++++++++++++++++++++++++++++++++ */
/* check LINK status of BOTH PHY ports */
#if DEBUG_ETHNETIF
uart_printf("PHY link check...\n\r");
#endif /* DEBUG_ETHNETIF */
u8PhyPortActive = PHY_PORT_NONE;
/* ETH */
if( EthPhyLinkStatus((uint8_t)PHY_PORT_ETH, ETH_LINK_CHECK_LONG) == HAL_OK )
{
u8PhyPortActive = PHY_PORT_ETH;
}
/* USB if ETH not LINKed */
if( PHY_PORT_NONE == u8PhyPortActive )
{
if( EthPhyLinkStatus((uint8_t)PHY_PORT_USB, ETH_LINK_CHECK_LONG) == HAL_OK )
{
u8PhyPortActive = PHY_PORT_USB;
}
}
#if( 1 ) // DEBUG_ETHNETIF
uart_printf("u8PhyPortActive = %u\n\r", u8PhyPortActive);
#endif /* DEBUG_ETHNETIF */
/* ++++++++++++++++++++++++++++++++++++++++++++++++ */
/* power down unused port */
if( PHY_PORT_NONE != u8PhyPortActive )
{
uint32_t u32PhyRegVal = 0;
volatile uint8_t u8PhyPortUnused = PHY_PORT_NONE;
uint8_t u8RetVal = 0;
UNUSED(u8RetVal);
if( PHY_PORT_ETH == u8PhyPortActive ) u8PhyPortUnused = PHY_PORT_USB;
else u8PhyPortUnused = PHY_PORT_ETH;
u8RetVal = EthPhyPowerDown(u8PhyPortUnused);
#if DEBUG_ETHNETIF
if( u8RetVal != HAL_OK ) uart_printf(SZC_TEXT_ERR "EthPhyPowerDown()\n\r");
else uart_printf("PWRDWN unused port set\n\r");
#endif /* DEBUG_ETHNETIF */
/* check */
HAL_Delay(10);
u8RetVal = EthPhyReadReg(PHY_REG_PWR_DOWN, &u32PhyRegVal, u8PhyPortUnused);
#if DEBUG_ETHNETIF
if( u8RetVal != HAL_OK ) uart_printf(SZC_TEXT_ERR "EthPhyReadReg(PHY_REG_PWR_DOWN)\n\r");
else
{
uart_printf("PHY_REG_PWR_DOWN = %04lX of unused port\n\r", u32PhyRegVal);
if( (u32PhyRegVal & (uint32_t)PHY_REG_BIT_PWR_DOWN) == 0 ) uart_printf(SZC_TEXT_ERR "EthPhyPowerDown() failed\n\r");
}
#endif /* DEBUG_ETHNETIF */
}
/* ++++++++++++++++++++++++++++++++++++++++++++++++ */
/* wait for auto-negotiation complete */
if( PHY_PORT_NONE != u8PhyPortActive )
{
if( EthPhyAutoNegStatus(u8PhyPortActive) == HAL_OK )
{
#if DEBUG_ETHNETIF
uart_printf("AUTO-NEGOTIATION 0 complete\n\r");
#endif /* DEBUG_ETHNETIF */
}
else
{
/* re-start Auto-Negotiation */
if( EthPhyAutoNegRestart(u8PhyPortActive) != HAL_OK )
{
#if( 1 ) // DEBUG_ETHNETIF
uart_printf(SZC_TEXT_ERR "EthPhyAutoNegRestart()\n\r");
#endif /* DEBUG_ETHNETIF */
return 6U; //HAL_ERROR;
}
/* wait for auto-negotiation complete */
if( EthPhyAutoNegStatus(u8PhyPortActive) == HAL_OK )
{
#if DEBUG_ETHNETIF
uart_printf("AUTO-NEGOTIATION 1 complete\n\r");
#endif /* DEBUG_ETHNETIF */
}
else
{
#if( 1 ) // DEBUG_ETHNETIF
uart_printf(SZC_TEXT_ERR "EthPhyAutoNegStatus() TIMEOUT\n\r");
#endif /* DEBUG_ETHNETIF */
return 7U; //HAL_TIMEOUT;
}
}
}
else
{
#if( 1 ) // DEBUG_ETHNETIF
uart_printf(SZC_TEXT_ERR "u8PhyPortActive = PHY_PORT_NONE\n\r");
#endif /* DEBUG_ETHNETIF */
return 8U; //HAL_ERROR;
}
uart_printf("EthPhyInit() good ->return 0\n\r");
return 0U; //HAL_OK;
}
PS:
- STM32H733 on a custom board
- working with that for > 1 year now, with lots of printf for debugging (UART DMA), never had something like this
- same on other boards
- still using STM32CubeIDE 1.10.1
2026-02-04 5:08 AM
> Until I turned optimization OFF, now all is good.
> WTF is happening there? Can optimization be that bad?
Rarely.
But code can be that "bad".
Just drop e.g. a few "volatile" specifiers for peripheral registers or variables in interrupt code, and any optimisation level > 0 will "break" it.
2026-02-04 5:56 AM
... I played a lot with that.
In this case, all of the problems not happening in ISRs, just simple non-OS start-up code / inits, even before the main loop.
The horrifying thing is that this is so simple, telling a function to return 0, but what's coming back is some seemingly random number.
2026-02-04 6:44 AM
There's no problems with the code presented as far as I can tell, even with optimization or cache turned on.
How does uart_printf work? If you use DMA, you will need to handle cache appropriately. Technically, you should cast u8RetInit to (int) in your printf call, though I don't think that's the issue.
2026-02-04 6:51 AM - edited 2026-02-04 6:52 AM
Have you tried stepping it at the instruction level ?
Have you tried any other optimisation settings ?
2026-02-05 1:30 AM
Thanks to all!
uart_printf: DMA yes, cache no, no buffer overwrite possible
casting the u8 doesn't change anything.
BTW, what's the appropriate specifier for printf for a single unsigned byte in gcc?
So far %u / %X worked quite well.
stepping: yes, I see EthPhyInit() might even get to the last uart_printf("EthPhyInit() good ->return 0\n\r") before "return 0", then still seeing "u8RetInit = EthPhyInit();" = 166 :D
other optimization: tried size, that resulted in ... some other problems.
... which makes me check some sources again. Shoot...
2026-02-05 1:48 AM
I meant stepping at the machine (assembler) instruction level - not the C source line level.
2026-02-05 2:25 AM
Oops, okay, don't know how to do that...
Meanwhile, I found some more strange things that do not happen without optimization:
There's a function with a timeout check using HAL_GetTick(), and I was wondering why I got timeouts so quickly, should have been 5 seconds. Then I checked the Ticks and these were all "wrong".
uint8_t EthPhyLinkStatus(volatile uint8_t u8PhyPortIn, uint8_t u8QuickCheck)
{
if( (u8PhyPortIn < PHY_PORT_ETH) || (u8PhyPortIn > PHY_PORT_USB) ) return HAL_ERROR;
uint32_t u32PhyRegVal = 0;
volatile uint32_t u32TickStart;
volatile uint32_t u32TickNow;
volatile uint32_t u32Timeout;
if( ETH_LINK_CHECK_QUICK == u8QuickCheck ) u32Timeout = ETH_TIMEOUT_LINKSTATE_QUICK_MS;
else u32Timeout = ETH_TIMEOUT_LINKSTATE_MS;
u32TickStart = HAL_GetTick();
u32TickNow = HAL_GetTick();
do
{
EthPhyReadReg(PHY_REG_LINK_STATUS, &u32PhyRegVal, u8PhyPortIn);
/* timeout ? */
u32TickNow = HAL_GetTick();
if( (u32TickNow - u32TickStart ) > u32Timeout )
{
break;
}
HAL_Delay(5);
} while( (u32PhyRegVal & (uint32_t)PHY_REG_BIT_LINK_UP) == 0 );
if( (u32PhyRegVal & (uint32_t)PHY_REG_BIT_LINK_UP) == 0 )
{
#if DEBUG_ETHNETIF_ERR
uart_printf(SZC_TEXT_ERR "EthPhyLinkStatus(%u) TIMEOUT %lu ms, u32PhyRegVal = %04lX\n\r", u8PhyPortIn, u32Timeout, u32PhyRegVal);
uart_printf("u32TickStart = %lu\n\r", u32TickStart);
uart_printf("u32TickNow = %lu\n\r", u32TickNow);
#endif /* DEBUG_ETHNETIF_ERR */
return HAL_ERROR;
}
return HAL_OK;
}Here's the UART output I get:
#E EthPhyLinkStatus(1) TIMEOUT 5000 ms, u32PhyRegVal = 008F
u32TickStart = 1190
u32TickNow = 1093
I don't understand anything anymore... why's u32TickStart > u32TickNow ?
2026-02-05 2:29 AM
... and when I remove the "volatile" from the ticks, it's working.
I thought there might be a problem without volatile and optimization, had something like this in a do / while before.
Not a software guy here...
2026-02-06 7:39 AM
Okay, I changed some variables to volatile for communication with the PHY using I2C with interrupts.
Now all works fine, even with opt. "fast".
I have to keep an eye on some more variables...
Still, I don't get:
1) When stepping through (on C level) I see "return 0;", then the return value in the above function gets some other seemingly random value.
2) The timing stuff above using volatile that does not work.
We’re moving the ST Community to a new platform to give you a better and more reliable community experience.