7

VHDL で定義されている多数のレジスタとインターフェイスするアプリケーションを作成しています。レジスタは 32 ビット幅で、グループに割り当てられます。グループのベース アドレスと、グループの各メンバーの 32 ビット オフセットが提供されます。以下は、1 つのグループ、グループ内のレジスター、およびレジスターの構造の例です。

グループ 1 |  ベースアドレス |  オフセット |  データポート

データポート |  alt_u32 データ 0 : 12;  | |  alt_u32 データ 1 : 1;  | |  ....

現在、I/O は次のビット フィールド構造体を使用して処理されます。

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

アドレスへのポインタを使用してフィールドを変更し、

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

これはこのプラットフォームでは機能するかもしれませんが、現在のコンパイラでは移植性が心配です。これらの型にはまらないデータ型の使用を余儀なくされたときに、ハードウェアへのインターフェイスを処理するためのより良い方法があるかどうか疑問に思っていましたか?

私が考えることができる 1 つの代替手段は、ハードウェアとレジスタ構造の間に別の層、揮発性の unsigned int ポインターを作成し、アプリケーション層でビットフィールド構造を使用することです。問題は、別のプラットフォームでは異なる位置に配置されている可能性があるビットフィールドから、別のトピックである可能性がある int にデータをコピーする必要があることです。

編集:
私が本当に探しているのは、ビットフィールドの使用を排除する方法だと思います。ビットフィールドメンバーを持つ構造体をハードウェアにマッピングすることは、実際には悪いアプローチのようです。そのため、代わりに次のいずれかを揮発性メモリ アドレスへのポインタとして使用します。

#define PeripheralBase ((uint32_t volatile *)BASE)

また

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

ここまで来れば、すべてが 32 ビット内で適切に調整されていることを願っています。これを行うために私が考えていた1つの方法は、同じ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)
        + .....;
}

構文が正しいかどうかはわかりませんが、コンパイラやプラットフォームを気にせずに値をシフトインするという考え方です。これはそれ以来作りますか?このアプローチには移植性の問題がありますか?

4

3 に答える 3

6

ビットフィールドは実装に大きく依存しますが、マクロを使用してレジスタを識別することができます。

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 
于 2013-10-09T22:29:51.837 に答える
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. この種のものにはビットフィールドを使用しないでください。それらは実装に依存するため、予期しない動作を示す可能性があります。上記の方法は、どのプラットフォームでも機能するはずです。
  2. レジスタのは、そのサイズを明示的に示すために、 の#defineような実装に依存しない型を使用する必要があります。uint32_t
于 2013-10-23T17:48:05.463 に答える
0

最良の選択肢は、ビットフィールド構造の使用を完全に排除することです。したがって、レジスタへの入力を処理するには、ビットを分離するのではなく、レジスタのコンポーネントを含む構造体を作成するだけです。

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 構造に割り当てられている値をチェックして、境界をオーバーランしないようにする必要があることです。そうしないと、レジスタに書き込まれたデータによって予期しない結果が生じる可能性があります。

于 2013-10-10T23:22:14.143 に答える