簡體   English   中英

將位域結構映射到易失性寄存器

[英]Mapping bit-field structure to volatile register

我正在編寫一個與VHDL中定義的許多寄存器接口的應用程序。 寄存器為32位寬,並分配成組。 我為該組的每個成員提供了組的基址和32位偏移量。 以下是一個組的示例,組內的寄存器和寄存器的結構。

第1組| base addr |偏移| DATA_PORT

data_port | alt_u32 data0:12; | alt_u32 data1:1; | ....

目前,使用以下位字段結構處理I / O,

typedef struct
{
   uint32_t   data0 : 12;
   uint32_t   data1 : 1;
   ...
}volatile data_port;

並使用指向地址的指針修改字段,

data_port *const p_data = (data_port *)0xc006380;

雖然這可能在這個平台上有效,但是對於當前的編譯器,我擔心可移植性。 我想知道在強制使用這些非常規數據類型時是否有更好的方法來處理硬件接口?

我能想到的另一種選擇是在硬件和寄存器結構之間創建另一層,使用易失性無符號int指針,並在應用層中使用位域結構。 問題是,數據仍然必須從位字段(可能在另一個平台上以不同方式對齊)復制到int,這可能是另一個主題。

編輯:
我認為我真正想要的是一種消除位域使用的方法。 將具有位字段成員的結構映射到硬件實際上似乎是一種糟糕的方法。 所以,為了消除這一點,我將使用以下之一作為指向易失性存儲器地址的指針,

#define PeripheralBase ((uint32_t volatile *)BASE)

要么

uint32_t volatile *const peripheral_base  = (uint32_t *) BASE;

希望,一旦我達到這一點,一切都將在32位內完全一致。 我想要做的一個方法是創建相同的data_port結構,但是刪除位打包,然后右邊一個函數專門為每個寄存器將位移位到unsigned int,然后可以使用它傳遞給寄存器易失性指針。

就像是,

static inline uint32_t struct_to_uint(data_port *data)
{
   return data->data0
        + ((uint32_t)data->data1 << 12)
        + ((uint32_t)data->data2 << 13)
        + .....;
}

我不確定語法是否正確,但我們的想法是將值轉移而不必擔心編譯器或平台。 這是否因為? 這種方法是否存在可移植性問題?

雖然位字段非常依賴於實現,但您可以使用宏來識別寄存器:

typedef struct
{
   uint32_t data0 : 12;
   uint32_t data1 : 1;
   ...
} data_port;

#define DATA_PORT (*(volatile data_port *) 0xc006380) 

然后以這種方式訪問​​位:

 DATA_PORT.data0 = 1;  // set data0 bit of DATA_PORT to 1 

用於訪問硬件寄存器內的字段的典型的與實現無關的方法是使用移位(和掩碼)。 例如:

#define DATA0_SHIFT   0
#define DATA0_MASK    0x3FF
#define DATA1_SHIFT   12
#define DATA1_MASK    0x1
#define DATA2_SHIFT   13
#define DATA2_MASK    0x1

// ...

uint32_t data = 0
   | ((data0 & DATA0_MASK) << DATA0_SHIFT)
   | ((data1 & DATA1_MASK) << DATA1_SHIFT)
   | ((data2 & DATA2_MASK) << DATA2_SHIFT);

對於寄存器本身,這樣的事情:

#define DATA_PORT_ADDR  0xc006380
#define DATA_PORT_REG  (*(volatile uint32_t *)(DATA_PORT_ADDR))

這意味着你可以這樣做:

DATA_PORT_REG = data; // Value from above.

也:

  1. 不要將bitfields用於此類事情。 它們依賴於實現,因此可以顯示意外行為。 上述方法應適用於任何平台。
  2. 寄存器的#define應使用與uint32_t類似的與實現無關的類型,以便明確顯示其大小。

最好的選擇似乎是完全消除位域結構的使用。 因此,要處理寄存器的輸入,而不是分離位,只需創建一個包含寄存器組件的結構。

typedef struct data_port
{
   uint32_t   data0;
   uint32_t   data1;
   ....
}data_port;

雖然此結構不直接管理硬件接口,但它是處理應用程序層中數據的有用方法。 可以使用宏或指向易失性const uint32_t的指針創建指向寄存器的指針。

uint32_t volatile *const peripheral_base  = (uint32_t *) BASE;

用於將數據從結構復制到無符號32位值的便攜式解決方案是使用函數將每個值移位到寄存器中的正確位置,然后將這些值一起添加。

static inline uint32_t struct_to_uint(data_port *data)
{
   return data->data0
    + (data->data1 << 12)
    + (data->data2 << 13)
    + .....;
}

然后可以使用對函數的調用來處理對寄存器的寫入。

*peripheral_base = stuct_to_uint(&data_port);

需要注意的是,由於未使用位域,因此必須檢查分配給應用程序中data_port結構的值,以確保它們不會超出其邊界。 否則,寫入寄存器的數據將產生意外結果。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM