Skip to content

Conversation

@gautierg-st
Copy link
Contributor

@gautierg-st gautierg-st commented Oct 10, 2025

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.

@gautierg-st gautierg-st changed the title Remove hal reg op STM32: Replace HAL register operations Oct 10, 2025
@gautierg-st gautierg-st added the block: HW Test Testing on hardware required before merging label Oct 10, 2025
@gautierg-st
Copy link
Contributor Author

Waiting for test results to unblock.

Copy link
Contributor

@etienne-lms etienne-lms left a 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.

@gautierg-st gautierg-st force-pushed the remove_hal_reg_op branch 2 times, most recently from 627e39b to a19ec9e Compare October 21, 2025 08:58
@gautierg-st
Copy link
Contributor Author

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 mem_addr_t that was otherwise required everywhere. If new system functions are added in the future, they can easily be used by just updating this new file.

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);
Copy link
Contributor

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:

Suggested change
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);

Copy link
Contributor

@mathieuchopstm mathieuchopstm Oct 21, 2025

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)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

#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)
Copy link
Contributor

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.

Copy link
Contributor

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() / ... ?

Copy link
Contributor Author

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

sys_set_bits((mem_addr_t)addr, mask);
}

static ALWAYS_INLINE void stm32_clear_mask(volatile uint32_t *addr, uint32_t mask)
Copy link
Contributor

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.


static ALWAYS_INLINE uint32_t stm32_read_mask(volatile uint32_t *addr, uint32_t mask)
{
return (*addr) & mask;
Copy link
Contributor

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:

Suggested change
return (*addr) & mask;
return sys_read32((mem_addr_t)addr) & mask;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

sys_clear_bits((mem_addr_t)addr, mask);
}

static ALWAYS_INLINE uint32_t stm32_read_mask(volatile uint32_t *addr, uint32_t mask)
Copy link
Contributor

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".

Comment on lines 28 to 31
static ALWAYS_INLINE bool stm32_test_mask(volatile uint32_t *addr, uint32_t mask)
{
return stm32_read_mask(addr, mask) == mask;
}
Copy link
Contributor

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:

Suggested change
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;
}

Copy link
Contributor

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;
}

Copy link
Contributor Author

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.

Copy link
Contributor

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 :-)

Copy link
Contributor Author

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.

Comment on lines 45 to 47
uint32_t temp = *addr;

*addr = (temp & ~mask) | value;
Copy link
Contributor

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():

Suggested change
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);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@gautierg-st gautierg-st force-pushed the remove_hal_reg_op branch 2 times, most recently from 62a4992 to 3658a6a Compare October 22, 2025 11:48
Copy link
Contributor

@etienne-lms etienne-lms left a 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().

@gautierg-st gautierg-st force-pushed the remove_hal_reg_op branch 2 times, most recently from a5f466b to ea37e79 Compare October 24, 2025 07:17
@gautierg-st
Copy link
Contributor Author

Rebased to fix conflicts.

etienne-lms
etienne-lms previously approved these changes Oct 24, 2025
Copy link
Contributor

@etienne-lms etienne-lms left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LAGTM


uintptr_t addr = (uintptr_t)adc + reg;
size_t reg32_offset = reg / sizeof(uint32_t);
uint32_t *addr = (uint32_t *)config->base + reg32_offset;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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)

Copy link
Contributor

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?

Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines 10 to 15
#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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the generic header:

Suggested change
#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>

Copy link
Contributor Author

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]>
Copy link
Contributor

@etienne-lms etienne-lms left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still LGTM.

@sonarqubecloud
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

platform: STM32 ST Micro STM32

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants