作者:Peter Zhou,AMD工程师;来源:AMD开发者社区
我们在Versal上开发驱动程序的时候,经常会访问某些硬件寄存器,读取或者写入某个值。访问寄存器有两种方式:
1. 按照32位的方式来访问,比如用API来读写32位的寄存器数据:
u32 Xil_In32(UINTPTR Addr);
void Xil_Out32(UINTPTR Addr, u32 Value);
2. 按照位域的方式来访问某一个或者某几个位,通常是会用到结构体和指针,比如
typedef struct {
u32 auth_only:1;
u32 crypto_byp:1;
u32 cipher_suite:2;
u32 conf_offset:6;
u32 replay_prot:1;
u32 validation_mode:2;
u32 reserve:19;
} etm_dec_cfg;
static void set_dec_aes_mode(void)
{
etm_dec_cfg * etm_dec_cfg_reg =
(etm_dec_cfg *)(0x00000004U);
etm_dec_cfg_reg->cipher_suite = (0x1 & 0x3);
}
笔者在用第1种方式,即以API 访问32位寄存器的条件下,通过联合仿真的结果出的波形与用板子来运行驱动程序所出的结果是一致的,能得到正确的验证结果。但是,用第2种方式,即以位域的方式来访问寄存器的条件下,通过联合仿真的结果出的波形与用板子来运行驱动程序所出的结果是不一致的,经过比对,会发现是联合仿真的时候的结果不正确。而且这个问题只是出现在R5的情况, A72没有这种情况。具体的问题请见下图的举例:
在第4步的wdata是0x00000006,但是期望的wdata是0x00001006, 第4不写的值覆盖了第3步写的值。
另外还有个现象,就是所写的地址也发生了变化,这是不应该的很奇怪的现象,请见下图的联合仿真的结果的波形:
awaddr 由0x004变成0x005了,这是不应该的。
这个问题的解决方案就是用API 来访问32位寄存器,而不是用位域的方式访问寄存器。即调用Xil_In32 和 Xil_Out32 来访问寄存器。
https://gcc.gnu.org/onlinedocs/gcc/Volatiles.html
其中有一段话是这样表述的:
As bit-fields are not individually addressable, volatile bit-fields may be implicitly read when written to, or when adjacent bit-fields are accessed. Bit-field operations may be optimized such that adjacent bit-fields are only partially accessed, if they straddle a storage unit boundary. For these reasons it is unwise to use volatile bit-fields to access hardware.
A72 elf 汇编截图如下:
R5 elf 汇编截图如下:
对比以上两张图会发现,A72的汇编地址是32位对齐的,但是R5的汇编指令ldrb 和 strb则是8位对齐的,这里就是区别,这会导致驱动用位域的时候出现问题。
所以,最终的解决方案就是用API Xil_In32 和 Xil_Out32来完整的访问32位寄存器,而不是用位域的方式通过指针来访问寄存器。