cancel
Showing results for 
Search instead for 
Did you mean: 

That is the readback value of a write only register.

WOGoos
Associate II

Hi

I am working on a drivers for the various peripherals on the STM32. Let me take the GPIO as an example. GPIO BSRR is a write only register that I want to read and I need to read back always 0x00000000. Why?

I made a bit-field struct on the whole GPIO block. That struct is accessed based on Register name and specific bit field.

The processor can not operate on individual bits so the compiler translates the Bit field instruction in to a register read followed by mask and shift operations ending with a Register write. It's the compiler that manipulates individual bits in a register in this way.

So the value of the write only register like a BSRR when it is read is important and needs to be 0x00000000 for a bit set operation.

Here is my question, is the read back of a write only register like the BSRR well-defined in the design of the STM32 chip or is that value undefined.

Question:

Can someone tell me if the read back of Write only registers is well-defined as 0x0000000 for the STM32 family of processors.

typedef struct {
	struct  {
		uint32_t  ch00 :4;
		uint32_t  ch01 :4;
		uint32_t  ch02 :4;
		uint32_t  ch03 :4;
		uint32_t  ch04 :4;
		uint32_t  ch05 :4;
		uint32_t  ch06 :4;
		uint32_t  ch07 :4; 
	} CRL;        // 0x44444444, RW
	struct  {
		uint32_t  ch08 :4;
		uint32_t  ch09 :4;
		uint32_t  ch10 :4;
		uint32_t  ch11 :4;
		uint32_t  ch12 :4;
		uint32_t  ch13 :4;
		uint32_t  ch14 :4;
		uint32_t  ch15 :4;  
	} CRH;        // 0x44444444, RW
	struct  {
		uint32_t  idr00 :1;
		uint32_t  idr01 :1;
		uint32_t  idr02 :1;
		uint32_t  idr03 :1;
		uint32_t  idr04 :1;
		uint32_t  idr05 :1;
		uint32_t  idr06 :1;
		uint32_t  idr07 :1;
		uint32_t  idr08 :1;
		uint32_t  idr09 :1;
		uint32_t  idr10 :1;
		uint32_t  idr11 :1;    
		uint32_t  idr12 :1;
		uint32_t  idr13 :1;
		uint32_t  idr14 :1;
		uint32_t  idr15 :1;       
		uint32_t  res   :16;
	} IDR;        // 0x00000000, R
	struct  {
		uint32_t  odr00 :1;
		uint32_t  odr01 :1;
		uint32_t  odr02 :1;
		uint32_t  odr03 :1;
		uint32_t  odr04 :1;
		uint32_t  odr05 :1;
		uint32_t  odr06 :1;
		uint32_t  odr07 :1;
		uint32_t  odr08 :1;
		uint32_t  odr09 :1;
		uint32_t  odr10 :1;
		uint32_t  odr11 :1;    
		uint32_t  odr12 :1;
		uint32_t  odr13 :1;
		uint32_t  odr14 :1;
		uint32_t  odr15 :1;       
		uint32_t  res   :16;
	} ODR;        // 0x00000000, RW 
	struct  {
		uint32_t  bs00 :1;
		uint32_t  bs01 :1;
		uint32_t  bs02 :1;
		uint32_t  bs03 :1;
		uint32_t  bs04 :1;
		uint32_t  bs05 :1;
		uint32_t  bs06 :1;
		uint32_t  bs07 :1;
		uint32_t  bs08 :1;
		uint32_t  bs09 :1;
		uint32_t  bs10 :1;
		uint32_t  bs11 :1;    
		uint32_t  bs12 :1;
		uint32_t  bs13 :1;
		uint32_t  bs14 :1;
		uint32_t  bs15 :1;       
		uint32_t  br00 :1;
		uint32_t  br01 :1;
		uint32_t  br02 :1;
		uint32_t  br03 :1;
		uint32_t  br04 :1;
		uint32_t  br05 :1;
		uint32_t  br06 :1;
		uint32_t  br07 :1;
		uint32_t  br08 :1;
		uint32_t  br09 :1;
		uint32_t  br10 :1;
		uint32_t  br11 :1;    
		uint32_t  br12 :1;
		uint32_t  br13 :1;
		uint32_t  br14 :1;
		uint32_t  br15 :1;       
	} BSRR;       // 0x00000000, W 
	struct  {
		uint32_t  br00 :1;
		uint32_t  br01 :1;
		uint32_t  br02 :1;
		uint32_t  br03 :1;
		uint32_t  br04 :1;
		uint32_t  br05 :1;
		uint32_t  br06 :1;
		uint32_t  br07 :1;
		uint32_t  br08 :1;
		uint32_t  br09 :1;
		uint32_t  br10 :1;
		uint32_t  br11 :1;    
		uint32_t  br12 :1;
		uint32_t  br13 :1;
		uint32_t  br14 :1;
		uint32_t  br15 :1;
		uint32_t  res  :16; 
	} BRR;        // 0x00000000, W  
	struct  {
		uint32_t  lck00 :1;
		uint32_t  lck01 :1;
		uint32_t  lck02 :1;
		uint32_t  lck03 :1;
		uint32_t  lck04 :1;
		uint32_t  lck05 :1;
		uint32_t  lck06 :1;
		uint32_t  lck07 :1;
		uint32_t  lck08 :1;
		uint32_t  lck09 :1;
		uint32_t  lck10 :1;
		uint32_t  lck11 :1;    
		uint32_t  lck12 :1;
		uint32_t  lck13 :1;
		uint32_t  lck14 :1;
		uint32_t  lck15 :1;
		uint32_t  lck16 :1;
		uint32_t  res  :15;
	} LCKR;       // 0x00000000, RW 
} GPIO_RBLK;
 
GPIO_RBLK RegisterBlock;
GPIO_RBLK *GPIO=&RegisterBlock;
 
 
 
#define Reg32(R)	(*(uint32_t*)&R)	
#define Reg16(R)	(*(uint16_t*)&R)
 
int main() {
printf("size:%u\n",sizeof(GPIO_RBLK));
GPIO->CRL.ch00=0x0u;
GPIO->CRL.ch01=0x1u;
GPIO->CRL.ch02=0x2u;
GPIO->CRL.ch03=0x3u;
Reg32(GPIO->CRL)=0xAABBCCDD;
printf("CRL:%08X\n",Reg32(GPIO->CRL));
 
HEX_MEM((uint8_t*)GPIO);
}

14 REPLIES 14

MPU or MCU ?

Don't expect Peripheral registers with logic behind them to act like regular memory cells.

Accessing the data registers for SPI and UART may touch completely unrelated logic.

BSRR maps to a latch/gate structure operating on the ODR, so you can modify in a single action rather than a RMW (Read-Modify-Write) one.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
WOGoos
Associate II

Yes I know about the RMW vs Atomic action. However I'm looking for the answer if the design of the chip is done in a way that a Read from a write only register gives 0x00000000. I did a small test with 10milj reads from this BSRR and yes it gave me back only 0x00000000 it seems that the design of the chip doesn't return and undefined values on this BSRR. I would however like to know if it was a design requirement.

PatrickF
ST Employee

Hi @WOGoos​ ,

I think in most case, a write only register will read back as 0 (i.e. by design, if there is no read path on APB bit, the value must be 0), however, due to variety of design/bus/IP providers, we cannot guarantee that over all STM32 IPs.

E.g. There could be bus errors returned on read, or read value not always at 0 because linked to some hidden internal state.

In general, bit structures on registers should be avoided as known to be not portable (behavior and padding depend on compiler).

Sometimes, when talking low level drivers, elegant coding may not be optimized. Brute force coding is usually more robust.

E.g. in your example the compiler will do RMW whereas it is useless on a bit set/reset write only register. Apart functionality behavior, this could impact performance as you will loose the advantage of having set/reset registers. A write could be posted on the processor and/or on busses (i.e. might be 0 wait state from processor view) whereas the additional read will create additional wait states (as it usually flushes all the pending writes and should wait for the read data to be completely done from peripheral to processor).

Regards.

In order to give better visibility on the answered topics, please click on 'Accept as Solution' on the reply which solved your issue or answered your question.

Yes, all what Clive and PatrickF said above, and I also find it silly to RMW the BSRR whatever noble reasons are behind it. You did not care to tell us, but given the CRx registers you are using 'F1xx which is probably the only family which does not guarantee BSRR being read as 0 in the RM. Btw. as the 'F1 GPIO is different from all other STM32 families' GPIO, general portability in this particular case is not an issue.

And, speaking of noble reasons:

> The processor can not operate on individual bits

That's not true generally. There are processors which can operate on individual bits, e.g. 8051, or Cortex-M3/4 through bit-banding (that the latter is RMW in hardware is a different thing again). So compiler may very well use whatever method they deem appropriate to set individual bits of a bitfield, including using those hardware facilities.

> so the compiler translates the Bit field instruction in to a register read followed by mask and shift

> operations ending with a Register write. It's the compiler that manipulates individual bits in a register in this way.

It's a particular compiler which does this in this way, it's not a requirement on the compiler. Read, it's not a portable or in any way guaranteed behaviour.

The true issue here is, that this is a volatile access (not in the code you presented above, but that's in fact an error on your side). And, according to C99 6.7.3, it's implementation-defined what constitutes an access to volatile-qualified object. In other words, the compiler may - and if desired, should - be made in that what that for particular hardware registers it performs a particular operation.

This is not as shockingly unseen as it may sound, for example the avr-gcc compiler "knows" that 16-bit timer registers (i.e. dedicated register-pairs) have to be accessed in a certain order (again for hardware-implemented atomicity reasons).

JW

#include <stdint.h>
typedef union {
  volatile struct  {
    uint32_t  bs00 :1;
    uint32_t  bs01 :1;
    uint32_t  bs02 :1;
    uint32_t  bs03 :1;
    uint32_t  bs04 :1;
    uint32_t  bs05 :1;
    uint32_t  bs06 :1;
    uint32_t  bs07 :1;
    uint32_t  bs08 :1;
    uint32_t  bs09 :1;
    uint32_t  bs10 :1;
    uint32_t  bs11 :1;
    uint32_t  bs12 :1;
    uint32_t  bs13 :1;
    uint32_t  bs14 :1;
    uint32_t  bs15 :1;
    uint32_t  br00 :1;
    uint32_t  br01 :1;
    uint32_t  br02 :1;
    uint32_t  br03 :1;
    uint32_t  br04 :1;
    uint32_t  br05 :1;
    uint32_t  br06 :1;
    uint32_t  br07 :1;
    uint32_t  br08 :1;
    uint32_t  br09 :1;
    uint32_t  br10 :1;
    uint32_t  br11 :1;
    uint32_t  br12 :1;
    uint32_t  br13 :1;
    uint32_t  br14 :1;
    uint32_t  br15 :1;
  };
  uint32_t all;
} GPIO_BSRR;
 
 
typedef struct {
  struct  {
    uint32_t  ch00 :4;
    uint32_t  ch01 :4;
[...]
    uint32_t  odr15 :1;
    uint32_t  res   :16;
  } ODR;        // 0x00000000, RW
  GPIO_BSRR BSRR;
  struct  {
    uint32_t  br00 :1;
    uint32_t  br01 :1;
[...]
    uint32_t  lck16 :1;
    uint32_t  res  :15;
  } LCKR;       // 0x00000000, RW
} GPIO_RBLK;
 
 
volatile GPIO_RBLK * const GPIOA = (volatile GPIO_RBLK *)0x40010800;
 
int main(void) {
  GPIOA->BSRR.all = (GPIO_BSRR){.bs03 = 1}.all;
}

compiling by arm-none-eabi-gcc (GNU Tools for ARM Embedded Processors) 4.8.3 20131129 (release) [ARM/embedded-4_8-branch revision 205641] results in

08000204 <main>:
 
 
volatile GPIO_RBLK * const GPIOA = (volatile GPIO_RBLK *)0x40000000;
 
int main(void) {
  GPIOA->BSRR.all = (GPIO_BSRR){.bs03 = 1}.all;
 8000204:	f04f 4380 	mov.w	r3, #1073741824	; 0x40000000
 8000208:	2208      	movs	r2, #8
 800020a:	611a      	str	r2, [r3, #16]
}
 800020c:	2000      	movs	r0, #0
 800020e:	4770      	bx	lr
 
08000210 <GPIOA>:
 8000210:	0000 4000                                   ...@

JW

I like your message, but it seems that you know more C tricks than I do. I understand that you try to show the Assembly translation of your code, however I have problems to read it.

Hi Patrick thanks for helping me. I agree with the non portability problem. I did some further digging and it turned out that at page 46 of Chapter2 of the RM0008 manual it states write-only (w) Software can only write to this bit. Reading the bit returns the reset value.

I did a test of 10mil reads and it did return all zeros. It seems to be by design.

The RMW is a direct need when you work with bit fields in C. You probably know that already.

Thanks waclawek for helping me. I'm quite hard-headed, you probably have recognized that characteristic. I am aware of the pro's and cons of Bit field operations, however ones you can prove that the compiler executes the operations well in a certain architecture it works so nice 🙂

WOGoos
Associate II

I found the answer it was right in front of me however I didn't see it:

I did some further digging, and it turned out that at page 46 of Chapter2 of the RM0008 manual it states write-only (w) Software can only write to this bit. Reading the bit returns the reset value. That value is 0x00000000

I did a test of 10milj reads and it did return all zeros. the Read of a Write only register returns 0x00 It seems to be by design.

The RMW is a direct need when you work with bit fields in C. You probably know that already. Thanks guys for helping me