Open6

Kendryte K210 でMMIOレジスタが読めてない問題

okuokuokuoku

K210にはメモリ保護ユニット的なのは無いようなので、たぶんCコンパイラ側じゃないかな。。公式SDKはgcc8、手元で使っているのはgcc11。

okuokuokuoku

現象

gpiohs_set_drive_mode(6, GPIO_DM_OUTPUT); のように gpiohs_set_drive_mode APIでGPIOを設定しようとすると、assertに当たってしまう。

(../kendryte-standalone-sdk/lib/drivers/gpiohs.c:38) io_number >= 0

このassertは GPIOHSドライバ内にあって 、FPIOAの設定を読み出せなかったことに依る。

int fpioa_get_io_by_function(fpioa_function_t function)
{
    int index = 0;
    for(index = 0; index < FPIOA_NUM_IO; index++)
    {
        printf("SRCH %lx %d => %d\n", (long) fpioa, index, fpioa->io[index].ch_sel);
        if(fpioa->io[index].ch_sel == function)
            return index;
    }

    printf("Cannot find %d\n", function);

    return -1;
}

のようにダンプを挟むと、

 :
SRCH 502b0000 43 => 255
SRCH 502b0000 44 => 255
SRCH 502b0000 45 => 255
SRCH 502b0000 46 => 255
SRCH 502b0000 47 => 255
Cannot find 30
(../kendryte-standalone-sdk/lib/drivers/gpiohs.c:38) io_number >= 0

のように all 1 が読めていることがわかる。

okuokuokuoku

32bit単位でのみ読み出せる

FPIOAのレジスタの場所は platform.h に書かれている

#define FPIOA_BASE_ADDR     (0x502B0000U)

これを↓のようにprintfしてみると、

    for(i=0;i!=48;i++){
        int* f = (int*)0x502B0000U;
        printf("%d: %lx\n",i, f[i]);
    }
0: ffffffff80900000
1: ffffffff80900001
2: ffffffff80900002
3: 1f03
4: ffffffff80900044
5: 1f45
6: ffffffff809100d9
7: ffffffff80991fda

のように割とそれらしい値が出てくる。しかし、これを char での読み出しに変更すると、

        char* f = (char*)0x502B0000U;

やはりall 1が読める。つまり、 MMIOレジスタは32bit巾で読み出す必要がある ことがわかる。

0: ff
1: ff
2: ff
3: ff
4: ff
5: ff
6: ff
7: ff
okuokuokuoku

volatile の使用状況をレビューする

というわけで、 volatile を使っているドライバをレビューしていく。

  • aes -- aes_mode_ctl_t がbitfield。アウト。
  • apu -- 殆ど がbitfield。アウト。あと typedef struct 中に volatile を宣言する必要性は無い。
  • clint -- MSIPフラグの1bit がbitfield。アウト。
  • dmac -- 全てのフィールドは64bit巾。セーフ。
  • dvp -- 全てのフィールドは32bit巾。セーフ。
  • fft -- fft_fft_ctrl_t など がbitfield。アウト。というかctlとctrlで統一して欲しい。。IPのベンダが違うとか。。?
  • fpioa - fpioa_io_config_t がbitfield。アウト。
  • gpio - ビット定義だけbitfield。。惜しいけどアウト。
  • gpiohs - 同上。アウト。
  • i2c - セーフ。
  • i2s - こちらはアウト。

... いやちょっとアウト多すぎるな。。これピンポイントで最適化切った方が良いか。。?

okuokuokuoku

Volatileを分割禁止する

ARMのEABIではVolatileは明示的に分割禁止となっている。

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=23623#c4

Quoting from the AAPCS:

7.1.7.5 Volatile bit-fields preserving number and width of container accesses

When a volatile bit-field is read, its container must be read exactly once using the access width appropriate to the type of the container.
When a volatile bit-field is written, its container must be read exactly once and written exactly once using the access width appropriate to the type of the container. The two accesses are not atomic.

gcc的にはこの目的のために -fstrict-volatile-bitfields が有るらしいが、手元で与えた限りは動作しているように見えない。。

okuokuokuoku

とりあえずワークアラウンドした

https://github.com/okuoku/amigotest/commit/285c9b6efb7b75baa4e9fb53902e4ecc8e4db454

ひとまずGCCはバグってるっぽいので

https://zenn.dev/okuoku/scraps/0a8ce406cc45ec

に分離。ただ、まぁビットフィールドでMMIOレジスタを表現する事自体があんまりよろしくない。。C言語では(キリの良い)ビットフィールドはより短い型の糖衣構文と見做して良いので。。

ワークアラウンドとしてはgetterを足した。

https://github.com/okuoku/kendryte-standalone-sdk/commit/2332f3c29857319bb1532610273713b8856f0fac

これが必要なコードを機械的に見つける良い方法はたぶん無いから、爆弾を抱えたまま作業することになるのか。。やっぱりclangに替えようかな。。