漂亮但是危险的寄存器位域访问

一年前我在做寄存器底层驱动的时候,在寄存器位域访问上给自己挖了一个很大的坑,最近工作中又碰到了这个问题,不过我发现很多细节都已经记不清了,所以又研究了一遍,看起来这个坑很容易跳进去,所以决定把这个过程记录下来,同时做一些验证和扩展。

现在流行的外设,硬件寄存器的地址往往是4字节对齐的32位,对应的数据位宽也是32位,寄存器作为软硬件的接口,映射的是硬件信号,然而硬件信号不都是32位,大多数情况下,表征一个硬件信号只需要一个或者几个位,这一个或者几个位构成了一个位域,往往是几个位域凑到一起,凑够32位,甚至为了保证对齐,中间还会增加一个或几个没有意义的位,这种结构很容易让人联想到这样的结构:

typedef struct { 
    union {
        volatile unsigned int reg0; 
        struct {
            volatile unsigned int field0:5;
            volatile unsigned int rsvd_5:11;
            volatile unsigned int field1:7;
            volatile unsigned int rsvd_23:9;
        } reg0_b;       
    };
}DDR_CTL_Type;

这个寄存器有两个有用的位域 field0field1,有两个保留的位域rsvd_5rsvd_23,这样可以通过 reg0 访问整个32位寄存器,通过 reg0_b.field0 访问 field0 这个位域,相比于把整个寄存器读出来然后再挑 field0 的办法,这种直接访问位域的办法更简洁优雅,

1 个赞