Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing to hardware register using void *

Tags:

c

embedded

I'm writing an application on top of an Altera soft processor. The API provides access to registers, defined in VHDL, as shown below.

#define __IO_CALC_ADDRESS_DYNAMIC(BASE, OFFSET) \
((void *)(((alt_u8*)BASE) + (OFFSET)))

#define IOWR_32DIRECT(BASE, OFFSET, DATA) \
__builtin_stwio (__IO_CALC_ADDRESS_DYNAMIC ((BASE), (OFFSET)), (DATA))

Based on everything I've learned about writing code that interfaces with hardware or registers directly, I've written my code like this,

typedef uint8_t volatile reg;    /* special type to access registers */

typedef struct reg_one           /* access to base and offsets */
{
    reg data_0;    /* first 8 bits */
    reg data_1;    /* second 8 bits */
    .
    .
} reg_one;

static reg_one *const reg_ptr = (reg_one *)SOME_BASE_ADDRESS;

uint32_t data;

data = 0;

__builtin_stwio(&(reg_ptr->data_0), data);

However, the address is cast to a void * and the volatile and const qualifiers are immediately discarded. Is there a work around for this, or am I just missing something simple?

I understand that using a macro to define a pointer is an option,

#define reg_ptr ((reg_one *)SOME_ADDRESS)

but I'd prefer to use a constant object for scope and type.

Thank you for any help in advance!

like image 662
dsell002 Avatar asked Oct 03 '22 06:10

dsell002


1 Answers

I would think that a more appropriate way to use a struct to define one's device register interface would be to declare the struct with normal types, but then to qualify the struct's type with the keyword volatile when defining the pointer to the registers' base address, thus:

typedef struct reg_one  /* access to base and offsets */
{
    uint8_t data_0;     /* first 8 bits */
    uint8_t data_1;     /* second 8 bits */
    /* ... */
} reg_one;

/*   key addition: volatile applied to the struct's type, reg_one   */
static volatile reg_one* const reg_ptr = (reg_one*)SOME_BASE_ADDRESS;

This will mark all accesses made through the pointer reg_ptr to the members of that struct as volatile. This is useful for ensuring that such statements as

reg_ptr->data_0 = (uint8_t)data;
/* reg_ptr         has type volatile reg_one*, so 
   reg_ptr->data_0 has type volatile uint8_t      */

won't be deleted or reordered by an optimizing compiler.

But it appears you are not accessing these members through C-language assignments, inc/decrements or what not. Instead you use what looks like a GCC builtin, __builtin_stwio. According to http://www.johnloomis.org/altera/nios2/gnutools/binutils/gcc/Altera-Nios-II-Built-in-Functions.html, the builtin __builtin_stwio is defined as

void __builtin_stwio (volatile void *, int)

When you become involved with compiler builtins, all bets are off as to their true behaviour, because a compiler give them any special treatment it wants. That said, I'd say it's a safe bet that your GCC variant will honor what the prototype appears to suggest. In all likelyhood your compiler will neither delete nor reorder calls to this builtin, and thus its use will obey volatile semantics.

like image 122
Iwillnotexist Idonotexist Avatar answered Oct 13 '22 10:10

Iwillnotexist Idonotexist