简体   繁体   中英

Reading hardware register in C program

Let say there is a 32 bits register defined as TIMER and its 32 bits address TIMER_ADDR in the memory (DDRAM).

uint32_t TIMER_ADDR; // 32 bits address declared as uint32_t

The layout of TIMER is defined as:

struct timer {
    uint32_t start:1;
    uint32_t mode: 3;
    uint32_t init: 4;
    uint32_t value:24
}

Later I defined a local var loc_timer as:

struct timer loc_timer;

How can I to read this register to a local register in the program so I can modify the content

loc_timer.mode = 4;
loc_timer.init = 10;

and write it back to register TIMER ?

something like

(*(uint32_t *))&loc_timer = (*((uint32_t *)(TIMER_ADDR))); // read
(*((uint32_t *)(TIMER_ADDR))) = (*(uint32_t *))&loc_timer; // write

but it does not work :-(

Like this:

struct timer loc_timer = *(struct timer *)TIMER_ADDR; // read register
loc_timer.mode = 4;                                   // modify fields
loc_timer.init = 10;
*(struct timer *)TIMER_ADDR = loc_timer;              // write register back again

Note that since this is a memory-mapped register you should treat it as volatile , eg

volatile struct_timer * const timer = (struct timer *)TIMER_ADDR;
struct timer loc_timer = *timer;                      // read register
loc_timer.mode = 4;                                   // modify fields
loc_timer.init = 10;
*timer = loc_timer;                                   // write register back again

Problem 1: the register isn't declared as volatile so reading from it or writing to it will not work. The compiler may decide to optimize the read to a different value than expected, post-phone it until later, or to skip it entirely.

Problem 2: the register is a 32 bit variable called "TIMER_ADDR". Does it contain an address, if so, why was it not declared as a pointer? Impossible to tell for the reader.

Problem 3: you can't use bit fields for bit mapping of hardware registers, for numerous reasons. See this .

Problem 4: you can't use structs nor bit fields for mapping of hardware registers, without a guarantee that no padding is enabled. You need a compiler-specific pragma or a compile time static check to protect against this.


If we ignore the above and also make a guess that TIMER_ADDR is indeed a variable and not an address to one, then the solution would be:

struct timer loc_timer = *(struct timer*) &TIMER_ADDR;
// and the other way around:
TIMER_ADDR = *(uint32_t*) &loc_timer;

Formally, such casts are undefined behavior and the compile might warn about them. In practice, it will work as long as we are certain that there are no alignment or padding issues.

After some investigations, here is the fact, PROGRAMMER knows what type of data TIMER_ADDR is pointing at (ie struct timer) so he/she should be able to dereference it correctly. Otherwise this exercise is pointless and will print garbage !!!

So in our case:

struct timer loc_timer;  
loc_timer = *(struct timer *)TIMER_ADDR;  

// Modify some struct members  

// Copy back loc_timer to register  
*(struct timer *)TIMER_ADDR = local_timer;  

// Print new content of TIMER register  
printf("new value  = %08x\n", *(struct timer *)TIMER_ADDR);   

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM