-
Notifications
You must be signed in to change notification settings - Fork 8.1k
STM32: Replace HAL register operations #97329
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
STM32: Replace HAL register operations #97329
Conversation
|
Waiting for test results to unblock. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if all the casts the mem_addr_t could be hidden some way with helper macros or inline functions. Outside of the scope of this P-R.
7b6ab32 to
c3ab6e2
Compare
627e39b to
a19ec9e
Compare
|
New version of the PR: I removed the new functions in sys_bitops.h and instead created a new file in soc/stm32 to add all bitops functions. These new functions use Zephyr bitops functions when possible and hides the cast to |
a19ec9e to
9b9e650
Compare
drivers/adc/adc_stm32wb0.c
Outdated
| const uint32_t shift = 4 * (Conversion & 7); | ||
|
|
||
| MODIFY_REG((&ADCx->SEQ_1)[reg], ADC_SEQ_1_SEQ0 << shift, Channel << shift); | ||
| stm32_modify_reg(&(&ADCx->SEQ_1)[reg], ADC_SEQ_1_SEQ0 << shift, Channel << shift); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be equivalent but more readable:
| stm32_modify_reg(&(&ADCx->SEQ_1)[reg], ADC_SEQ_1_SEQ0 << shift, Channel << shift); | |
| stm32_modify_reg((&ADCx->SEQ_1) + reg, ADC_SEQ_1_SEQ0 << shift, Channel << shift); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Outside PR scope and I'd have to check ASM but it might just be simpler to do:
const volatile uint32_t *reg = (Conversion & 8) ? &ADCx->SEQ_2 : &ADCx->SEQ_1;Edit: no it isn't (-2/4 cycles due to jumps)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
soc/st/stm32/common/stm32_bitops.h
Outdated
| #include <zephyr/arch/common/sys_bitops.h> | ||
| #include <zephyr/arch/common/sys_io.h> | ||
|
|
||
| static ALWAYS_INLINE void stm32_set_mask(volatile uint32_t *addr, uint32_t mask) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest stm32_set_bits for consistency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is dedicated to 32bit SoC registers (pointers to 32bit cells):
stm32_reg_set_bits() / stm32_reg_clear_bits() / stm32_reg_read_masked() / stm32_reg_read() / ... ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I chose stm32_reg_write / stm32_reg_read / stm32_reg_set_bits / stm32_reg_clear_bits / stm32_reg_read_bits / stm32_reg_modify_bits
soc/st/stm32/common/stm32_bitops.h
Outdated
| sys_set_bits((mem_addr_t)addr, mask); | ||
| } | ||
|
|
||
| static ALWAYS_INLINE void stm32_clear_mask(volatile uint32_t *addr, uint32_t mask) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest stm32_clear_bits for consistency.
soc/st/stm32/common/stm32_bitops.h
Outdated
|
|
||
| static ALWAYS_INLINE uint32_t stm32_read_mask(volatile uint32_t *addr, uint32_t mask) | ||
| { | ||
| return (*addr) & mask; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the sys_read32 primitive:
| return (*addr) & mask; | |
| return sys_read32((mem_addr_t)addr) & mask; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
soc/st/stm32/common/stm32_bitops.h
Outdated
| sys_clear_bits((mem_addr_t)addr, mask); | ||
| } | ||
|
|
||
| static ALWAYS_INLINE uint32_t stm32_read_mask(volatile uint32_t *addr, uint32_t mask) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest stm32_read_bits/stm32_read_masked or something that says the read value is masked, rather than "we are reading a mask".
soc/st/stm32/common/stm32_bitops.h
Outdated
| static ALWAYS_INLINE bool stm32_test_mask(volatile uint32_t *addr, uint32_t mask) | ||
| { | ||
| return stm32_read_mask(addr, mask) == mask; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works fine for single-bit fields but is a disaster waiting to happen for multi-bit fields.
Proposal:
| static ALWAYS_INLINE bool stm32_test_mask(volatile uint32_t *addr, uint32_t mask) | |
| { | |
| return stm32_read_mask(addr, mask) == mask; | |
| } | |
| /** | |
| * Test if one bit in the value at @p addr is set. | |
| * | |
| * @param addr MMIO address to read | |
| * @param bit_mask Mask with bit to test set and all others clear | |
| */ | |
| static ALWAYS_INLINE bool stm32_test_bit(volatile uint32_t *addr, uint32_t bit_mask) | |
| { | |
| return stm32_read_bits(addr, bit_mask) == bit_mask; | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For multi-bit fields, another helper should be introduced that consumed the mask too, i.e.:
bool stm32_test_bits(vu32 *addr, u32 field_mask, u32 value) {
return stm32_read_bits(addr, field_mask) == value;
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A function named stm32_test_bit should IMO take a bit argument rather than a bit_mask. After all, that's what all sys_xxx_bit functions do.
Agree about taking a mask and a value to compare to for multi-bit fields.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there is no real consensus on such a test-multi-bits-are-set helper function.
In the end, I one is to call if (stm32_test_maskedxxx(ptr, mask, expected_masked_value)),
I think one would prefer an explicit if (stm32_read_masked(ptr, mask) == expected_masked_value)).
(at least I would :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the test function, and fixed the code everywhere it is needed with an explicit boolean test.
soc/st/stm32/common/stm32_bitops.h
Outdated
| uint32_t temp = *addr; | ||
|
|
||
| *addr = (temp & ~mask) | value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use sys_{read,write}32():
| uint32_t temp = *addr; | |
| *addr = (temp & ~mask) | value; | |
| const mem_addr_t memaddr = (mem_addr_t)addr; | |
| uint32_t temp = sys_read32(mem_addr); | |
| sys_write32((temp & ~mask) | value, mem_addr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
62a4992 to
3658a6a
Compare
3658a6a to
f97903d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few review comments that may be addressed later but an issue to fix in drivers/adc/adc_stm32.c set_reg_value().
a5f466b to
ea37e79
Compare
|
Rebased to fix conflicts. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LAGTM
drivers/adc/adc_stm32.c
Outdated
|
|
||
| uintptr_t addr = (uintptr_t)adc + reg; | ||
| size_t reg32_offset = reg / sizeof(uint32_t); | ||
| uint32_t *addr = (uint32_t *)config->base + reg32_offset; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| uint32_t *addr = (uint32_t *)config->base + reg32_offset; | |
| volatile uint32_t *addr = (volatile uint32_t *)config->base + reg32_offset; |
This should have raised a compiler warning (I didn't check if it did or not)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+ outside PR scope but now that the STM32 helper functions exist, maybe we don't need these {get,set}_reg_value() helpers?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should have raised a compiler warning (I didn't check if it did or not)
The many places where stm32_reg_xxx() are used use a pointer argument that is not an explicit volatile data pointer. I don't think it's needed here. Actaully, I don't think the address argument of these helper functions need to be volatile. The cast is done in the sys_xxx() functions and that should be enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
soc/st/stm32/common/stm32_bitops.h
Outdated
| #include <zephyr/arch/common/sys_bitops.h> | ||
| #ifdef CONFIG_CPU_CORTEX_M | ||
| #include <zephyr/arch/common/sys_io.h> | ||
| #elif defined(CONFIG_CPU_AARCH32_CORTEX_R) || defined(CONFIG_CPU_AARCH32_CORTEX_A) | ||
| #include <zephyr/arch/arm/cortex_a_r/sys_io.h> | ||
| #endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the generic header:
| #include <zephyr/arch/common/sys_bitops.h> | |
| #ifdef CONFIG_CPU_CORTEX_M | |
| #include <zephyr/arch/common/sys_io.h> | |
| #elif defined(CONFIG_CPU_AARCH32_CORTEX_R) || defined(CONFIG_CPU_AARCH32_CORTEX_A) | |
| #include <zephyr/arch/arm/cortex_a_r/sys_io.h> | |
| #endif | |
| #include <zephyr/arch/cpu.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Add a set of bitops functions in order to replace the STM32 HAL bitops macros throughout the drivers. Signed-off-by: Guillaume Gautier <[email protected]>
For all STM32 drivers, replace the SET_BIT macro (defined in the STM32 HAL) by stm32_reg_set_bits defined in Zephyr. Signed-off-by: Guillaume Gautier <[email protected]>
For all STM32 drivers, replace the CLEAR_BIT macro (defined in the STM32 HAL) by stm32_reg_clear_bits defined in Zephyr. Signed-off-by: Guillaume Gautier <[email protected]>
For all STM32 drivers and SoC, replace the READ_BIT macro (defined in the STM32 HAL) by stm32_reg_read_bits. Fixes some cases where the return value was tested like a boolean despite not being one. Signed-off-by: Guillaume Gautier <[email protected]>
For all STM32 drivers and SoC, replace the WRITE_REG macro and the LL_xxx_WriteReg functions (defined in the STM32 HAL) by stm32_reg_write defined in Zephyr. Signed-off-by: Guillaume Gautier <[email protected]>
For all STM32 drivers and SoC, replace the READ_REG macro and the LL_xxx_ReadReg functions (defined in the STM32 HAL) by stm32_reg_read defined in Zephyr. Signed-off-by: Guillaume Gautier <[email protected]>
For all STM32 drivers and SoC, replace the MODIFY_REG macro (defined in the STM32 HAL) by stm32_reg_modify_bits defined in Zephyr. Signed-off-by: Guillaume Gautier <[email protected]>
Call stm32_reg_set_bits instead of stm32_reg_read then stm32_reg_write. Signed-off-by: Guillaume Gautier <[email protected]>
Use dedicated set/clear bits function instead of reading/masking/writing manually. Signed-off-by: Guillaume Gautier <[email protected]>
ea37e79 to
b18ff3c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still LGTM.
|



This PR replaces the STM32 HAL register operations macros (such as WRITE_REG or MODIFY_REG) and functions (such as LL_I2C_WriteReg) by Zephyr equivalents in all STM32 drivers and SoC.