昨日知り合いとBlackfin関連の話をしていたところ、「2次元DMAのバッファ管理には問題があるのか」という話しが出ました。数年前にそう言う事があった気がしましたが、さすがに記憶が薄れています。
調べ直してようやく思い出しました。CURR_Y_COUNTをバッファ管理に使う場合には、トリプル・バッファ以上にしなければなりません。ダブル・バッファでCURR_Y_COUNTを使う事はできません。
これは、CURR_Y_COUNTの特殊な動作のためです。CURR_Y_COUNTは、DMAコントローラが現在何番目の行転送を行っているか管理するためのレジスタです。このレジスタはCURR_X_COUNTと対で管理されています。CURR_X_COUNTは列カウンタで、1転送ごとにデクリメントされます。CURR_Y_COUNTは行カウンタで、1行転送終了後にデクリメントされます。
BF533のHWRやBF51xのHWRで確認すると、CURR_Y_COUNTのアップデート・タイミングは以下のようになっています。
- 最初の転送でCURR_Y_COUNTにはY_COUNTレジスタの設定値(総転送行数)がロードされる
- 行内の最後の転送でCURR_Y_COUNTがデクリメントされる
- 2次元転送の最後の転送ではCURR_Y_COUNTは1になる
- 2次元伝送が自動継続する場合には、次の周期の最初の転送で、CURR_Y_COUNTにY_COUNTレジスタの設定値(総転送行数)がロードされる
何も問題無いように思えますが、とんでもない落とし穴が潜んでいます。行転送割り込みは行内の最後の列の転送完了で発生します。これを表で表すとこうなります。
転送終了した行番号 |
CURR_Y_COUNT |
3 | 2 |
2 | 1 |
1 | 1 |
4 | 3 |
3 | 2 |
1行目と2行目の区別を付けることができません。
このようなことがおきるのは、2次元DMAの最後の転送でCURR_Y_COUNTが更新されないためです。結果的に、2次元DMAを使ってダブル・バッファを実装しても正しく転送中のバッファ位置を知ることはできません。最低でもトリプル・バッファを使う必要があります。
なお、DMAデスクリプタのリンクを使う場合には、ダブル・バッファも安全に管理できます。