/var/log/messages

debugging with sixth sense

JTAG 接続なソレを書き換えてみる

以下なカンジになってるのですが

初期処理を assembler で書くとどうなるのだろうか、とか。

ええと、gtags -v して init_gpio から確認。以下から順に。

    // pullup all
    *GPPUD = 0x02;

GPUUD は rpi_lib/peripherals/rpi_peripherals.h で定義されてます。

// GPIO PULLUP/DOWN
#define GPPUD ((vu32_t *)PHY_PERI_ADDR(GPIO_BASE + 0x94))

ソースで tab 使いまくっててちょい微妙。PHY_PERI_ADDR は同じファイルで定義されてますね。

#define PHY_PERI_ADDR(x) (0x20000000 + (x))

これ、BCM2835 ARM Peripherals なドキュメントに I/O Base set in arm loader って記述があります。GPIO_BASE も同様に同じファイルで定義。

#define GPIO_BASE   (0x00200000)

マニュアル確認。これ、GPIO なレジスタの base address になる模様。マニュアルだと 0x7E000000 が base になってて gpio は 0x7E200000 になってます。

で、GPPUD なマクロは、ということで見てみるに GPPUD GPIO Pin Pull-up/down Enable という記述になってますね。次ですが以下な記述になってます。

    // wait 150 cycle
    for(i=0;i<150;i++){
        // nop
        asm("mov r0,r0");
    }
    *GPPUDCLK0 = 0xffffffff;
    *GPPUDCLK1 = 0xffffffff;
    // wait 150 cycle
    for(i=0;i<150;i++){
        // nop
        asm("mov r0,r0");
    }
    *GPPUDCLK0 = 0;
    *GPPUDCLK1 = 0;

wait して云々してますね。ちなみに BareMetal で遊ぶ、なソレの 4 章に GPIO なレジスタに関する記述があります。

と、GPPUD と GPPUDCLK は設定反映とかレジスタの値削除まで 150 cycle かかる、という記述を発見。マニュアルの 101p にも確かに記載あり。

  • 最初の GPPUD を pull-up な状態にしておいて
  • 150 cycle 待って
  • GPPUDCLK[01] なレジスタのフラグを全部立てて
  • 150 cycle 待って
  • GPPUDCLK[01] なレジスタのフラグを全部落として

ということをしているのか。最初だから全部ヤッとけ、って理解で良いのかな。

次、以下です。

    *GPFSEL0 = 0;
    *GPFSEL1 = 0;
    *GPFSEL2 = 0;
    *GPFSEL3 = 0;
    *GPFSEL4 = 0;
    *GPFSEL5 = 0;

GPFSEL は GPIO Function Selector とのことなのでこれも初期化ですね。定義は同様に rpi_peripherals.h になってて

#define GPFSEL0 ((vu32_t *)PHY_PERI_ADDR(GPIO_BASE + 0x00))
#define GPFSEL1 ((vu32_t *)PHY_PERI_ADDR(GPIO_BASE + 0x04))
#define GPFSEL2 ((vu32_t *)PHY_PERI_ADDR(GPIO_BASE + 0x08))
#define GPFSEL3 ((vu32_t *)PHY_PERI_ADDR(GPIO_BASE + 0x0c))
#define GPFSEL4 ((vu32_t *)PHY_PERI_ADDR(GPIO_BASE + 0x10))
#define GPFSEL5 ((vu32_t *)PHY_PERI_ADDR(GPIO_BASE + 0x14))

これで init_gpio 手続きは終了。言われてみれば確かに initialize gpio ですね。次は以下の記述。このあたりを boot.S あたりに書きたい訳です。

    pinMode (22 ,ALT4);

pinMode は C の手続きですね。rpi_lib/gpio/gpio.c で定義されています。定義が以下。

void pinMode(int pin,int mode){
    vu32_t *res;

    // GPFSEL select
    if(0 <= pin && pin <= 9){
        res = GPFSEL0;
    }else if (pin <= 19)
    {
        res = GPFSEL1;
    }else if (pin <= 29)
    {
        res = GPFSEL2;
    }else if (pin <= 39)
    {
        res = GPFSEL3;
    }else if (pin <= 49)
    {
        res = GPFSEL4;
    }else if (pin <= 53)
    {
        res = GPFSEL5;
    }else{
        // pin missmuch
        return;
    }

定義が長いので、ここで一旦切ります。これは引数で渡される pin の情報をもとに設定するレジスタを選択してるんですね。

    // mode set
    switch(mode){
        case INPUT:
            *res &= GPFSEL_MASK_IN(pin);
            break;
        case INPUT_PULLUP:
            setPullUpDown(pin,INPUT_PULLUP);
            *res &= GPFSEL_MASK_IN(pin);
            break;
        case INPUT_PULLDOWN:
            setPullUpDown(pin,INPUT_PULLDOWN);
            *res &= GPFSEL_MASK_IN(pin);
            break;
        case OUTPUT:
            *res |= GPFSEL_MASK_OUT(pin);
            break;
        case ALT0:
            *res |= GPFSEL_MASK_ALT0(pin);
            break;
        case ALT1:
            *res |= GPFSEL_MASK_ALT1(pin);
            break;
        case ALT2:
            *res |= GPFSEL_MASK_ALT2(pin);
            break;
        case ALT3:
            *res |= GPFSEL_MASK_ALT3(pin);
            break;
        case ALT4:
            *res |= GPFSEL_MASK_ALT4(pin);
            break;
        case ALT5:
            *res |= GPFSEL_MASK_ALT5(pin);
            break;
        default:
            // error!
            ;
    }
    return;
}

で、そのレジスタに何か、を設定するのか。ここは ALT4 または 5 限定なのでそこに絞って確認します。定義が rpi_peripherals.h で以下。

#define GPFSEL_MASK_ALT4(n) (0x03 << ((n % 10) * 3))
#define GPFSEL_MASK_ALT5(n) (0x02 << ((n % 10) * 3))

ええと、

  • 22 を ALT4 (ARM_TRST – ARM JTAG reset)
  • 4 を ALT5 (ARM_TDI – ARM JTAG Data in)
  • 27 を ALT4 (ARM_TMS ARM JTAG Mode select)
  • 25 を ALT4 (ARM_TCK ARM JTAG Clock)
  • 24 を ALT4 (ARM_TDO ARM JTAG Data out)

らしい。なるほど。

つうかこの altenative なナニについての解説が見つけられぬ。マクロ見てみるに ALT なソレは 0x0 から 0x7 までなので 3bit か。ピン番号の下一桁に 3 をかけた値で左に shift させてます。

あ、GPFSEL なレジスタの説明に書いてあるや。しかもそれぞれ 0 から 9 な FSEL で、って形になっていますね。成程。

最後が以下です。

    // GPIO16 を H にセット
    digitalWrite (16 , HIGH );
    // GPIO16 を L にセット
    digitalWrite (16 , LOW );

digitalWrite という手続きも rpi_lib/gpio/gpio.c で定義されてます。二番目の引数が HIGH なら GPSET0 または GPSET1 が対象で LOW なら GPCLR0 または GPCLR1 が対象になる模様。0 か 1 かは pin の番号で決まるようです。

GPIO16 て何に使ってるのかな、って思ったらこれ GPIO16 に繋いだ LED を云々するサンプルが所以らしいのでこれは不要ですね。

アセンブラ書き換え

面倒なので gpio.c をコンパイルまでで止めておいて中身を確認。つうか init_gpio とかって boot.S から呼び出したりできるのか。

つうか、ARM のアセンブラ命令から復習が必要らしい。

出力な gpio.s によれば

    ldr     r3, .L6
    mov     r2, #2
    str     r2, [r3]

なナニで GPPUD の初期設定をしている模様。.L6 の肝心な部分が以下らしいんですが

.L6:
        .word   538968212 //0x20200094
        .word   538968216 //0x20200098
        .word   538968064 //0x20200000
        .word   538968068 //0x20200004
        .word   538968072 //0x20200008
        .word   538968076 //0x2020000c
        .word   538968080 //0x20200010
        .word   538968084 //0x20200014

不思議なのが GPPUDCLK[01] を初期化しているあたり。

    ldr     r3, .L6+4
    mvn     r2, #0
    str     r2, [r3]
    ldr     r3, .L6+4
    mvn     r2, #0
    str     r2, [r3]

という記述になっているのですがこれだと GPPUDCLK0 しか初期化されないのではないのかな。ともあれ、-S で出てきたソレを使いまわせば init_gpio は何とかなりそう。

pinMode もより具体的な記述の羅列にしてやれば良いだけなんだけどな。

ということで main.c の pinMode なソレを以下にしてみて

    res = GPFSEL2;
    *res |= GPFSEL_MASK_ALT4(22);

    res = GPFSEL0;
    *res |= GPFSEL_MASK_ALT5(4);

    res = GPFSEL2;
    *res |= GPFSEL_MASK_ALT4(27);

    res = GPFSEL2;
    *res |= GPFSEL_MASK_ALT4(25);

    res = GPFSEL2;
    *res |= GPFSEL_MASK_ALT4(24);

-S で見てみると以下なカンジになってます。

    ldr     r3, .L2
    str     r3, [fp, #-8]
    ldr     r3, [fp, #-8]
    ldr     r3, [r3]
    orr     r2, r3, #192
    ldr     r3, [fp, #-8]
    str     r2, [r3]
    ldr     r3, .L2+4
    str     r3, [fp, #-8]
    ldr     r3, [fp, #-8]
    ldr     r3, [r3]
    orr     r2, r3, #8192
    ldr     r3, [fp, #-8]
    str     r2, [r3]
    ldr     r3, .L2
    str     r3, [fp, #-8]
    ldr     r3, [fp, #-8]
    ldr     r3, [r3]
    orr     r2, r3, #6291456
    ldr     r3, [fp, #-8]
    str     r2, [r3]
    ldr     r3, .L2
    str     r3, [fp, #-8]
    ldr     r3, [fp, #-8]
    ldr     r3, [r3]
    orr     r2, r3, #98304
    ldr     r3, [fp, #-8]
    str     r2, [r3]
    ldr     r3, .L2
    str     r3, [fp, #-8]
    ldr     r3, [fp, #-8]
    ldr     r3, [r3]
    orr     r2, r3, #12288
    ldr     r3, [fp, #-8]
    str     r2, [r3]

or してるソレが即値になってるしw

ちなみに init_gpio の呼び出しは以下で良い模様。

    bl      init_gpio

これ boot.S に盛り込んで動かしてみようかな。どうなるか。

書き換え

以下を gcc -S してみました。

    *GPFSEL2 |= GPFSEL_MASK_ALT4(22);

これは卑怯。以下が出力されたので採用。

ldr r3, #538968072
ldr r2, #538968072
ldr r2, [r2]
orr r2, r2, #192
str     r2, [r3]

と思ったら ldr の記述が駄目、って叱られますね。仕方が無いのでこーゆーの追加。

.L2:
        .word   538968072
        .word   538968064

なんつーか対処の仕方が酷い。そして ld で Unknown EABI object attribute 34 って warning が出てるな。branch して tempolary な commit 作って手を入れる前のソレを確認してみるに warning は出てない模様。

ググッてみるにアライメントなナニらしい。以下なエントリによれば問題はなさげ、ってことでそのまま使うことに。

動かしてみた

のですが openocd がエラーっておっしゃいます。main.c を元に戻すと正常動作。

下記を有効にするとどうなるのかな。

    *GPFSEL2 |= GPFSEL_MASK_ALT4(22);
    *GPFSEL0 |= GPFSEL_MASK_ALT5(4);
    *GPFSEL2 |= GPFSEL_MASK_ALT4(27);
    *GPFSEL2 |= GPFSEL_MASK_ALT4(25);
    *GPFSEL2 |= GPFSEL_MASK_ALT4(24);

これも無問題。boot.S の記述が駄目らしいことが分かったので今日はこれで終わり。

Comments