以下なカンジになってるのですが
{% gist 11105171 %}
初期処理を 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 の記述が駄目らしいことが分かったので今日はこれで終わり。