使用void*写入硬件寄存器

dse*_*002 5 c embedded

我正在Altera软处理器上编写应用程序.API提供对VHDL中定义的寄存器的访问,如下所示.

#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))
Run Code Online (Sandbox Code Playgroud)

基于我所学到的关于编写与硬件或寄存器直接接口的代码的所有内容,我编写了这样的代码,

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);
Run Code Online (Sandbox Code Playgroud)

但是,地址被强制转换为void*,并且会立即丢弃volatile和const限定符.有没有解决这个问题,或者我只是遗漏了一些简单的东西?

我知道使用宏来定义指针是一个选项,

#define reg_ptr ((reg_one *)SOME_ADDRESS)
Run Code Online (Sandbox Code Playgroud)

但我更喜欢使用范围和类型的常量对象.

感谢您提前帮助!

Iwi*_*ist 4

我认为使用结构体定义设备寄存器接口的更合适的方法是使用普通类型声明结构体,然后在定义指向寄存器基地址的指针时使用关键字限定volatile结构体的类型,因此:

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;
Run Code Online (Sandbox Code Playgroud)

reg_ptr这会将通过指向该结构成员的指针进行的所有访问标记为volatile。这对于确保诸如

reg_ptr->data_0 = (uint8_t)data;
/* reg_ptr         has type volatile reg_one*, so 
   reg_ptr->data_0 has type volatile uint8_t      */
Run Code Online (Sandbox Code Playgroud)

优化编译器不会删除或重新排序。

但看来您没有通过 C 语言赋值、递增/递减或其他方式访问这些成员。相反,您使用看起来像 GCC 内置的__builtin_stwio. 根据http://www.johnloomis.org/altera/nios2/gnutools/binutils/gcc/Altera-Nios-II-Built-in-Functions.html,内置函数__builtin_stwio定义为

void __builtin_stwio (volatile void *, int)
Run Code Online (Sandbox Code Playgroud)

当您开始使用编译器内置函数时,所有关于它们真实行为的赌注都会消失,因为编译器会给它们任何它想要的特殊待遇。也就是说,我敢打赌,您的 GCC 变体将遵循原型的建议。您的编译器很可能既不会删除也不会重新排序对此内置函数的调用,因此它的使用将遵循volatile语义。