質問

組み込みシステムでは、診断のためにコマンドラインインターフェイスを介して任意のデータを読み取ることができるセットアップがあります。ほとんどのデータでは、これは正常に機能します。memcpy()を使用して、要求されたアドレスでデータをコピーし、シリアル接続経由で送り返します。

ただし、16ビットのハードウェアレジスタの場合、<=>によって問題が発生します。 2つの8ビットアクセスを使用して16ビットハードウェアレジスタにアクセスしようとすると、上位バイトが正しく読み取れません。

この問題に遭遇した人はいますか?私は「ハイレベル」(C#/ Java / Python / Ruby)で、ハードウェアに近づいています。これは異質の領域です。

これに対処する最良の方法は何ですか?いくつかの情報、特にやや紛らわしい[私には]投稿こちら。この投稿の著者は、私とまったく同じ問題を 持っていますが、私が何をしているかを完全に理解せずにソリューションを実装するのは嫌です。

この問題に当てはまるものはどれでも大歓迎です。ありがとう!

役に立ちましたか?

解決

エディが言ったに加えて、通常は揮発性ポインタを使用してハードウェアレジスタを読み取る必要があります(すべてのシステムに当てはまるわけではありませんが、メモリマッピングされたレジスタを想定しています)。次のようなもの:

// using types from stdint.h to ensure particular size values
// most systems that access hardware registers will have typedefs
// for something similar (for 16-bit values might be uint16_t, INT16U,
// or something)

uint16_t volatile* pReg = (int16_t volatile*) 0x1234abcd;  // whatever the reg address is

uint16_t val = *pReg;  // read the 16-bit wide register

C / C ++でメモリマップされたレジスタを効果的に使用するために知っておく必要があることのほとんどすべてを提供するDan Saksによる一連の記事を次に示します。

他のヒント

このハードウェアの各レジスタは2バイト配列として公開され、最初の要素は2バイト境界に整列されます(アドレスは偶数です)。 memcpy()はサイクルを実行し、各反復で1バイトをコピーします。したがって、これらのレジスタからこのようにコピーします(すべてのループが展開され、charは1バイトです):

*((char*)target) = *((char*)register);// evenly aligned - address is always even
*((char*)target + 1) = *((char*)register + 1);//oddly aligned - address is always odd

ただし、ハードウェア固有の理由により、2行目が正しく機能しません。一度に1バイトではなく、一度に2バイトをコピーする場合、代わりに次のようにコピーされます(short intは2バイトです):

*((short int*)target) = *((short*)register;// evenly aligned

ここでは、1回の操作で2バイトをコピーし、最初のバイトを均等に揃えます。奇妙に整列したアドレスからの個別のコピーがないため、機能します。

修正されたmemcpyは、アドレスが完全に整列しているかどうかを確認し、整列している場合は2バイトのチャンクでコピーします。

特定のサイズのハードウェアレジスタにアクセスする必要がある場合、2つの選択肢があります。

  • 適切な整数型を使用してメモリにアクセスできるように、Cコンパイラがコードを生成する方法を理解する、または
  • 正しいバイトサイズまたはワードサイズでアクセスするために、いくつかのアセンブリを埋め込みます。

ハードウェアレジスタの読み取りは、レジスタとその機能にもよりますが、副作用がある可能性があるため、適切なサイズのアクセスでハードウェアレジスタにアクセスして、レジスタ全体を一度に読み取ることが重要です。

通常、レジスタと同じサイズの整数型を使用すれば十分です。 ほとんどのコンパイラでは、ショートは16ビットです。

void wordcpy(short *dest, const short *src, size_t bytecount)
{
    int i;
    for (i = 0;  i < bytecount/2;  ++i)
        *dest++ = *src++;
}

すべての詳細は投稿したスレッドに含まれていると思いますので、少し分解してみます;

具体的に;

If you access a 16-bit hardware register using two 8-bit
accesses, the high-order byte doesn't read correctly (it
always read as 0xFF for me). This is fair enough since
TI's docs state that 16-bit hardware registers must be
read and written using 16-bit-wide instructions, and
normally would be, unless you're using memcpy() to
read them.

そのため、ここでの問題は、ハードウェアレジスタが単一の16ビット読み取りで値が読み取られた場合にのみ正しい値を報告することです。これは、実行と同等です。

uint16 value = *(regAddress);

これは、単一の16バイト読み取りを使用して、アドレスから値レジスターに読み取ります。一方、一度に1バイトずつデータをコピーするmemcpyがあります。次のようなもの;

while (n--)
{
  *(uint8*)pDest++ = *(uint8*)pSource++;
}

これにより、レジスタが一度に8ビット(1バイト)読み取られ、値が無効になります。

このスレッドに投稿された解決策は、ソースと宛先が6ビットにアラインされている場所であればどこでも16ビット読み取りを使用してデータをコピーするバージョンのmemcpyを使用することです。

何を知る必要がありますか?あなたはすでにそれを説明する別の投稿を見つけました。どうやらCPUのドキュメントでは、16ビットのハードウェアレジスタは16ビットの読み取りと書き込みでアクセスする必要がありますが、memcpyの実装では8ビットの読み取り/書き込みを使用します。したがって、それらは一緒に動作しません。

解決策は、memcpyを使用してこのレジスタにアクセスしないことです。 代わりに、16ビット値をコピーする独自のルーチンを作成します。

質問が正確にわからない-私はその投稿が正しい解決策を持っていると思います。 あなたが述べたように、問題は標準のmemcpy()ルーチンが一度に1バイトを読み取ることであり、これはメモリマップドハードウェアレジスタに対して正しく動作しません。これはプロセッサの制限です。一度に1バイトを読み取る有効な値を取得する方法はありません。

推奨される解決策は、ワード境界で整列したアドレスでのみ動作し、一度に16ビットのワードを読み取る独自のmemcpy()を記述することです。これはかなり簡単です-リンクはcバージョンとアセンブリバージョンの両方を提供します。唯一の落とし穴は、常に有効な位置にあるアドレスから16ビットコピーを実行するようにすることです。 2つの方法でそれを行うことができます:リンカコマンドまたはプラグマを使用して物事が整列されていることを確認するか、整列されていないバッファの前に余分なバイトの特別なケースを追加します。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top