我正在 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))
基于我所学到的有关编写与硬件接口(interface)或直接注册的代码的所有知识,我编写了这样的代码,
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)
但我更愿意为范围和类型使用常量对象。
提前感谢您的帮助!
最佳答案
我认为使用结构来定义一个设备寄存器接口(interface)的更合适的方法是用普通类型声明结构,然后用关键字 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
。这对于确保诸如
reg_ptr->data_0 = (uint8_t)data;
/* reg_ptr has type volatile reg_one*, so
reg_ptr->data_0 has type volatile uint8_t */
不会被优化编译器删除或重新排序。
但您似乎没有通过 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)
当您开始使用编译器内置函数时,所有关于它们真实行为的赌注都会落空,因为编译器会给予它们任何它想要的特殊待遇。就是说,我敢打赌您的 GCC 变体将尊重原型(prototype)所暗示的内容。您的编译器很可能既不会删除也不会重新排序对此内置函数的调用,因此它的使用将遵循 volatile
语义。
关于c - 使用 void * 写入硬件寄存器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20385500/