Author Topic: C programming readback hardware register without reading  (Read 1328 times)

0 Members and 6 Guests are viewing this topic.

Offline KarelTopic starter

  • Super Contributor
  • ***
  • Posts: 2539
  • Country: 00
C programming readback hardware register without reading
« on: August 17, 2026, 09:48:51 am »
When writing peripheral hardware initialization code for STM32, I often "read" back the same
register after writing in order to make sure the register has received the data before I continue
with the next register. It looks like this:

Code: [Select]
  /* enable the clock for the GPIOA/B/C/D/E peripherals */
  RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN | RCC_AHB2ENR_GPIOBEN | RCC_AHB2ENR_GPIOCEN | RCC_AHB2ENR_GPIODEN | RCC_AHB2ENR_GPIOEEN;
  RCC->AHB2ENR;

For obvious reasons, these registers are declared as "volatile uint32_t" so I assume the compiler will not
optimize away the last line of code in the example above. Correct?

Second question, is there some formal "proof" (e.g. in the GCC manual or in the C standard) that this is
a valid way of reading back? I mean, normally one should do something like "var = RCC->AHB2ENR;" for reading back, right?
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 6398
  • Country: nz
Re: C programming readback hardware register without reading
« Reply #1 on: August 17, 2026, 11:15:46 am »
Yes, if it's correctly declared "volatile" (the thing the pointer points to, not the pointer itself) then that useless 2nd statement can't be deleted as it normally would be and the read will be done.

But it's not going to prove anything. The "volatile" forced the compiler to emit a read instruction, but nothing prevents the CPU from delaying it with respect to following instructions e.g. to wait for the write to happen

If you want to prove something then you should USE the value read. The sensible thing to do would be to compare it to what you wrote and see if they are the same.

Code: [Select]
  int newVal =   RCC->AHB2ENR | RCC_AHB2ENR_GPIOAEN | RCC_AHB2ENR_GPIOBEN | RCC_AHB2ENR_GPIOCEN | RCC_AHB2ENR_GPIODEN | RCC_AHB2ENR_GPIOEEN;
  RCC->AHB2ENR = newVal;
  if (RCC->AHB2ENR != newVal) goto houston_whap;
 

Online ledtester

  • Super Contributor
  • ***
  • Posts: 4115
  • Country: us
Re: C programming readback hardware register without reading
« Reply #2 on: August 17, 2026, 11:26:57 am »

Second question, is there some formal "proof" (e.g. in the GCC manual or in the C standard) that this is
a valid way of reading back? I mean, normally one should do something like "var = RCC->AHB2ENR;" for reading back, right?


Maybe this section in the GCC manual answers the question:

https://gcc.gnu.org/onlinedocs/gcc/Qualifiers-implementation.html

Quote
However, if the volatile storage is not being modified, and the value of the volatile storage is not used, then the situation is less obvious. For example

Code: [Select]
volatile int *src = somevalue;
*src;

According to the C standard, such an expression is an rvalue whose type is the unqualified version of its original type, i.e. int. Whether GCC interprets this as a read of the volatile object being pointed to or only as a request to evaluate the expression for its side effects depends on this type.

If it is a scalar type, or on most targets an aggregate type whose only member object is of a scalar type, or a union type whose member objects are of scalar types, the expression is interpreted by GCC as a read of the volatile object; in the other cases, the expression is only evaluated for its side effects.

When an object of an aggregate type, with the same size and alignment as a scalar type S, is the subject of a volatile access by an assignment expression or an atomic function, the access to it is performed as if the object’s declared type were volatile S.

In any case, no assignment to a variable is needed.
« Last Edit: August 17, 2026, 11:34:02 am by ledtester »
 

Offline Kalvin

  • Super Contributor
  • ***
  • Posts: 2175
  • Country: fi
  • Embedded SW/HW.
Re: C programming readback hardware register without reading
« Reply #3 on: August 17, 2026, 11:40:22 am »
Some registers may have status bits that change state to reflect some hardware state or event.  Some bits may be write only ie you write a bit value "1" but that same bit will read always as "0". Therefore a plain readback is not guaranteed to work as you have expected.  Instead you may need to read the register, apply a bitmask to the value, and then check the value.
 

Offline KarelTopic starter

  • Super Contributor
  • ***
  • Posts: 2539
  • Country: 00
Re: C programming readback hardware register without reading
« Reply #4 on: August 17, 2026, 12:37:08 pm »
So, the "best" answer here https://community.st.com/stm32-mcus-products-25/memory-instruction-barriers-before-writing-to-the-backup-sram-44692
is not correct?

Code: [Select]
PWR->CR |= PWR_CR_DBP;
(void)PWR->CR; // readback to ensure the bit is set before commencing the SRAM/RTC access, as PWR is on APB1 whereas RTC and SRAM are on AHB1
(copied form the link)

I remember to have seen this trick a lot in STM's HAL library code but that was some time ago.
Could be because of the different speeds of the buses.


 

Offline eutectique

  • Frequent Contributor
  • **
  • Posts: 628
  • Country: be
Re: C programming readback hardware register without reading
« Reply #5 on: August 17, 2026, 08:14:36 pm »
When writing peripheral hardware initialization code for STM32, I often "read" back the same register after writing in order to make sure the register has received the data before I continue with the next register.

Looks like voodoo programming. If the value is not written into a register, that peripheral is broken.

I have some vague recollection of a driver (was it Tundra Universe in QNX4 ?) which had lines in the init function:
Code: [Select]
//+ HARDWARE BUG! INIT TWICE!
someCtrlReg = value;
someCtrlReg = value;
//-

 

Offline Kalvin

  • Super Contributor
  • ***
  • Posts: 2175
  • Country: fi
  • Embedded SW/HW.
Re: C programming readback hardware register without reading
« Reply #6 on: August 17, 2026, 08:22:30 pm »
When writing peripheral hardware initialization code for STM32, I often "read" back the same register after writing in order to make sure the register has received the data before I continue with the next register.

Looks like voodoo programming. If the value is not written into a register, that peripheral is broken.

I have some vague recollection of a driver (was it Tundra Universe in QNX4 ?) which had lines in the init function:
Code: [Select]
//+ HARDWARE BUG! INIT TWICE!
someCtrlReg = value;
someCtrlReg = value;
//-

It is not voodoo, it is just instruction pipeline effect that register value may take effect only after a few instruction cycles.
 

Online langwadt

  • Super Contributor
  • ***
  • Posts: 5755
  • Country: dk
Re: C programming readback hardware register without reading
« Reply #7 on: August 17, 2026, 08:26:45 pm »
When writing peripheral hardware initialization code for STM32, I often "read" back the same register after writing in order to make sure the register has received the data before I continue with the next register.

Looks like voodoo programming. If the value is not written into a register, that peripheral is broken.


it will be written, but if the peripheral is running at slower clock it will take a while because it won't happen until the peripheral next slower clock cycle

reading back the register will force the software to wait for the sync to the slower clock to get the read result so at that point the write will have happened


 
The following users thanked this post: Karel

Offline eutectique

  • Frequent Contributor
  • **
  • Posts: 628
  • Country: be
Re: C programming readback hardware register without reading
« Reply #8 on: August 17, 2026, 08:43:05 pm »
It is not voodoo, it is just instruction pipeline effect that register value may take effect only after a few instruction cycles.

I think, a data synchronization barrier (DSB) would be appropriate for such a case.
 

Online langwadt

  • Super Contributor
  • ***
  • Posts: 5755
  • Country: dk
Re: C programming readback hardware register without reading
« Reply #9 on: August 17, 2026, 08:53:21 pm »
It is not voodoo, it is just instruction pipeline effect that register value may take effect only after a few instruction cycles.

I think, a data synchronization barrier (DSB) would be appropriate for such a case.

DSB only guarantees the CPU has done the write on the bus, not that the peripheral with the slower clock has actually synced up and the write taken effect
 
The following users thanked this post: Karel

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 6398
  • Country: nz
Re: C programming readback hardware register without reading
« Reply #10 on: August 18, 2026, 02:44:47 am »
It is not voodoo, it is just instruction pipeline effect that register value may take effect only after a few instruction cycles.

That's not a question about persuading a C compiler to emit an instruction, that's something requiring knowledge of the hardware. Will (as OP wants) writing a value on the bus followed by reading the same IO space register do what they want? You can only know by comsulting the documentation for the specific chip (or ask its maker).
 
The following users thanked this post: Karel, SiliconWizard

Online gf

  • Super Contributor
  • ***
  • Posts: 1826
  • Country: de
Re: C programming readback hardware register without reading
« Reply #11 on: August 18, 2026, 11:31:54 am »
It is not voodoo, it is just instruction pipeline effect that register value may take effect only after a few instruction cycles.

I think, a data synchronization barrier (DSB) would be appropriate for such a case.

DSB only guarantees the CPU has done the write on the bus, not that the peripheral with the slower clock has actually synced up and the write taken effect

Even then, the read-back only guarantees that the store has reached the device register. To ensure that the device has processed the request, a status register poll or an explicit delay may still be required (depending on the device).

While the read-back eliminates the need for DSB for STM32 Device Memory (since store-to-load-forwarding is disabled, or not available at all on Cortex M0...M4), cores with a dual-issue pipeline (such as the Cortex-M7) may still require a DMB after the read-back to prevent undesired instruction reordering if the desired ordering is not implicitly enforced by data dependency. For single-issue cores (e.g., M0–M4), a software compiler barrier should be placed after the read-back to prevent compiler reordering, since volatile memory access is not a replacement for a barrier.
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 17773
  • Country: fr
Re: C programming readback hardware register without reading
« Reply #12 on: August 18, 2026, 03:36:36 pm »
You can rarely cut corners and not read the docs for the specific MCU. Some do have sync bits for that (such as Atmel ARM-based MCUs) that you can poll. Many do not. And it's not always necessary to synchronize either.
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 11140
  • Country: fi
Re: C programming readback hardware register without reading
« Reply #13 on: August 18, 2026, 06:58:24 pm »
I wouldn't do that as a generic pattern, but in some peripherals, you indeed have to do a write, then read back to guarantee it has reached the peripheral (through the clock domain synchronizers), before doing something else - like triggering a start of some operation by writing to some other register (of maybe some other peripheral) - but that would be usually documented, and large majority of peripherals just work by writing what you need in one go. Even for the fairly common "you need to write config bits first, then separately write enable bit" pattern, a read inbetween is not needed because the two writes are guaranteed to go in-order anyway. But, some special snowflake peripheral might need some settling time between the operations and maybe the dummy read is perfect way to achieve that - but not as a generic programming pattern, but a special case for that special peripheral.
 

Offline Boiled-potato

  • Newbie
  • Posts: 2
  • Country: in
    • GitHub
Re: C programming readback hardware register without reading
« Reply #14 on: August 19, 2026, 10:51:17 pm »
Yes, if it's correctly declared "volatile" (the thing the pointer points to, not the pointer itself) then that useless 2nd statement can't be deleted as it normally would be and the read will be done.

But it's not going to prove anything. The "volatile" forced the compiler to emit a read instruction, but nothing prevents the CPU from delaying it with respect to following instructions e.g. to wait for the write to happen

If you want to prove something then you should USE the value read. The sensible thing to do would be to compare it to what you wrote and see if they are the same.

Code: [Select]
  int newVal =   RCC->AHB2ENR | RCC_AHB2ENR_GPIOAEN | RCC_AHB2ENR_GPIOBEN | RCC_AHB2ENR_GPIOCEN | RCC_AHB2ENR_GPIODEN | RCC_AHB2ENR_GPIOEEN;
  RCC->AHB2ENR = newVal;
  if (RCC->AHB2ENR != newVal) goto houston_whap;


cant we use the standard cmsis instruction which would come really handy and avoid a branch instruction.
/* enable the clock for the GPIOA/B/C/D/E peripherals */
  RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN | RCC_AHB2ENR_GPIOBEN | RCC_AHB2ENR_GPIOCEN | RCC_AHB2ENR_GPIODEN | RCC_AHB2ENR_GPIOEEN;
__DMB():// data memory buffer stops all memory operation(read/write) until previous memory operations are finished its the //optimised version of __DSB(); which stops all instructions until previous memory operations are finished ,
  RCC->AHB2ENR;
 

Online gf

  • Super Contributor
  • ***
  • Posts: 1826
  • Country: de
Re: C programming readback hardware register without reading
« Reply #15 on: August 21, 2026, 10:11:35 am »

Regarding

Code: [Select]
/* enable the clock for the GPIOA/B/C/D/E peripherals */
  RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN | RCC_AHB2ENR_GPIOBEN | RCC_AHB2ENR_GPIOCEN | RCC_AHB2ENR_GPIODEN | RCC_AHB2ENR_GPIOEEN;
__DMB():// data memory buffer stops all memory operation(read/write) until previous memory operations are finished its the //optimised version of __DSB(); which stops all instructions until previous memory operations are finished ,
  RCC->AHB2ENR;

  ... access the newly enabled peripherals ...

Below is my humble understanding regarding STM32 AHB2ENR clock enabling.
[ Please correct me if anything is incorrect or if I missed something. ]

The __DMB() instruction has no effect in this context. While its purpose is to prevent memory reordering, Cortex-M cores do not reorder loads or stores to Device Memory anyway, even within dual-issue pipelines. In this code fragment, the read-back sequence (RCC->AHB2ENR;) is what actually guarantees the write buffer is drained before the CPU moves on.

The actual governing factor is the time the clock tree needs to stabilize after writing to AHB2ENR. As soon as the read-back finishes and clears the hazard stall, execution continues. On high-speed cores, the CPU might try to access the newly enabled peripheral (e.g. GPIO) on the very next cycle. Even though the bus keeps everything in order, the newly enabled peripheral itself may not yet be ready.

The required delay depends on how the peripheral is clocked:

  * AHB bus peripherals (GPIOs): Need 2 AHB clock cycles.
  * APB bus peripherals: Need a dynamic delay of 1 + (AHB/APB prescaler) AHB clock cycles.

Because this delay scales with the slower bus clocks instead of CPU speed, you can end up needing a substantial number of processor cycles of delay if you are using a clock prescaler and/or running a high-performance STM32 MCU at high core frequencies.

Special case: If the AHB clock matches the CPU clock, the read-back sequence alone should generate a sufficient timing buffer to meet the stabilization requirements of AHB peripherals.
 

Offline Jeroen3

  • Super Contributor
  • ***
  • Posts: 4561
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
Re: C programming readback hardware register without reading
« Reply #16 on: August 21, 2026, 11:57:55 am »
The barrier instructions have not the intended effect because they do not look beyond the interface of the cortex M to the bus.
I suspect the safest and most obvious and portable way to ensure this will not be optimised is to load into a register volatile.

You can also put in duplicate writes, but today we have AI and static analysis tools, or even juniors, that may misinterpret this.
Or you wrap specific platform behaviour into a macro.

ST's HAL offers these macro's for you so you don't have to worry about this:

Code: (https://github.com/STMicroelectronics/stm32l4xx-hal-driver/blob/master/Inc/stm32l4xx_hal_rcc.h#L750) [Select]
#define __HAL_RCC_GPIOB_CLK_ENABLE()           do { \
                                                 __IO uint32_t tmpreg; \
                                                 SET_BIT(RCC->AHB2ENR, RCC_AHB2ENR_GPIOBEN); \
                                                 /* Delay after an RCC peripheral clock enabling */ \
                                                 tmpreg = READ_BIT(RCC->AHB2ENR, RCC_AHB2ENR_GPIOBEN); \
                                                 UNUSED(tmpreg); \
                                               } while(0)

There is a lot going on here, __IO == volatile
SET_BIT  is just |=
READ_BIT is &
UNUSED is (void)... this suppresses warnings.
I think they put this inside the do..while to ensure it tmpreg doesn't throw a duplicate error.

It is this convoluted because you also want to compile and or test your code on other hardware. Though the need for set/read macros is debatable.
Due to C being C you sometimes have to go to pre-processor acrobatics to get dependency inversion.

---

A read is recommended as it has no side effects. But you could tailor your own and fill this time with other init code in a way to minimise the wait.
« Last Edit: August 21, 2026, 12:00:03 pm by Jeroen3 »
 

Offline jheissjr

  • Regular Contributor
  • *
  • Posts: 151
  • Country: us
Re: C programming readback hardware register without reading
« Reply #17 on: August 21, 2026, 03:17:25 pm »
What is purpose to cast a unit32_t to void? Does it change the output of the compiler?

Code: [Select]
//https://github.com/STMicroelectronics/stm32l4xx-hal-driver/blob/master/Inc/stm32l4xx_hal_def.h
#if !defined(UNUSED)
#define UNUSED(X) (void)X      /* To avoid gcc/g++ warnings */
#endif /* UNUSED */
 

Online langwadt

  • Super Contributor
  • ***
  • Posts: 5755
  • Country: dk
Re: C programming readback hardware register without reading
« Reply #18 on: August 21, 2026, 03:32:55 pm »
What is purpose to cast a unit32_t to void? Does it change the output of the compiler?

Code: [Select]
//https://github.com/STMicroelectronics/stm32l4xx-hal-driver/blob/master/Inc/stm32l4xx_hal_def.h
#if !defined(UNUSED)
#define UNUSED(X) (void)X      /* To avoid gcc/g++ warnings */
#endif /* UNUSED */

it stops the compiler warning you that you are not using the result

 

Online gf

  • Super Contributor
  • ***
  • Posts: 1826
  • Country: de
Re: C programming readback hardware register without reading
« Reply #19 on: August 21, 2026, 03:35:38 pm »
I think they put this inside the do..while to ensure it tmpreg doesn't throw a duplicate error.

The do { ... } while(0) pattern is a common macro idiom used to wrap multiple statements so that __HAL_RCC_GPIOB_CLK_ENABLE(); behaves syntactically like a single statement, even in the context of if-else.

Using a simple block { ... } would fail because the trailing semicolon at the end of __HAL_RCC_GPIOB_CLK_ENABLE(); breaks the if-else structure when

Code: [Select]
if (condition)
  __HAL_RCC_GPIOB_CLK_ENABLE();
else
   ...

expands to

Code: [Select]
if (condition) {
  ...
};
else        // <= syntax error
   ...
 
The following users thanked this post: SiliconWizard

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 17773
  • Country: fr
Re: C programming readback hardware register without reading
« Reply #20 on: August 21, 2026, 05:14:34 pm »
Yes, as to the general code style of the STM HALs, it's heavily directed by their decision to be MISRA-C-compliant, hence the often convoluted constructs (the do-while is again not one of them, it's just for the reason gf gave).

For reading a register, merely using the following statement:
READ_BIT(RCC->AHB2ENR, RCC_AHB2ENR_GPIOBEN);
would be enough. No need to assign the value to a local variable the type of which is a volatile. As long as RCC->AHB2ENR is itself volatile (which it is as with all CMSIS-like register definitions), the read is guaranteed to be executed because the compiler can't assume anything about it and discard the actual read. No need to assign it to a variable (volatile or not) and then discard the result (to silence warnings). You can directly use the struct field as an expression.

If you want to be convinced: https://godbolt.org/z/sza8x5c9v
« Last Edit: August 21, 2026, 05:16:19 pm by SiliconWizard »
 
The following users thanked this post: Jeroen3, Karel

Offline Jeroen3

  • Super Contributor
  • ***
  • Posts: 4561
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
Re: C programming readback hardware register without reading
« Reply #21 on: August 21, 2026, 05:55:49 pm »
Ah yes of course, as I said, pre-processor acrobatics.

And nice godbolt link  :-+
So this is literally enough to get a guaranteed read instruction. Fascinating.
Code: [Select]
(void) RCC->AHB2ENR;

Though this syntax does not read as useful statement, it may throw some tools off...
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 17773
  • Country: fr
Re: C programming readback hardware register without reading
« Reply #22 on: August 21, 2026, 06:18:45 pm »
It is enough to guarantee that the register read is executed, but there's still a difference if you assign the result to a volatile-qualified local variable: in the latter case, after a read from the register is executed, the assignment to the local variable itself will also be executed (because it's itself volatile), and so:
- First the register is read.
- Second it is written to the local variable.

That can be seen in Foo2(): https://godbolt.org/z/133n1rYMv

That adds some delay but also guarantees that the read operation is completed as it must be stored after being read. It's possible on some CPU that only reading the register does not guarantee completion of the operation when the next instruction is executed.

So, I don't think the "generic" HAL code is wrong here. Possibly overkill on some CPU cores, but probably required on the Cortex-M7 and above.
 
The following users thanked this post: jheissjr

Online gf

  • Super Contributor
  • ***
  • Posts: 1826
  • Country: de
Re: C programming readback hardware register without reading
« Reply #23 on: August 21, 2026, 07:00:24 pm »
So this is literally enough to get a guaranteed read instruction. Fascinating.
Code: [Select]
(void) RCC->AHB2ENR;

Though this syntax does not read as useful statement, it may throw some tools off...

Under the C and C++ standards, volatile load and store operations are defined as visible side effects.
Therefore, the compiler is strictly prohibited from optimizing them away, even if they appear to be dead or useless code.
 

Offline Kalvin

  • Super Contributor
  • ***
  • Posts: 2175
  • Country: fi
  • Embedded SW/HW.
Re: C programming readback hardware register without reading
« Reply #24 on: August 21, 2026, 07:15:35 pm »
Probably Zephyr or Linux kernel / drivers provide hints how to do it (in a portable way).
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf