我正在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))
基于我所学到的关于编写与硬件或寄存器直接接口的代码的所有内容,我编写了这样的代码,
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);
但是,地址被强制转换为void *,并且会立即丢弃volatile和const限定符。有没有解决这个问题,或者我只是遗漏了一些简单的东西?
据我所知,使用宏来定义指针是一个选项,
#define reg_ptr ((reg_one *)SOME_ADDRESS)
但我更喜欢使用范围和类型的常量对象。
提前感谢您的帮助!
答案 0 :(得分:4)
我认为使用结构来定义一个设备寄存器接口的更合适的方法是使用普通类型声明结构,然后使用关键字{{限定结构的类型1}}在定义指向寄存器基址的指针时,因此:
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;
对该结构的成员进行的所有访问reg_ptr
。这对于确保
volatile
不会被优化编译器删除或重新排序。
但是看起来你没有通过C语言分配,增加/减少或者没有访问这些成员。而是使用看起来像GCC内置的reg_ptr->data_0 = (uint8_t)data;
/* reg_ptr has type volatile reg_one*, so
reg_ptr->data_0 has type volatile uint8_t */
。根据{{3}},内置__builtin_stwio
定义为
__builtin_stwio
当你参与编译器内置函数时,所有的赌注都是关于它们的真实行为,因为编译器给它们任何它想要的特殊处理。也就是说,我认为可以肯定的是,你的GCC变体会尊重原型所暗示的内容。在所有可能的情况下,您的编译器既不会删除也不会重新排序对此内置函数的调用,因此它的使用将遵循void __builtin_stwio (volatile void *, int)
语义。