Skip to main content
Associate III
September 20, 2024
Question

STM32 HAL Macro functions

  • September 20, 2024
  • 6 replies
  • 6572 views

Hi everyone,

I’ve noticed that STM32 drivers make extensive use of macros, and I’m curious about the reasoning behind this. Is the primary motivation is to increase performance / improve maintainability / are there other factors involved?

This topic has been closed for replies.

6 replies

TDK
September 20, 2024

Because they work and take up no memory. It's common in C and has the least headaches with implementation as you don't need to resolve symbols at the linker stage. Also will be the fastest.

"If you feel a post has answered your question, please click ""Accept as Solution""."
sarunAuthor
Associate III
September 21, 2024

Being the fastest makes sense, but the majority of macros I’ve seen are used for initialization purposes. Is there really a need for such speed during initialization? What would happen if we converted these macros to regular functions or static inline functions instead?

Andrew Neil
Super User
September 21, 2024

@sarun wrote:

 What would happen if we converted these macros to regular functions or static inline functions instead?


An issue with inline is that it's really just a hint to the compiler, so the result becomes is compiler dependant.

 

PS:

"inline does not force inlining; the compiler is free to choose not to inline the function at all, or only in some cases. Different compilers vary in how complex a function they can manage to inline"

https://en.wikipedia.org/wiki/Inline_function#Problems:~:text=inline%20does%20not%20force%20inlining%3B%20the%20compiler%20is%20free%20to%20choose%20not%20to%20inline%20the%20function%20at%20all%2C%20or%20only%20in%20some%20cases.%20Different%20compilers%20vary%20in%20how%20complex%20a%20function%20they%20can%20manage%20to%20inline.

and other issues:

https://en.wikipedia.org/wiki/Inline_function#Problems

 

A complex system that works is invariably found to have evolved from a simple system that worked.A complex system designed from scratch never works and cannot be patched up to make it work.
Andrew Neil
Super User
September 20, 2024

(never mind)

A complex system that works is invariably found to have evolved from a simple system that worked.A complex system designed from scratch never works and cannot be patched up to make it work.
Andrew Neil
Super User
September 20, 2024

@sarun wrote:

I’ve noticed that STM32 drivers make extensive use of macros, and I’m curious about the reasoning behind this


As @TDK said, it's pretty standard C practice.

Can you give an example, and say what alternative implementation(s) you would find less surprising?

 

PS:

Here you have someone asking for more macros:

https://community.st.com/t5/stm32cubeide-mcus/macro-for-different-dma-types/td-p/722443 

A complex system that works is invariably found to have evolved from a simple system that worked.A complex system designed from scratch never works and cannot be patched up to make it work.
sarunAuthor
Associate III
September 21, 2024

Hey, I’m not complaining—I just want to understand the reasoning behind it. Nowadays, many are aiming for MISRA code qualification, so for a product as comprehensive as the HAL, why not take a more compliant approach? From what I understand, using macros in this way may violate MISRA guidelines.

As for alternatives, I’m considering the use of inline functions or regular functions. However, I believe the decision would depend on the performance requirements.

Pavel A.
September 21, 2024

As for alternatives, I’m considering the use of inline functions

This is what the "modern C++" school teaches, and a good idea generally. But a simple example:

 

#define FOO() (*moo)

 

 If macro FOO is not used in the compilation unit and moo is not defined, there's no problem. But if FOO is defined as inline  it will compile and fail when moo is not defined (or has wrong type):

 

inline int FOO(void) { return *moo; }

 

 

 

sarunAuthor
Associate III
September 23, 2024

I’m considering this from a safety and defensive programming standpoint. In that case, choosing normal functions over macros seems like a better idea. Normal functions provide better type safety, prevent unintended side effects, and are easier to debug compared to macros

Andrew Neil
Super User
September 23, 2024

@sarun wrote:

 are easier to debug compared to macros


Is that (necessarily) true?

A complex system that works is invariably found to have evolved from a simple system that worked.A complex system designed from scratch never works and cannot be patched up to make it work.
LCE
Principal II
September 23, 2024

It really depends on the macros.

For example, for the "do-while" macros which have usually a little more code inside, might be replaced with functions without a drawback,

But - I think, no statistics on my side - the mostly used macros in STM32 HAL are those to write / read register / bits.
And replacing these with functions would really bloat the HAL even more.

These would be better replaced by "normal" register access (like: TIM1->CR |= TIM1_CR_EN; ).

TDK
September 23, 2024

Macros also allow the ability to redefine the macro in one spot to affect all of them, unlike functions. Consider this:

assert_param(IS_GPIO_ALL_INSTANCE(GPIOx));

along with its definition:

#ifdef USE_FULL_ASSERT
/**
 * @brief The assert_param macro is used for function's parameters check.
 * @PAram expr: If expr is false, it calls assert_failed function
 * which reports the name of the source file and the source
 * line number of the call that failed.
 * If expr is true, it returns no value.
 * @retval None
 */
 #define assert_param(expr) ((expr) ? (void)0U : assert_failed((uint8_t *)__FILE__, __LINE__))
/* Exported functions ------------------------------------------------------- */
 void assert_failed(uint8_t *file, uint32_t line);
#else
 #define assert_param(expr) ((void)0U)
#endif /* USE_FULL_ASSERT */

 

If USE_FULL_ASSERT is not defined, the code is simply not there at all--zero overhead. There is no way to replace this with a function and retain the same functionality.

 

Since HAL runs on all chips, even the lowly ones with only 16 kB FLASH, this can be necessary. No way to do that if it was defined as a function. So if it were changed, you would be breaking a lot of things.

 

MISRA doesn't flat out forbid macros, or function-like macros.

"If you feel a post has answered your question, please click ""Accept as Solution""."
Andrew Neil
Super User
September 23, 2024

@TDK wrote:

the code is simply not there at all--zero overhead. There is no way to replace this with a function and retain the same functionality..


Is that true?

Are the tools not smart enough to spot an empty function, and emit no code at all?

But that does bring us back to an earlier point: it becomes dependent on the particular compiler (and its settings).

A complex system that works is invariably found to have evolved from a simple system that worked.A complex system designed from scratch never works and cannot be patched up to make it work.
TDK
September 23, 2024

Not sure I follow. If you replaced the macro with a function, why would the function be empty?

To the larger question: I imagine all common compilers will eliminate an empty function entirely under reasonable optimization levels. However, if the function is in a different compilation unit, which it would be here, it would almost certainly require link-time-optimization, which can be especially slow.

"If you feel a post has answered your question, please click ""Accept as Solution""."