SSブログ

beagleboard を触ろう - UART DMA モード [組み込みソフト]


UART を DMA モードで使用してみましょう。

OMAP35x TRM では、The System Direct Memory Access (SDMA) と呼ばれている、システムで汎用的に使用できる DMA デバイスがあります。
UART で DMA 転送を行う場合は、SDMA を使います。

SDMA は、32 チャンネルあります。
つまり、32 個、別々の DMA 転送を同時に設定できるわけです。

SDMA の使用方法は、概略、以下のような感じです。

・DMA リクエスト番号を設定する
・転送サイズを設定する
・転送元のアドレスとアドレッシングモードを設定する
・転送先のアドレスとアドレッシングモードを設定する
・転送元のバーストサイズを設定する
・転送先のバーストサイズを設定する
・デバイスとの同期方法を設定する
・必要ならば、割り込み設定をする
・イネーブルビットを立てて DMA を有効にする

この状態で、デバイスから DMA 要求が上がると、DMA 転送が行われます。
逆に言えば、デバイスから DMA 要求が上がらない限り、何も行われません。
(厳密に言えば、RAM からデバイスに転送する場合は、デバイスから DMA 要求が上がらなくても、RAM からプリフェッチされることはあるかもですが。)

DMA リクエスト番号は、モジュール毎に専用の値が割り当てられています。
UART3 の場合、送信リクエストは 52 番、受信リクエストは 53 番です。
RAM から UART3 にデータを転送したい場合は 52 番を、UART3 から RAM に転送したい場合は 53 番を使います。

アドレッシングモードは、Constant, Post-increment, Single-index, Double-index の 4通りから選択できます。
Constant は、ずっと同じアドレスに対して読み/書きをします。
Post-increment は、1 バイト読み/書きしたら、1 バイトアドレスを進めます。
Single-index と Double-index は、ここでは省略です。

デバイスとの同期方法は、ブロック同期、フレーム同期、パケット同期、あともう一つ、呼び方がよう分からんですが、シングルエレメント同期(?)の 4通りから選択できます。
これらは、デバイスから DMA 要求が SDMA に上がってきた場合に、何バイトデータを転送するかを指定するものです。

DMA_block_frame_element.png

DMA 全体で転送されるサイズは、この図の block 分です。
block は、n 個の frame で成り立っています。
また、frame は、m 個の element で成り立っています。
DMA 全体で転送されるサイズは、(element のサイズ) x (frame 中の element 数) x (block 中の frame 数) ということになります。

element のサイズ、frame 中の element 数、block 中の frame 数は、SDMA のレジスタで設定することができます。
element のサイズは、8/16/32 ビットの 3通りから選択できます。
frame 中の element 数、block 中の frame 数は、好きな値を設定することができます。


同期方法の話に戻りますが、デバイスから DMA 要求が上がってきた際に、

・一気に block 分転送してしまうのがブロック同期
・1 frame だけ転送するのがフレーム同期
・1 element だけ転送するのがシングルエレメント同期(呼び方不明)
・別途指定したパケット数分だけ転送するのがパケット同期

ということになります。
パケット同期が、一番フレキシブルな方法ですね。


SDMA は、割り込みを上げることができます。
パケットの転送完了、ブロックの転送完了、フレームの転送完了等々のイベントで、割り込みを上げることが可能です。
転送が完了した時を知りたい時は、割り込みを使えばいいですね。

割り込みで使用するレジスタは、4種類です。

・DMA4_IRQENABLE_L
・DMA4_IRQSTATUS_L
・DMA4_CICRi
・DMA4_CSRi

DMA_intreg.png

DMA4_IRQENABLE_L は、32 ビットのレジスタですが、各ビットが各チャネルを表します。
対応するビットに 1 を立てると、そのチャネルでの割り込みがイネーブルされます。
例えば、DMA チャネル n を使用する場合は、DMA4_IRQENABLE_L のビット n に 1 を立てます。

DMA4_CICRi は、32 ビットのレジスタで、DMA4_CICRi は全体でチャネル分、32 本あります。
このレジスタで、どのようなタイプの割り込みを上げるかを指定します。
例えば、ブロック完了割り込みを上げたい場合は、ビット 5 に 1 を立てます。

割り込みを上げるには、DMA4_IRQENABLE_L, DMA4_CICRi の両方への設定が必要です。
片方だけではダメです。

割り込みが上がった時、割り込みハンドラーで参照するのが DMA4_IRQSTATUS_L レジスタと DMA4_CSRi レジスタです。
DMA4_IRQSTATUS_L には、割り込みを上げたチャネルに対応するビットが立っています。
例えば、DMA チャネル n を使用する場合は、DMA4_IRQSTATUS_L のビット n に 1 が立ちます。

DMA4_CSRi は、その割り込みを上げたチャネルで、どのような種類のイベントが発生したのかを示しています。
例えば、ブロック完了割り込みが発生した場合は、DMA4_CSRi のビット 5 に 1 が立ちます。

割り込みハンドラーでは、DMA4_IRQSTATUS_L と DMA4_CSRi、それぞれのビットの立っている位置に 1 を書き込んで、割り込み要因をクリアする必要があります。
これもまた、片方だけではダメで、DMA4_IRQSTATUS_L と DMA4_CSRi の両方に、書き込んでやる必要があります。
でないと、割り込み要因がクリアされません。


今まで説明したことを含め、SDMA の設定方法詳細は、OMAP35x TRM に載っています。
DMA 章の SDMA Basic Programming Model 節です。

・・・

UART で DMA 転送をするには、UART の方にもちょっと設定をしてやる必要があります。
といっても、閾値の設定と DMA モードの設定だけです。
閾値は、UART 割り込みモードで見た閾値とまったく同じです。
設定方法も同じです。

UART が転送元になる場合、即ち UART から RAM へデータ転送する場合、設定した閾値の分だけ受信 FIFO にデータがたまった時に、DMA 要求が上がります。
SDMA は、これを受けて、受信 FIFO からデータを取り出し、RAM へ転送します。

DMA 要求が上がった際に、何バイト読み出すかは、デバイスとの同期方法に依ります。
ブロック同期、フレーム同期、パケット同期、シングルエレメント同期、どれを使ってもいいんですが、パケット同期を使った場合は、設定したパケット数分だけ転送されます。

DMA 転送のパケット数と、UART の閾値は、同じ値にしておくとよいです。
例えば、パケット数、UART の閾値をともに 4 にした場合、

・受信 FIFO に 4 バイトたまると、DMA 要求が上がる
・SDMA は、4 バイト分(=パケット数)、受信 FIFO から RAM に DMA 転送する
・受信 FIFO は空になる

となって、いい塩梅です。


それでは、また簡単なテストです。
UART3 から RAM へ、DMA 転送を行います。

SDMA の初期化を、以下のように行っておきます。

・DMA チャネル 0 番を使用
・DMA リクエスト番号は 53 (UART3 RX) を設定
・転送元のアドレスは UART3 の RHR に、アドレッシングモードは Constant に設定
・転送先のアドレスは 適当な RAM 領域に、アドレッシングモードは Post-increment に設定
・element のサイズは 8 ビット
・frame 中の element 数は 8
・block 中の frame 数は 1
・デバイスとの同期はパケット同期
・パケット数は 4 バイト
・割り込み設定は、ブロック完了割り込みをイネーブル

DMA 転送サイズは、(element のサイズ) x (frame 中の element 数) x (block 中の frame 数) なので、8 バイトです。
パケット数は 4 バイトなので、DMA 要求が上がってきた際に転送するサイズは 4 バイトです。
DMA 転送完了までに、2 パケット転送することになります。
ブロック完了割り込みをイネーブルしているので、DMA 転送が完了した時に割り込みが上がります。


UART3 の方は、以下のように設定しておきます。

・受信 FIFO の閾値を 4 に設定
・DMA モードを 1 に設定


このように SDMA, UART3 を設定しておくと、

・シリアルコンソールからキー入力が 4 回行われると、受信 FIFO に 4 バイトたまる
・受信 FIFO にたまっているデータが、設定した閾値と同じになったので、UART3 は DMA 要求を上げる
・SDMA は、これを受けて、設定してあるパケット数、4 バイトを UART3 受信 FIFO から RAM へ転送する
・受信 FIFO が空になる
・再度シリアルコンソールからキー入力が 4 回行われると、受信 FIFO に 4 バイトたまる
・UART3 は DMA 要求を上げる
・SDMA は、4 バイトを受信 FIFO から RAM へ転送する
・DMA 転送サイズである 8バイト分の転送が終わったので、SDMA はブロック完了割り込みを上げる

という動作が行われます。

テストコードは、こんな感じです。

 90: static volatile int dma_completed;
 91: static unsigned char dma_buf[DMA_LEN];
	  :
106: static int uart_dma_test(void)
107: {
108:    int i;
109:
110:    dma_completed = 0;
               :
       (SDMA, UART3 の初期化)
               :
169:    while (!dma_completed)
170:        ;
171:	
172:    serial_reinit2(1, 1, 0);
173:
174:    for (i = 0; i < DMA_LEN; i++)
175:        printf("%c ", dma_buf[i]);
176:    printf("\n");
177: }


SDMA, UART3 の初期化をした後は、169-170 行目で dma_completed を見張りながらビジーループします。
dma_completed は、割り込みハンドラーが設定します。
172 行目は、UART3 の設定を元の状態 (送信 FIFO 閾値 = 1, 受信 FIFO 閾値 = 1, DMA モード = 0) に戻しています。
最後、dma_buf を先頭から 8 バイト分表示しておしまいです。
dma_buf は、DMA の転送先に設定してある領域です。
DMA が完了した後は、UART3 から転送されたデータが書き込まれているはずです。


割り込みハンドラーは、以下のような感じです。

 88: #define DMA_IT_TYPE   0x20
           :
 93: static void dma_handler(int irq)
 94: {
 95:    unsigned long dma4_csr;
 96:
 97:    dma4_csr = __raw_readl(DMA4_CSR(DMA_CHANNEL));
 98:    if(!(dma4_csr & DMA_IT_TYPE))
 99:        return;
100:    __raw_writel(dma4_csr, DMA4_CSR(DMA_CHANNEL));
101:    dma_clear_irq(DMA_IRQ_LINE, DMA_CHANNEL);
102:
103:    dma_completed = 1;
104: }


割り込みハンドラーは、DMA 転送完了時に呼び出されます。
やっていることは、割り込み要因のクリアと、dma_completed に 1 を設定していることだけです。
100 行目が DMA4_CSR のクリア、101 行目が DMA4_IRQSTATUS_L のクリアです。

SDMA の初期化時に、ブロック完了割り込みをイネーブルしています。
それ以外の割り込みはディセーブルしているので、ブロック完了割り込み以外は上がってこないはずなのですが、念のため 98 行目でチェックしています。


uart_dma_test() を実行し、シリアルコンソールから、'1', '2', '3', '4', '5', '6', '7', '8' を入力すると、以下のようになります。

dma_test.png

・・・

これで、DMA の確認もできました。
相変わらずしょうもないテストですが・・・。

でも、DMA と割り込みを抑えると、なんかちょっと、デバイスを動かしているなあという実感が湧いてきます (^-^)

beagleboard を触ろう - UART 割り込みモード [組み込みソフト]


UART モジュールを割り込みモードで使ってみましょう。

UART は、データを受信した時、割り込みを上げることができます。
受信 FIFO に、あらかじめ設定した閾値以上のデータを受信すると、割り込みが上がります。

UART の受信割り込みのための設定は、閾値の値を決めることと、受信割り込みイネーブルビットを立てることの 2つだけです。

閾値の値は、SCR_REG, TLR_REG, FCR_REG によって決定します。

1. SCR_REG[7] RX_TRIG_GRANU1 ビットが 0 かつ TLR_REG[7:4] RX_FIFO_TRIG_DMA ビットフィールドが 0 の時
- FCR_REG[7:6] RX_FIFO_TRIG ビットフィールドの値で閾値が決定する
- 設定可能な値は 8, 16, 56, 60 の 4 通り

2. SCR_REG[7] RX_TRIG_GRANU1 ビットが 0 かつ TLR_REG[7:4] RX_FIFO_TRIG_DMA ビットフィールドが 0 以外の時
- TLR_REG[7:4] RX_FIFO_TRIG_DMA ビットフィールドの値で閾値が決定する
- 設定可能な値は 4, 8, 12,..., 60 の 15 通り

3. SCR_REG[7] RX_TRIG_GRANU1 ビットが 1 の時
- TLR_REG[7:4] RX_FIFO_TRIG_DMA を上位 4 ビット、FCR_REG[7:6] RX_FIFO_TRIG を下位 2 ビットとした 6 ビット値で閾値が決定する
- 設定可能な値は 1, 2, 3,..., 63 までの 63 通り


受信データが閾値バイト数以上、受信 FIFO にたまると、割り込みがアサートされます。
例えば、閾値を 1 に設定した場合、受信 FIFO に 1 バイト以上受信データが入れば、UART は割り込みをアサートします。
割り込み要因は、受信 FIFO から受信データを読み出すことによってクリアされます。

割り込み要因をクリアするには、受信 FIFO にたまっているデータが閾値未満になるまで取り出さなければなりません。
閾値を 1 に設定した場合、受信 FIFO に 2 バイトたまっていれば、1 バイト読み出しただけでは割り込み要因はクリアされず、UART は割り込みをアサートし続けます。

OMAP35x TRM に、この様子が図解されています。
赤丸で囲んだところが、受信 FIFO に閾値以上データがたまっている間です。
その間、割り込みがアサートされていることを表すのが、青線部分です。

RHR_intr.png


受信割り込みのイネーブルは、IER_REG にて行います。
IER_REG[0] RHR_IT ビットを立てると、受信割り込みがイネーブルされます。
IER_REG[0] RHR_IT ビットが落ちた状態だと、受信割り込みを上げる条件が満たされても、UART から割り込みがアサートされることはありません。

RHR_IT ビットは、受信割り込みとともにタイムアウト割り込みも有効にします。
受信 FIFO に受信データがあるのに、取り出さない場合は、タイムアウト割り込みが発生します。

タイムアウト割り込みを無効にすることは、多分できません。
また、タイムアウト値は固定値のような気がします(値不明)。
ここら辺、OMAP35x TRM には何も書かれていないと思うんですよねえ・・・。
それとも見落としているだけかな・・。。


閾値の設定、受信割り込みのイネーブルを含め、UART の初期化手順は OMAP35x TRM に載っています。
UART/IrDA/CIR 章の UART/IrDA/CIR Basic Programming Model 節です。

※実際のコード上の注意
UART のレジスタって、同じアドレスでもモードによって異なるレジスタが見えたり、また同じアドレスでも読み出しアクセスされる場合と、書き込みアクセスされる場合とで、異なるレジスタが見えたりと、ちょっと扱いづらいです。
特に、読み出しと書き込みでアクセスされるレジスタが違うというのは、厄介です。
例えば、アドレス 0x49020008 は、読み出しアクセスでは IIR レジスタが、書き込みアクセスでは FCR レジスタがアクセスされます。
FCR の RX_FIFO_TRIG ビットフィールドの値を変更しようとして、

FCR = (FCR & ~0xC0) | 0x40;

というコードを書くのはダメです。
これ、FCR = (IIR & ~0xC0) | 0x40 という意味になってしまいますから。
要注意です。


beagleboard に載っている RS-232 コネクタは、UART3 に接続されています。
UART3 に割り当てられている IRQ 番号は、74 です。
割り込みコントローラのマスクレジスタ (MIR) の IRQ_74 に対応するビットは、落としておく必要があります。
ここを落としておかないと、UART が割り込みを上げても割り込みコントローラがブロックしてしまいます。

また、CPU の割り込みマスクである CPSR の I ビットも、同じように落とす必要があります。
これも、落としておかないと、CPU でブロックされてしまいますね。

・UART の設定(閾値設定、受信割り込みイネーブル設定)
・割り込みコントローラの設定(MIR の設定)
・CPU の設定(CPSR の設定)

これらが設定できれば、受信割り込みを CPU で拾うことができます。


それではちょっとテストしてみましょう。

事前に、UART3 を割り込みモードで初期化しておきます。
受信 FIFO の閾値は 1 にして、IER_REG には RHR_BIT ビットを立てて、受信割り込みをイネーブルしておきます。
また、CPU の CPSR の I ビットも落としておきます。

初期化が終わったら、下のようなコードを動かしておきます。
これは、割り込みとは関係のない動作をします。

64: volatile char serial_ch;
65: static int uart_interrupt_test(void)
66: {
67:	int i;
68:
69:	serial_ch = 0;
70:	intc_enable_irq(UART3_IRQ);
71:
72:	while (serial_ch != 'q') {
73:		for (i = 0; i < WAIT_LOOP; i++)
74:			__asm__ volatile("nop");
75:		serial_printf("%s\n", __FUNCTION__);
76:	}
77:
78:	intc_disable_irq(UART3_IRQ);
79: }

WAIT_LOOP を適当に調節して、シリアルコンソール上に 1秒おきくらいに "uart_interrupt_test" が表示されるようにしておきます。
70 行目の intc_enable_irq() は、INTC (割り込みコントローラ) の MIR レジスタの IRQ_74 に対応するビットを落とします。
78 行目の intc_disable_irq() は、逆に対応するビットを立てます。
serial_ch は、割り込みハンドラーによって設定されます。
割り込みハンドラーが serial_ch に 'q' を設定したら、この関数はループから抜けて終了します。


割り込みハンドラーは、次のようなものを用意します。

29: extern volatile char serial_ch;
30: void uart_handler(int irq)
31: {
32:	int it_type;
33:
34:	it_type = (__raw_readb(UART3_IIR) & 0x3E) >> 1;
35:	if (it_type != 2)
36:		return;
37:
38:	uart3_int_cnt++;
39:	serial_ch = serial_getc();
40:	serial_printf("OK\n");
41: }

IIR レジスタは、[5:1] IT_TYPE ビットフィールドに、割り込みタイプを保持します。
RHR 割り込み(受信割り込み)が発生した場合は、割り込みタイプは 0x2 になります。
前述したように、RHR 割り込みをイネーブルした場合は、タイムアウト割り込みも有効になります。
タイムアウト割り込みが発生した場合は、割り込みタイプは 0x6 になります。
RHR 割り込みのみを扱うように、35 行目で判別を行っています。

39 行目での serial_getc() が、割り込み要因のクリアになります。
受信 FIFO からデータを読み出して、閾値以下にすることが割り込み要因クリアの条件ですので。
serial_getc() は、RHR レジスタから 1バイト読み出します。


この uart_handler() を、次のような register_handler() 関数を使って登録しておきます。

12: static void (*irq_handler[96])(int);
    :
33:
34: void register_handler(void (*handler)(int), int irq)
35: {
36:	irq_handler[irq] = handler;
37: }


register_handler(uart_handler, UART3_IRQ) 呼び出しによって登録します。



UART3 受信割り込みが発生すると、登録した uart_hander() は do_irq() の 26 行目で呼び出されます。

16: void do_irq(void)
17: {
18: 	int irq;
19:	void (*handler)(int);
20:
21:	irq_cnt++;
22:
23:	irq = __raw_readl(INTCPS_SIR_IRQ) & 0x7F;
24:	handler = irq_handler[irq];
25:	if (handler)
26:		handler(irq);
27:	sr32(INTCPS_CONTROL, 0, 1, 1);
28:	
29:	/* Data synchronization barrier */
30:	__asm__ volatile("mov r0, #0\n\t"
31:			 "mcr p15, 0, r0, c7, c10, 4\n");
32: }

受信 FIFO の閾値を 1 に設定しているので、受信割り込みはシリアルコンソールにキー入力が行われる度に発生します。
よって、uart_handler() は、シリアルコンソールにキー入力が行われる度に呼び出されることになり、その度に "OK" を表示します。

uart_interrupt_test() を実行させて、適当にキー入力をしてみると、こんな感じになります↓

interrupt_test.png

キー入力がある度に、"OK" が表示されています。
'q' を入力すると、uart_interrupt_test() は終了します。

・・・

今回は、受信 FIFO の閾値を 1 にしてみました。
閾値を 2 にすると、どうなるでしょうか。

閾値を 2 に設定した場合は、キー入力が 2 回行われないと、受信割り込みは上がってこないです。
その代わり、キー入力を 1回行った後、タイムアウト割り込みが上がってきます。
キー入力 1回では受信割り込みが発生せず、uart_handler() が呼び出されないので、受信 FIFO のデータが取り出されないからですね。

uart_handler() の 34-36 行目は、このタイムアウト割り込みを排除しています。

34:	it_type = (__raw_readb(UART3_IIR) & 0x3E) >> 1;
35:	if (it_type != 2)
36:		return;


タイムアウト割り込みが発生した場合は、uart_handler() で何も処理しないので、割り込み要因がクリアされません。
クリアされないので、uart_handler() が終わると、またタイムアウト割り込みが上がってしまいます。
タイムアウト割り込み発生 ⇒ ハンドラーで何もせず ⇒ タイムアウト割り込み発生・・・というループを延々繰り返すので、uart_interrupt_test() はまったく動けなくなります。

あと、閾値を 2 にした場合は、uart_handler() で serial_getc() を 2回呼び出して、受信 FIFO を空にしてあげる必要があります。
serial_getc() を 1回しか呼び出さなかったら、FIFO に 1バイト残ってしまって、またタイムアウト割り込みが発生してしまいますね。

キーを 1回入力する ⇒ uart_interrup_test() が動けなくなる ⇒ キーをもう 1回入力する ⇒ 受信割り込みが上がって割り込み要因がクリアされる ⇒ uart_interrupt_test() が動けるようになる

というような感じになります。

・・・

まあ、とりあえずこれで UART の割り込みモードが確認できました。

今まで受信割り込みについて見てきましたが、送信割り込みを発生させることも可能です。
送信割り込みの場合も、受信割り込みの場合と同様に、閾値を設定し、送信割り込みイネーブルビットを立てます。

送信割り込みが発生する条件は、送信 FIFO が閾値以下になることです。
逆に、送信 FIFO にデータが閾値より多くたまると、送信割り込みのアサートは止まります。
割り込み要因のクリア方法は、送信データを書きこむことだけです。
送信データはないけど割り込みを発生させたくない場合、割り込み要因はクリアできないので、この場合は割り込みコントローラの MIR レジスタでマスクするしかないですね。

THR_intr.png

送信割り込みの使い方は、

・普段は MIR レジスタでマスクして送信割り込みが上がらないようにしておく
・データ送信時に、送信 FIFO が一杯になって書き込めないとき、MIR のマスクを解除して送信割り込みが上がるようにする
・送信割り込みが上がってきたとき、保留していた送信データを送信 FIFO に書き込む
・保留していたデータをすべて送信したら、MIR レジスタをマスクして、元の状態に戻す

というような感じになると思います。

beagleboard を触ろう - 割り込み [組み込みソフト]


割り込み処理について見てみましょう。

ARM プロセッサーでは、割り込み(またはその他例外)が発生すると、ベクターテーブルにジャンプします。
ベクターテーブルの位置は、system control coprocessor control register の V ビットによって決まります。

V ビットが 0 の時:Vector Base Address Register が指すアドレス
V ビットが 1 の時:0xFFFF0000

起動時は、V ビットが 0 で Vector Base Address Register の値が 0x14000 なので、ベクターテーブルは 0x14000 にあるということになります。
OMAP3530 の場合、アドレス 0x14000 には Boot ROM がマップされています。
(正確には、Boot ROM は、本来 0x40000000 - 0x4001BFFF にマップされていて、リマップレジスタによって 0x00000000- 0x0001BFFF にもマップされているように見える・・・ハズ)
Boot ROM 上にあらかじめベクターテールが用意されているわけですね。


0x14000 に配置されているベクターテーブルの内容は、OMAP35x TRM に載っています。

ROMvector2.png

リセットベクターは、Boot ROM 内の startup コードにジャンプします。
それ以外は、0x4020FFxx というアドレスにジャンプします。
0x4020FFxx は、内臓 SRAM の一番終端部分ですね。

割り込み (IRQ) が発生した場合、

0x14018 にジャンプ
    ↓
0x4020FFDC にジャンプ
    ↓(0x4020FFDC に IRQ ハンドラへのジャンプ命令を書いておく)
IRQ ハンドラにジャンプ

という経路を経て、IRQ ハンドラに到達します。

0x4020FFC8 には未定義例外ハンドラへのジャンプ命令を、0x4020FFCC には SWI 例外ハンドラへのジャンプ命令を・・・ 0x4020FFE0 には FIQ ハンドラへのジャンプ命令を、それぞれ書き込んでおく必要があります。

でも、ジャンプを 2回もして、効率があまりよくなさそうです。

vector_jump.png

これとは別に、Vector Base Address Register を書き直して、SRAM 上のベクターテーブルに直接ジャンプさせることもできます。
この方法だと、上の方法よりジャンプが 1回少なくなって、経済的です。
しかし、x-loader は、2回ジャンプする方法を採っています。

・・・

ところが、、、

x-loader は、なぜか Boot ROM のベクターテーブルからのジャンプ先を 0x4020F800 として中継テーブルを構成しちゃってるんですよねえ・・・。
0x4020F800 じゃなくて 0x4020FFC8 じゃないですかあ。

まあ、x-loader 実行中は割り込み禁止にしてあるので、どうでもいいのかもしれませんが (^^;
そもそも、x-loader の例外ハンドラーは do_hang() という関数で、"X-Loader hangs" というメッセージをシリアルコンソールに残して無限ループするだけなので、例外をちゃんと処理するようにはできていません。
絶対例外発生しないはずなんだけど、万が一発生したら "X-Loader hangs" を出して通知する、くらいの感じかもしれません。

割り込みを扱うならば、ちゃんとやっておく必要があります。
以下のようなアセンブリコードを、0x4020FFC8 に配置しておきます。

_vector:
	b	reset
 	ldr	pc, _und
	ldr	pc, _swi
	ldr	pc, _pabort
	ldr	pc, _dabort
	ldr	pc, _unused
	ldr	pc, _irq
	ldr	pc, _fiq
_und:	
	.word _do_und
_swi:	
	.word _do_swi
_pabort:	
	.word _do_pabort
_dabort:	
	.word _do_dabort
_unused:	
	.word _do_unused
_irq:	
	.word _do_irq
_fiq:	
	.word _do_fiq


SRAM_vector2.png


これ以降は、とりあえず IRQ だけに注目します。

IRQ は、割り込みコントローラから CPU に上げられてきます。
OMAP3530 では 96個の IRQ がサポートされており、各番号ごとに特定のデバイスが割り当てられています。

interrupt_mapping2.png

デバイスからアサートされた IRQ が CPU に到達するには、2つの関門があります。
1つ目が、デバイスから割り込みコントローラへ IRQ を通すかどうかを決定する Mask Interrupt Register (MIR) によるマスクです。
MIR は MIR0 - MIR2 までの計 96 ビットのレジスタで、各ビットが IRQ 番号に対応しています。
MIR0 のビット 0 が IRQ_0, ビット 1 が IRQ_1,... MIR1 のビット 0 が IRQ_32,... MIR2 のビット 0 が IRQ_64,... のような感じです。
MIR のビットが立っていると、対応する IRQ はマスクされます。
即ち、割り込みコントローラは IRQ を受け付けません。
マスクされていた場合は、IRQ をアサートしているデバイスは、アサート信号を出し続けることになります。
そして、マスクが解除された時に、アサート信号が拾われて CPU に伝達されます。

2つ目が、Current Program Status Register (CPSR) の I ビットによるマスクです。
このビットが立っていると、CPU に割り込みコントローラからのアサート信号が上がっても、CPU は割り込みを受け付けません。
マスクされていた場合は、アサートしている割り込みコントローラは、アサート信号を出し続けることになります。
そして、マスクが解除された時に、アサート信号が拾われて割り込みが CPU に受け付けられます。

CPSR_MIR2.png

デバイスからの IRQ アサートが CPU に受け付けられるようにするには、MIR の対応するビットと CPSR の I ビットの両方を 0 にしなければいけないことになります。

割り込みが発生した場合、CPSR の I ビットは、自動的にマスクされます。
つまり、割り込み処理は、割り込み禁止状態で開始することになります。
割り込み処理が完了した後、元の処理に戻る際に、CPSR の I ビットは復元され、マスク解除されます。

MIR の方は、このような自動マスク機能はありません。
マスクしたい場合は、S/W が明示的に行う必要があります。


さて、割り込みハンドラー _do_irq はどのような処理をすればいいでしょう?

1. まず、IRQ 発生時に処理を行い、処理を行ったら元の処理に戻らなければならないので、レジスタ値を保存する処理が必要です。
2. 次に、本来の目的である、デバイスの割り込みに対応した、デバイス特有の処理を行う必要があります。
3. 最後に、保存したレジスタ値を復帰して、元の処理に戻ります。

270: _do_irq:	
271: 	sub lr,lr,#4
272: 	str lr,[sp,#-4]!
273:	mrs r14,spsr
274:	stmfd sp!,{r0-r3,r12,r14}
275:	bl do_irq
276:	ldmfd sp!,{r0-r3,r12,r14}
277:	msr spsr_csxf,r14
278:	ldmfd sp!,{pc}^


271 - 274 行目が 1 の処理、276 - 278 行目が 3 の処理になります。

_do_irq に到達した時点で、r13 (= sp) と r14 (= lr) は、Cortex-A8 の H/W が自動的に保存してくれます。
また、r15 (= pc) の値は、r14 にコピーされます。
これも H/W が自動的にやってくれます。
S/W として行わなければならないのは、r0 - r12, r14 の保存です。

まず r14 (= lr) ですが、272 行目でスタックに保存しています。
r14 には、割り込みが発生した時の r15 (= pc = program counter) の値が入っています。
この r15 の値は、割り込みが発生した時の命令アドレスが入っているわけではなく、+4 された値が入っています。
そのため、271 行目で lr の値を -4 しています。
こうしないと、割り込み処理終了後に元の処理に戻るとき、元の処理から +4 したところに戻ってしまいます。

273 行目で Saved Program Status Register (SPSR) の値を r14 にコピーしていますが、これは IRQ がネストした場合に備えてですね。
IRQ がネストしないように作ってやれば、この処理は不要なはずです。

274 行目で、r0 - r3, r12, r14 (= SPSR の値が入っている) のレジスタ値をスタックに保存しています。
これらの値を、276 行目でスタックから復帰させています。
なので、275 行目の呼び出し先 do_irq() で、これらのレジスタがどのように使われようが、元の状態に戻すことができますね。

S/W で保存しなければならないレジスタは r0 - r12, r14 なのですが、274 行目までに r0 - r3, r12, r14 の値しかスタックに保存していません。
残りの r4 - r11 の値は保存していませんが、いいんでしょうか。

ARM Procedure Call Standard では、r0 - r3, r12, r14 を caller で保障し、それ以外は callee が保障しなければならないことになっています。
275 行目で呼び出している do_irq より先で、r0 - r3, r12, r14 以外は破壊されないことが保証されるため、r4 - r11 は放っておいても大丈夫です。
コンパイラが r4 - r11 を保障するようなアセンブリコードを生成してくれます。


275 行目で呼び出している do_irq の先で、割り込み処理を行います。

14: static void (*irq_handler[96])(int);
15:
16: void do_irq(void)
17: {
18: 	int irq;
19: 	void (*handler)(int);
20:
21:	irq = __raw_readl(INTCPS_SIR_IRQ) & 0x7F;
22:	handler = irq_handler[irq];
23:	if (handler)
24:		handler(irq);
25:	sr32(INTCPS_CONTROL, 0, 1, 1);
26:	
27:	/* Data synchronization barrier */
28:	__asm__ volatile("mov r0, #0\n\t"
29:			 "mcr p15, 0, r0, c7, c10, 4\n");
30:}


21 行目でアクセスしている INTCPS_SIR_IRQ レジスタは、割り込みコントローラのレジスタで、現在アクティブになっている IRQ 番号を下位7ビットに保持しています。

この IRQ 番号で登録されているハンドラーがあれば、それを呼び出します。
この呼び出したハンドラーの中で、デバイス特有の処理を行います。
ハンドラーでは、通常は、割り込み要因を取り下げ、データを読み書きする処理を行います。

25 行目でアクセスしている INTCPS_CONTROL レジスタは、同じく割り込みコントローラのレジスタで、IRQ シグナルをリセットして、新しい IRQ が上がってくることを許可するためのレジスタです。

25 行目は分かりにくいですが、INTCPS_CONTROL[0] NEWIRQAGR ビットに 1 を立てています。
INTCPS_CONTROL[0] NEWIRQAGR に 1 を立てるまでは、割り込みコントローラから IRQ がアサートされ続けています。
INTCPS_CONTROL[0] NEWIRQAGR に 1 を立てることにより、アサートを止めると同時に、同じ IRQ 番号による次のアサートが可能になります。

24 行目で呼び出しているハンドラーの中で、デバイスの割り込み要因を取り下げないとどうなるでしょうか。
デバイスからは割り込みコントローラにアサートし続ける状態になってしまいます。
さらに、割り込み処理が終わって CPU の割り込みマスクが解除されると、CPU は IRQ を受け付けるようになりますので、今処理が終わったばかりの割り込みを再度処理することになります。
処理しても処理しても、割り込み要因が取り除かれないので、永久ループになってしまいますね。

こうなってはまずいので、24 行目で呼び出すハンドラーは、必ずデバイスの割り込み要因を取り下げなくてはなりません。(もしくは MIR で対応するビットを立ててマスクする。)

28 行目、29 行目では、data synchronization barrier を張っています。
この命令は、この行までのデータ読み書きを完了させてから、次の行以降を実行します。
これがあると、「INTCPS_CONTROL[0] NEWIRQAGR ビットを 1 にする ⇒ CPSR の I ビットを 0 にする」という順番が保証されます。
仮に、これが逆になって、「CPSR の I ビットを 0 にする ⇒ INTCPS_CONTROL[0] NEWIRQAGR ビットを 1 にする」という順番になってしまうとすると、IRQ がネストしてしまいます。
INTCPS_CONTROL[0] NEWIRQAGR ビットを 1 にしないと、割り込みコントローラは IRQ アサートを取り下げないからです。
・・・という理由で data synchronization barrier を配置するんだと思いますが、合ってるかな?
data synchronization barrier を張れ、というのは、OMAP35x TRM の Interrupt Controller 章の Interrupt Controller Basic Programming Model 節に書いてあります。


全体を通して、IRQ のアサートは、↓のように変遷します。

IRQ_assert_1of2.png
IRQ_assert_2of2.png

あ、そうだ。
事前に IRQ モードのスタックをちゃんと設定しておかないといけないのでした。
ARM は割り込みが発生すると、IRQ モードに自動的に移行し、スタックを IRQ モード用のスタックに変更します。
割り込みを有効にする前に、これをやっておかねばなりません。
こんな感じのコードです。

187:	/* stack setup for IRQ mode */
188:	mrs	r0,cpsr
189:	bic	r0,r0,#0x1f
190:	orr	r0,r0,#0xd2
191:	msr	cpsr,r0
192:	ldr	sp,=SRAM_STACK_IRQ


・・・

こんな感じで、割り込みを扱うには、少々の準備が必要です。
実際に自分で割り込みを動かしてみようとすると、結構はまってしまいます。
ちょっと間違えるとすぐに吹っ飛ぶし・・・。
ICE があればいいんですけどねえ。

beagleboard を触ろう - UART [組み込みソフト]



beagleboard は RS232 コネクタを持っていて、PC のシリアルコンソール (Teraterm 等) と通信できるのはご存じの通りです。
OMAP3530 の内部デバイスである UART モジュールから出ている信号線が、RS232 ポートに繋がっています。

UART モジュールには、UART1/UART2/UART3 の 3つが装備されています。
回路図を見ると、RS232 に結線されているのは UART3 であることが分かります。
x-loader が初期化しているのも UART3 でしたね。

同じく回路図を見てみると、UART3 からの UART3_TX と UART3_RX 信号が RS232 ポートに繋がっているのが分かりますが、UART3_CTS / UART3_RTS は死んでいることが分かります。

uart3_signal.png

CTS / RTS は、ハードウェアフロー制御を行う際に使われる信号で、この信号を使ってデータを取りこぼさないようにしています。
CTS / RTS が使えないので、beagleboard では、ハードウェアフロー制御はできません。

さっきから UART モジュールと言っていますが、このモジュールは UART のみではなく、IrDA / CIR としても使えます。
UART として使うか、IrDA として使うか、CIR として使うか、レジスタに設定値を書き込んでモードを決定します。
使ったことがないのでよく分かりませんが、IrDA / CIR は赤外線通信のためのデバイスです。

UART モジュールを S/W 的観点から絵にすると、こんな感じです。

uart_reg.png

THR が送信用のレジスタです。
このレジスタに 1バイトのデータを書くと、UART_TX から 1ビットずつデータを送信してくれます。
逆に、RHR が受信用のレジスタです。
このレジスタから 1バイトのデータを読むと、UART_RX から 1ビットずつ受信したデータを取得できます。
もちろん、実際に TX/RX を流れているデータをそのまま読み書きするわけではなくて、8N1 等のデータフォーマットに従って、実データのみを読み書きできます。

THR / RHR の裏には、64バイト分の FIFO が控えています。
FIFO はメモリの一種です。名前の通り、先に書いたものが先に読みだされる、特殊なメモリです。
データをたくさん送信する場合、THR に次々書き込んだデータは送信 FIFO に貯められます。
貯められたデータは、UART モジュールによって自動的に排出されます。
逆に、データを次々に受信する場合、UART_RX から受信したデータは、受信 FIFO に貯められます。
CPU (とか DMA とか) が RHR レジスタを読み出すことにより、受信 FIFO に貯められたデータが読み出されていきます。
CPU が RHR を読み出してくれなくても、受信したデータを FIFO に貯めることができるので、データを取りこぼさずに済みます。
しかし、受信 FIFO がフルの時、さらにデータを受信すると、さすがにデータを取りこぼしてしまいます。
これを防ぐにはフロー制御するしかないですが、普通はフロー制御なしでやっているように思います。
beagleboard の場合、ハードウェアフロー制御ができないので、やるとしたらソフトウェアフロー制御ですね。

なお、FIFO は無効にすることもできます。
この場合は、送信データを連続して THR に書き込むことはできなくなりますし、データを受信したら、すぐに RHR から読み出さなくてはなりません。
実際には、FIFO をバイパスするわけではなくて、FIFO の位置 0 だけが使われるようになるみたいです。

THR, RHR レジスタ以外にもいくつかレジスタがあります。
割り込みを使わないなら、設定系レジスタで動作モードを決定し、データ送受信の際には、ステータスレジスタでデータ送受信可能かチェックしながら THR/RHR を読み書きするような使い方になります。

UART モジュールの詳細な使い方は、OMAP35x TRM (Technical Reference Manual) に載っています。
"UART/IrDA/CIR Basic Programming Model" の節です。
ここでは、それを参考にしつつ、x-loader がやっていることを見ていきましょう。

x-loader による UART の初期化は、NS16550_init() という関数で行っています。
23: void NS16550_init (NS16550_t com_port, int baud_divisor)
24: {
25: 	com_port->ier = 0x00;
26: #ifdef CONFIG_OMAP
27: 	com_port->mdr1 = 0x7;   /* mode select reset TL16C750*/
28: #endif
29: 	com_port->lcr = LCR_BKSE | LCRVAL;
30: 	com_port->dll = baud_divisor & 0xff;
31: 	com_port->dlm = (baud_divisor >> 8) & 0xff;
32: 	com_port->lcr = LCRVAL;
33: 	com_port->mcr = MCRVAL;
34:  	com_port->fcr = FCRVAL;
35: #if defined(CONFIG_OMAP)
36: 	com_port->mdr1 = 0;	/* select uart mode */
37: #endif
38: }


com_port は、UART3 レジスタのベースアドレスである 0x49020000 が渡されてきます。
また、CONFIG_OMAP は 1 になっています。

25: UART のすべての割り込みを無効にする
27: UART-IrDA-CIR モードを無効にする
29: Configuration mode A に移行
30: ボーレートを 115200 bps に設定
31: ボーレートを 115200 bps に設定
32: データフォーマットを 8N1 に設定し、かつ Operational mode に移行
33: よく分からん (^^;
34: FIFO を有効化して送受信 FIFO ともクリアー
36: UART 16x モードに設定

ここでちょっと注意すべきことが。
dll とか dlm (OMAP35x TRM では DLM ではなく、DLH) とかは、通常はアクセスできないレジスタです。
Configuration mode を変更することによって、初めてアクセスできるようになります。
L29 の lcr への書き込みを行って Configuration mode A に移行することによって、dll, dlm にアクセスできるようになります。
L32 の lcr への書き込みを行って Operational mode に移行すると、dll, dlm はもう見えません。

データ送受信関数は、以下の通りです。
51: void NS16550_putc (NS16550_t com_port, char c)
52: {
53: 	while ((com_port->lsr & LSR_THRE) == 0);
54: 	com_port->thr = c;
55: }

57: char NS16550_getc (NS16550_t com_port)
58: {
59: 	while ((com_port->lsr & LSR_DR) == 0);
60: 	return (com_port->rbr);
61: }


53: LSR レジスタの TX_FIFO_E ビットをチェック
54: THR レジスタに 1バイト書き込み

59: LSR レジスタの RX_FIFO_E ビットをチェック
60: RHR レジスタに 1バイト書き込み

TX_FIFO_E ビットは、送信 FIFO が空だと 1 が立ちます。
NS16550_putc() は、FIFO が空になるのを待って、データを THR に書き込んでいるんですね。
これだと送信 FIFO の意味はまったくありませんが、まあ x-loader では起動メッセージを出すのにちょっと使っているだけなので、よいのでしょう (^^;

RX_FIFO_E ビットは、受信 FIFO にデータが存在すると 1 が立ちます。

THR, RHR は、同じアドレスにマップされています。
0x49020000 に書き込むと THR へのアクセス、0x49020000 から読み出すと RHR へのアクセスということになります。

・・・

x-loader の UART 制御は、いわゆるポーリングモードですよねえ。
UART モジュールは割り込みを上げることが出来るので、割り込みモードで使用することが出来ますし、また DMA チャネルも持っているので、DMA を使った送受信も可能です。

そちらも見てみないといけないですねえ。

beagleboard を触ろう - xloader 概要 [組み込みソフト]

これから OMAP3530 を少しずつ触ってみたいと思います。
x-loader に手を入れて、各種デバイスにアクセスしてみようと思います。

その前に・・・

x-loader が何をやっているか、ざっと眺めておきます。


MMU, キャッシュ初期設定
命令キャッシュ有効、データキャッシュ無効、分岐予測有効、MMU 無効という設定にしています。
ARM って MMU を有効にしないとキャッシュが効かないと思うんですが、それってデータキャッシュだけですかね?
命令キャッシュは MMU 無効でも効くのかなあ?
ちょっと謎です。


プロテクション解除
OMAP3530 の内部バスは、Sonics という会社の、Open Core Protocol (OCP) ベースの SonicsMX なるものが使われているようです。
OCP については、↓が非常に参考になります。

http://www.kumikomi.net/archives/2007/05/30ocp0.php

SonicsMX の概要についてはこちら↓

http://www.sonicsinc.com/uploads/pdfs/SMX_121007J.pdf

SoncisMX は、バスに繋がったデバイス間でアクセス制御をすることができるらしく、使いようによってはセキュリティーを高めることができる、ということですかね。
x-loader では、すべてのプロテクションを解除して、すべてのデバイスをアクセス OK にしています。
x-loader に制御が渡ってきた時点でプロテクション設定がされているとは思えませんが、念のためやっておくということなんですかね。


Pad multiplexing 設定
OMAP3530 チップからは、外部に信号線が百数十本出ていますが、これが内部でどのデバイスに繋がっているのか、あらかじめ決まっているわけではありません。
S/W で、コンフィギュレーションが可能になっています。

ただ、なんでもかんでも自由に設定できるわけではありません。
例えば、DSS_D0 という信号には、DSS_DATA0, DSI_DX0, UART1_CTS, DSSVENC656_DATA0, GPIO_70 のどれかを割り当てることができます。
ここでいう DSS_DATA0 とか、DSI_DX0 とか、UART1_CTS とかは、内部デバイスから出ている信号線です。

pad_multiplexing.png

どのように設定するかは、ボードに何が搭載されているかに依存します。
beagleboard には SDRC, NAND flash, USB コネクタ、S-Video コネクタ、オーディオコネクタ等々載っていますが、これらを正しく機能させるには pad multiplexing を適切に行う必要があります。
上の例の DSS_D0 の場合、DSS_D0 信号線は DVI-D インターフェースに結線されていて、DSS モジュールの DSS_DATA0 によって制御するので、それらをちゃんと結びつけるように設定する必要があるわけです。

ただ、x-loader は beagleboard 上のすべてのデバイスの pad multiplexing 設定をしているわけではありません。
SDRC, NAND flash, SD カード、UART, I2C, JTAG, その他 GPIO の設定だけです。
自分の使うデバイスだけですね。

割り当てを変更する機能の他に、信号線を (CPU から見て) 入出力両方とも可能にするか、出力のみ可能にするかの設定機能、各信号線をプルアップ/プルダウンするかどうか設定する機能、プルアップ/プルダウンするならば、プルアップにするかプルダウンにするか設定する機能もあります。
プルアップ/プルダウンは、プルアップ抵抗とかプルダウン抵抗とか使って、電圧不定値を避ける仕組みです。


クロック設定
OMAP3530 は各内部デバイスに対して、クロックを供給する/しないをコンフィギュレーションすることができます。
使用する内部デバイスに対しては、クロックを供給しないといけないので、それをやっています。
というか、使用しない内部デバイスも含めてほとんどすべての内部デバイスに対してクロック供給するような設定をしています。
クロックゲーティング(デバイスへのクロック供給停止)を行うと、省エネに効いてきますが、x-loader は次段のイメージをロードしたらすぐに終わりなので、省エネもくそもないですもんね。
なので、適当なんだと思います。
また、各デバイスに供給するクロック周波数を、CPU は 500MHz, DSP は 360MHz, バスクロックは 166/83 MHz となるように設定しています。
datasheet を見てみると、CPU クロックは 720MHz, DSP クロックは 520MHz, バスクロックは 166/83 MHz が最大値みたいなので、CPU クロックと DSP クロックは余裕がありますね。
これも、次段が適切に設定することを期待して、ある程度適当にやっているのかもしれません。


SDRAM コントローラ初期化
SDRAM コントローラリセット、CAS Latency 設定、AC タイミング設定、リフレッシュ設定、SDRAM に対する初期化シーケンスの発行等々をやっています。
SDRAM なのに AC タイミングを設定してやらないといけないんですねえ。
ここで SDRAM コントローラを初期化することによって、初めて SDRAM にアクセスできるようになります。

設定するパラメータは、搭載されている DDR-SDRAM に依存するものであり、適切な値を設定するには DDR-SDRAM の datasheet が必要です。
beagleboard の System Reference Manual を見てみると、DDR-SDRAM は POP memory という実装方法で、OMAP3530 の上に NAND flash と一緒に搭載されています。
この POP memory は Micron 製であり、MT29C2G48MAKLCJI-6 という型番のようです。また、その中の SDRAM の型番は、MT46H32M32LFJG-6 IT のようです。

pop_memory.png


UART 初期化
UART3 を初期化してボーレート 115200bps, 8N1 に設定します。
x-loader のブート時に、シリアルコンソールに "Texas Instruments X-Loader 1.4.4ss (Jul 27 2011 - 22:17:10)" のようなメッセージが出ますが、UART 初期化を行った直後にこのメッセージを出しています。
Windows 側のシリアルコンソールを 115200bps, 8N1 にしなければいけないのは、ここでの設定に合わせなければいけないからですね。


NAND 初期化
NAND flash ROM は、General Purpose Memory Controller (GPMC) というモジュールの先に繋がっています。
ここでは、NAND flash を初期化しているわけではなくて、GPMC の初期化を行っています。
NAND flash は、SDRAM のような初期化シーケンスは必要ないんですね。
GPMC の初期化をするだけで使えちゃいます。
設定パラメータは、搭載されている NAND flash に依存します。
しかし、こちらも DDR-SDRAM 同様、型番不明ですかねー。
System Reference Manual には、SDRAM ともども Micron 製だとは書いてあります。


I2C 初期化
I2C1 をクロック 12MHz で初期化します。
なぜ I2C の初期化をするかというと、SD カードに電力を供給するためです。
SD カードは電源モジュール TPS65950 から電力を供給されています。
TPS65950 は、I2C1 に繋がっているので、TPS65950 制御のために I2C1 を初期化しておくんですね。


SD カード初期化
SD カードコントローラおよび SD カードに対する初期化を行っています。
SD カードに 3.0V を供給するための設定、SD カードコントローラに対する初期化シーケンスの実行、SD カードか MMC かの判別、SD カードの初期化の実行、アドレス設定、クロック設定、読み書きブロック長設定、SD カードの容量取得を行っています。
SD カードは、MMC のアッパーコンパチで、S/W 的には MMC と全く同じように扱えるらしいですが、ここではちゃんと扱いを分けてますね。
データ転送幅は、SD カードは最大 4ビット、MMC は最大 8ビットいけるはずですが、ここではどちらのケースでも 1ビットに設定しています。
4ビットなり 8ビットなりに設定すれば、それだけ速くなるはずです。


u-boot のロード
SD カードからのロードをまず試みます。
それが失敗したら(=カードが刺さってなかった、u-boot.bin が見つからなかった)、NAND からのロードを試みます。
ロードアドレスは 0x80008000 です。
なぜ 0x80008000 かというと、u-boot が 0x80008000 が開始アドレスになるようにビルドされているからです。
ちなみに、SDRAM は 0x80000000 - 0x90000000 にマップされているので、0x80008000 は SDRAM の最初の方のアドレスです。
ロードが完了したら、0x80008000 にジャンプして、u-boot 実行です。


・・・

というのが、x-loader の概要でした。

beagleboard を触ろう - ドキュメント入手 [組み込みソフト]

x-loader, u-boot, Linux が一通りビルドできるようになったので、そろそろ OMAP3530 の中身の探索をしたいと思います。
そのためには、ドキュメントが必要です。
ドキュメントを集めましょう。


Beagleboard System Reference Manual
SRM.png
http://beagleboard.org/ の一番先頭、Getting Started にあります。
System Reference Manuals の直下に BeagleBoard というリンクがありますんで、それです。
beagleboard の写真が載ってて、このチップは何々、このコネクタは何々とかが分かります。
後は OMAP3530 のブロック図とか搭載デバイスの回路図とか簡単な説明とかです。
全部で 180ページくらいですが、ざっと眺めておくと、beagleboard 全体の雰囲気が掴めそうです。


回路図
schematic.png
Redstar という会社が beagleboard を取り扱っていて、そこのページに beagleboard の回路図があります。

http://www.redstar.co.jp/bb.htm

OMAP3530 外部のコネクタ (RS-232 とか USB とか) が、OMAP3530 ピンのどこに繋がっているのかとか調べる時に参照します。


OMAP3530 datasheet
omap3530ap.png
google で OMAP3530 で検索すると引っ掛かるページ

http://focus.tij.co.jp/jp/docs/prod/folders/print/omap3530.html

に、OMAP3530 に関するいろいろなドキュメントがあります。
一番先頭にあるのが "OMAP3530/25 Applications Processor" というドキュメントです。
あまり内容を吟味していませんが、H/W 寄りの情報なので、S/W 的にはあまり参照しないと思います。


OMAP35x Technical Reference Manual
omap35xtrm.png
同ページ http://focus.tij.co.jp/jp/docs/prod/folders/print/omap3530.html のユーザーガイドという項に、"OMAP35x Technical Reference Manual" があります。
総ページ数 3456 ページの超大作です!

OMAP3530 に搭載されている各デバイスの詳細な説明がてんこ盛りで、デバイスの使い方、レジスタ仕様が書かれています。
あまりに分厚すぎて恐れをなしてしまいますが、S/W 的には外せない情報なので、必要な個所を必要なだけ読む、というスタンスですかねー。
各章、だいたい Overview, Environment, Integration, Functional Description, Basic Programming Model の節に分かれています。

Overview
このデバイスはこんな特徴を持っています、こんなスペックです、というような概要説明

Environment
そのデバイスの外部との関わりについて、通信におけるデータフォーマットなど

Integration
そのデバイスの内部との関わりについて、クロック、DMA, 割り込みなど

Functional Description
そのデバイスの機能説明

Basic Programming Model
初期化するにはレジスタをこう設定する、データ受信するにはこう設定する、等々レジスタレベルでの制御方法の説明

Register Manual
レジスタの詳細な説明

一番大事なのは Functional Description でしょうか。
次いで Basic Programming Model, Register Manual を読んでみると、何となく制御方法は分かってくるように思います。


TPS65950 Technical Reference Manual
tps65950trm.png
beagleboard 上には TPS65950 という電源モジュールが搭載されています。
これを制御するための情報が、このマニュアルです。
google で "tps65950 trm" で検索すると、Texas Instruments の TPS65950 のページが引っ掛かりますので、そこの User Guides のところから TRM をダウンロードです。
こちらは、そんなに参照することはないと思います。
934 ページもあるし・・・。


ARM v7-A&R Architecture Reference Manual
armarm.png
OMAP3530 に搭載されている CPU コアは Cortex-A8 であり、Cortex-A8 は ARM v7 アーキテクチャーです。
v7 アーキテクチャーの詳細な説明がこれです。
左側が英語、右側が日本語という変わった PDF です。
ARM ARM と呼ばれたりしているみたいです。

ARM のサイト http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0406b/index.html から取得できます。
ただし、ユーザー登録が必要です。

google で "arm v7-a architecture reference manual" で検索すると、日本語訳がついていない英語のみのドキュメントが引っ掛かりますね。
これだとユーザー登録しなくても取得できてしまいますね。
いいのかな・・。

このドキュメントも全 4287 ページからなる超大作で、辞書的に参照するのがよいと思います。
かといって、辞書的に見ても分からない用語がたくさん出てきたりして、ちょっと使いにくいんですよねー。
日本語検索できないし・・。
PDF 内にリンクが張ってなくて、例えば目次でこの項目見たい、と思ってもリンクがないのでジャンプできなくて、不便だったり・・。
贅沢は言えませんが。


Cortex-A8 Technical Reference Manual
cortexa8trm.png
コプロセッサーの説明が役立ちます。(Chapter 3 System Control Coprocessor)
キャッシュやら MMU やらバリアなど、様々な制御がコプロセッサーに対するコマンド発行で行われるので、結構参照すると思います。
こちらも ARM のサイトから取得することができます。

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0344c/index.html

これは、ARM ARM と違って、ユーザー登録不要です。

beagleboard を触ろう - Linux ビルド [組み込みソフト]

続いて Linux のビルドです。

ソースコードの入手方法とビルド方法は http://elinux.org/BeagleBoard#Linux_kernel に書いてあります。

それでは書いてある通りにやってみましょう。
ソースコードの取得は、時間帯やネットワーク環境にも依るかもしれませんが、結構時間がかかります。
うちの ADSL 環境では10分から15分くらい。

$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap-2.6.git linux-omap-2.6↓

コンパイルは↓のように書いてあるのですが、CROSS_COMPILE の指定が抜けていますね。

$ cd linux-omap-2.6↓
$ make distclean↓
$ make ARCH=arm omap2plus_defconfig↓
$ make ARCH=arm uImage↓

以下のようにやりましょう。

$ cd linux-omap-2.6↓
$ make distclean↓
$ make ARCH=arm omap2plus_defconfig↓
$ make CROSS_COMPILE=arm-none-linux-gnueabi- ARCH=arm uImage↓

arch/arm/boot ディレクトリに uImage が出来上がります。
この uImage を SD カードにコピーして、ブートしてみましょう。

・・・ところが、あらまー。
以下のメッセージを出すだけで、それ以上ピクリとも動きません!


Starting kernel ...

Uncompressing Linux... done, booting the kernel.


いろいろ調べてみると・・・

http://ameblo.jp/fadis/entry-10257409831.html

という非常に参考になるページが見つかりました。
このページによると、OpenEmbedded プロジェクトが linux-omap パッチを用意してくれているみたいです。
なので、OpenEmbedded の linux-omap パッチを取得します。

$ git clone git://git.openembedded.net/openembedded↓

openembedded ディレクトリができて、このディレクトリ下にパッチがダウンロードされます。

・・・

のはずなんですが、ダメですね。

$ git clone git://git.openembedded.net/openembedded
Cloning into openembedded...
git.openembedded.net[0: 140.211.169.165]: errno=Connection refused
fatal: unable to connect a socket (Connection refused)

というエラーが出て、サーバーに繋げません・・・。
1か月くらい前は OK だったのですが・・・。
うーん、とりあえず OpenEmbedded のホームページに行ってみましょう。

http://www.openembedded.org/index.php/Main_Page

左の方に、"git server" というリンクがあるので、ここをクリックすると、

http://cgit.openembedded.org/

に行きます。
ここのページで、一番左の列にある "openembedded" をクリックすると、

http://cgit.openembedded.org/cgit.cgi/openembedded/

に行きます。
ここから適当に取ってきましょう。
release2011.03 という shapshot が良さそうです。

http://cgit.openembedded.org/cgit.cgi/openembedded/snapshot/openembedded-release-2011.03.tar.bz2

を取ってきましょう。
これを解凍すると、openembedded-release-2011.03 というディレクトリができます。
この中に、パッチが入っています。

openembedded-release-2011.03/recipes/linux/ には、
   :
linux-omap-2.6-2.6.9-omap1
linux-omap-2.6.26
linux-omap-2.6.27
linux-omap-2.6.28
linux-omap-2.6.29
linux-omap-2.6.31
linux-omap-2.6.32
linux-omap-2.6.37
linux-omap-2.6.38
linux-omap-2.6.39
linux-omap-2.6_2.6.9-omap1.bb
   :

等のディレクトリがあります。
これらのディレクトリが、各カーネルバージョンに対応したパッチの模様です。
このどれかを使いましょう。

一方、kernel.org から取得済みの linux-omap-2.6 にどのようなブランチがあるか調べてみると、

$ cd linux-omap-2.6↓
$ git branch -r↓
   :
origin/omap-2.6.28
origin/omap-2.6.29
origin/omap-2.6.30
origin/omap-2.6.31
origin/omap-2.6.32
   :

とかがあります。
git の branch -r は、リモートのリポジトリにあるブランチをリストアップするコマンドです。

kernel.org の linux-omap ソースと、openembedded のパッチとを見比べて、その中で一番最新なバージョンにしましょう。
ということで、2.6.32 を選びます。

それでは omap-2.6.32 をベースにパッチを当てていきましょう。
まず、omap-2.6.32 ブランチに切り替えます。

$ git checkout origin/omap-2.6.32↓

この状態で作業を開始することもできますが、パッチを当ててソースコードを変更することになるので、自分用のブランチを作りましょう。

$ git checkout -b omap-2.6.32-beagle-patched↓

次に、openembedded の omap-2.6.32 パッチを当てていきます。
先ほど取得した openembedded-release-2011.03 ディレクトリのうち、omap-2.6.32 パッチのディレクトリ openembedded-release-2011.03/recipes/linux/linux-omap-2.6.32 には、以下のファイルがあります。


0001-ARM-OMAP-Overo-Add-support-for-second-ethernet-po.patch
0002-ARM-OMAP-ehci-omap-use-new-location-for-usb.h-inc.patch
0003-drivers-net-smsc911x-return-ENODEV-if-device-is-n.patch
0004-drivers-input-touchscreen-ads7846-return-ENODEV.patch
0005-ARM-OMAP-add-support-for-TCT-Zippy-to-Beagle-board.patch
0006-ARM-OMAP-Make-beagle-u-boot-partition-writable.patch
0007-ASoC-enable-audio-capture-by-default-for-twl4030.patch
0008-scripts-Makefile.fwinst-fix-typo-missing-space-in.patch
0009-MTD-NAND-omap2-proper-fix-for-subpage-read-ECC-error.patch
holes.patch
sctp-fix.patch

beagleboard/defconfig

cm-t35/0001-OMAP-DSS2-add-Toppoly-TDO35S-panel.patch
cm-t35/0001-backlight-tdo24m-ensure-chip-select-changes-between-.patch
cm-t35/0001-omap3-cm-t35-add-mux-initialization.patch
cm-t35/0002-omap3-cm-t35-add-DSS2-display-support.patch
cm-t35/0003-omap3-cm-t35-update-defconfig-for-DSS2.patch
cm-t35/0004-omap3-cm-t35-add-cm-t35-mux-configs.patch
cm-t35/0006-omap3-cm-t35-update-defconfig.patch

dss2/0012-OMAP-DSS2-Add-support-for-LG-Philips-LB035Q02-pane.patch
dss2/0014-OMAP-DSS-Add-DSS2-support-for-Overo.patch
dss2/0015-OMAP-DSS-Add-DSS2-support-for-Beagle.patch
dss2/0016-video-add-timings-for-hd720.patch
dss2/beagle-dss2-support.diff

madc/0009-drivers-mfd-add-twl4030-madc-driver.patch
madc/0010-ARM-OMAP-Add-twl4030-madc-support-to-Overo.patch
madc/0011-ARM-OMAP-Add-twl4030-madc-support-to-Beagle.patch
madc/0013-ARM-OMAP-Add-missing-twl4030-madc-header-file.patch

usb/ehci-omap.c-mach-to-plat.diff
usb/ehci.patch


openembedded-release-2011.03/recipes/linux/linux-omap-2.6.32 ディレクトリ直下にある 11 個のパッチは当てるとして、他はどうしましょう。

cm-t35 というのは、CompuLab という会社が出している評価ボードのようです。
beagleboard とは無関係なので、cm-t35 ディレクトリのパッチは無視します。

madc ディレクトリ下のパッチ名に含まれている TWL4030 は、Texas Instruments 製の電源モジュールです。
beagleboard rev.C4 に搭載されている電源モジュールは TPS65950 というモジュールです。
TWL4030 は、rev.C2 以前の beagleboard に搭載されていたようですね。
Linux は TWL4030 を制御するつもりで TPS65950 にアクセスしにいきますが(※)、TWL4030 と TPS65950 はソフトウェア的には同じに見えるようなので、問題なしです。
madc ディレクトリのパッチは当てます。
※ Linux のブートメッセージで "twl4030" が出ていたので、そうかなと (^^;

DSS は、Display Sub System の略で、OMAP3530 搭載デバイスなので、dss2 ディレクトリのパッチも当てます。

また、OMAP3530 搭載の USB ホストコントローラは、EHCI コントローラなので、usb/ehci.patch も当てます。


$ patch -p1 <../openembedded-release-2011.03/recipes/linux/linux-omap-2.6.32/0001-ARM-OMAP-Overo-Add-support-for-second-ethernet-po.patch↓
patching file arch/arm/mach-omap2/board-overo.c

$ patch -p1 <../openembedded-release-2011.03/recipes/linux/linux-omap-2.6.32/0002-ARM-OMAP-ehci-omap-use-new-location-for-usb.h-inc.patch↓
patching file drivers/usb/host/ehci-omap.c

$ patch -p1 <../openembedded-release-2011.03/recipes/linux/linux-omap-2.6.32/0003-drivers-net-smsc911x-return-ENODEV-if-device-is-n.patch↓
patching file drivers/net/smsc911x.c

$ patch -p1 <../openembedded-release-2011.03/recipes/linux/linux-omap-2.6.32/0004-drivers-input-touchscreen-ads7846-return-ENODEV.patch↓
patching file drivers/input/touchscreen/ads7846.c

$ patch -p1 <../openembedded-release-2011.03/recipes/linux/linux-omap-2.6.32/0005-ARM-OMAP-add-support-for-TCT-Zippy-to-Beagle-board.patch↓
patching file arch/arm/mach-omap2/board-omap3beagle.c

$ patch -p1 <../openembedded-release-2011.03/recipes/linux/linux-omap-2.6.32/0006-ARM-OMAP-Make-beagle-u-boot-partition-writable.patch↓
patching file arch/arm/mach-omap2/board-omap3beagle.c

$ patch -p1 <../openembedded-release-2011.03/recipes/linux/linux-omap-2.6.32/0007-ASoC-enable-audio-capture-by-default-for-twl4030.patch↓
patching file sound/soc/codecs/twl4030.c

$ patch -p1 <../openembedded-release-2011.03/recipes/linux/linux-omap-2.6.32/0008-scripts-Makefile.fwinst-fix-typo-missing-space-in.patch↓
patching file scripts/Makefile.fwinst

$ patch -p1 <../openembedded-release-2011.03/recipes/linux/linux-omap-2.6.32/0009-MTD-NAND-omap2-proper-fix-for-subpage-read-ECC-error.patch↓
patching file drivers/mtd/nand/omap2.c

$ patch -p1 <../openembedded-release-2011.03/recipes/linux/linux-omap-2.6.32/holes.patch↓
patching file arch/arm/Kconfig
Hunk #1 succeeded at 687 (offset -12 lines).

$ patch -p1 <../openembedded-release-2011.03/recipes/linux/linux-omap-2.6.32/sctp-fix.patch↓
patching file net/sctp/sysctl.c
Hunk #1 FAILED at 248. ← 該当箇所見当たらず
1 out of 1 hunk FAILED -- saving rejects to file net/sctp/sysctl.c.rej

$ patch -p1 <../openembedded-release-2011.03/recipes/linux/linux-omap-2.6.32/dss2/0012-OMAP-DSS2-Add-support-for-LG-Philips-LB035Q02-pane.patch↓
patching file drivers/video/omap2/displays/Kconfig
patching file drivers/video/omap2/displays/Makefile
patching file drivers/video/omap2/displays/panel-lgphilips-lb035q02.c

$ patch -p1 <../openembedded-release-2011.03/recipes/linux/linux-omap-2.6.32/dss2/0014-OMAP-DSS-Add-DSS2-support-for-Overo.patch↓
patching file arch/arm/mach-omap2/board-overo.c
Hunk #8 FAILED at 536. ← このパッチ修正は既に行われている
Hunk #9 succeeded at 545 (offset -5 lines).
Hunk #10 succeeded at 567 (offset -5 lines).
Hunk #11 succeeded at 609 (offset -5 lines).
Hunk #12 succeeded at 632 (offset -5 lines).
1 out of 12 hunks FAILED -- saving rejects to file arch/arm/mach-omap2/board-overo.c.rej

$ patch -p1 <../openembedded-release-2011.03/recipes/linux/linux-omap-2.6.32/dss2/0015-OMAP-DSS-Add-DSS2-support-for-Beagle.patch↓
patching file arch/arm/mach-omap2/board-omap3beagle.c
Hunk #5 succeeded at 495 (offset -5 lines).
Hunk #6 succeeded at 507 (offset -5 lines).
Hunk #7 succeeded at 583 (offset -5 lines).

$ patch -p1 <../openembedded-release-2011.03/recipes/linux/linux-omap-2.6.32/dss2/0016-video-add-timings-for-hd720.patch↓
patching file drivers/video/modedb.c

$ patch -p1 <../openembedded-release-2011.03/recipes/linux/linux-omap-2.6.32/madc/0009-drivers-mfd-add-twl4030-madc-driver.patch↓
patching file drivers/mfd/Kconfig
patching file drivers/mfd/Makefile
patching file drivers/mfd/twl4030-madc.c

$ patch -p1 <../openembedded-release-2011.03/recipes/linux/linux-omap-2.6.32/madc/0010-ARM-OMAP-Add-twl4030-madc-support-to-Overo.patch↓
patching file arch/arm/mach-omap2/board-overo.c
Hunk #1 succeeded at 538 (offset 148 lines).

$ patch -p1 <../openembedded-release-2011.03/recipes/linux/linux-omap-2.6.32/madc/0011-ARM-OMAP-Add-twl4030-madc-support-to-Beagle.patch↓
patching file arch/arm/mach-omap2/board-omap3beagle.c
Hunk #1 succeeded at 395 (offset 81 lines).
Hunk #2 succeeded at 407 (offset 81 lines).

$ patch -p1 <../openembedded-release-2011.03/recipes/linux/linux-omap-2.6.32/madc/0013-ARM-OMAP-Add-missing-twl4030-madc-header-file.patch
patching file include/linux/i2c/twl4030-madc.h

$ patch -p1 <../openembedded-release-2011.03/recipes/linux/linux-omap-2.6.32/usb/ehci.patch↓
patching file arch/arm/mach-omap2/Kconfig
Reversed (or previously applied) patch detected! Assume -R? [n] ←このパッチ修正は既に行われている

sctp-fix.patch の失敗が気になりますが、該当箇所が見つからないので、仕方ないっす。
修正ファイル一覧を見ると、こんな感じ。

$ git checkout↓
M arch/arm/Kconfig
M arch/arm/mach-omap2/board-omap3beagle.c
M arch/arm/mach-omap2/board-overo.c
M drivers/input/touchscreen/ads7846.c
M drivers/mfd/Kconfig
M drivers/mfd/Makefile
M drivers/mtd/nand/omap2.c
M drivers/net/smsc911x.c
M drivers/usb/host/ehci-omap.c
M drivers/video/modedb.c
M drivers/video/omap2/displays/Kconfig
M drivers/video/omap2/displays/Makefile
M scripts/Makefile.fwinst
M sound/soc/codecs/twl4030.c


これでソースが揃いました。
カーネルコンフィギュレーションファイルは、パッチディレクトリの中にある beagle/defconfig を使いましょう。
arch/arm/configs/omap3_beagleoe_defconfig という名前で beagle/defconfig をコピーします。

ではビルドしてブートしてみましょう。

   :

あれあれ・・・。
さっきと全く同じ現象ですねー。


Starting kernel ...

Uncompressing Linux... done, booting the kernel.


ここで止まっちゃいます。

むー、どうしよう。
よく分かんないけど、カーネルコンフィギュレーションを変えてみようかしら。
Angstrom のカーネルコンフィギュレーションでも試してみますか。
Angstrom の demo image

http://www.angstrom-distribution.org/beagleboard-demo-image-available

でブートさせて # zcat /proc/config.gz↓ でコンフィギュレーションを見ることができます。
これをそのままコピーして、arch/arm/configs/omap3_beagleangstrom_defconfig という名前のコンフィギュレーションファイルを作ります。
あ、コンフィギュレーションファイルの改行コードは、CRLF でなくて LF でないとダメかも。

それでは再びビルドです。


$ make distclean↓
$ make ARCH=arm omap3_beagleangstrom_defconfig↓
$ make CROSS_COMPILE=arm-none-linux-gnueabi- ARCH=arm uImage↓
$ make CROSS_COMPILE=arm-none-linux-gnueabi- ARCH=arm modules↓
$ make CROSS_COMPILE=arm-none-linux-gnueabi- ARCH=arm modules_install INSTALL_MOD_PATH=./modules↓


ビルドが終わると、カーネルイメージ arch/arm/boot/uImage が出来上がり、./modules ディレクトリにはローダブルモジュールファイルが出来上がります。

出来上がったローダブルモジュールを見てみると・・・

うむー。
GPU とか DSP とかのローダブルモジュールが出来てないなあ。
GPU, DSP のローダブルモジュールは、angstrom のデモ rootfs の中には /lib/modules/2.6.32/ 以下に存在しているんです。
ひょっとしたらビルドできていないかなあと思っていましたが、ダメでした。
でも、まあいいや。
ビルドしたカーネルはバージョン 2.6.32 で、angstrom デモのカーネルバージョンと同じなので、ローダブルモジュールはそのまま使えるはず。

ビルドしたファイルでブートしてみましょう。
uImage を SD カードの FAT32 領域にコピーします。
ローダブルモジュールはせっかくビルドしましたが、angstrom デモの rootfs にあるローダブルモジュールをそのまま使うので、何もせずです。

それではブートです。


:
FATAL: Error inserting cmemk (/lib/modules/2.6.32/kernel/drivers/dsp/cmemk.ko): Invalid module format
:


ありゃ。
バージョンミスマッチですかあ。
カーネルバージョン同じなだけじゃダメなんだっけ?

まあ、でも一応ブートして X も起動して液晶ディスプレイにちゃんと表示されているので・・
画面、変な余白があって、文字も微妙に変な横線が入っているけど・・

angstromX.png

angstromXfont.png

rootfs をちゃんと作らないといけないんでしょうが、とりあえずよしとしましょう・・・。

beagleboard を触ろう - u-boot ビルド [組み込みソフト]


次は u-boot のビルドです。
x-loader の時と同じく、

http://elinux.org/BeagleBoard#Source

を見ましょう。
u-boot の git リポジトリは

git://git.denx.de/u-boot.git u-boot-main

であるように書かれていますが、"Update on April 23 - 2010" という但し書きに、

http://gitorious.org/beagleboard-validation/

にアップデート版があるように書かれています。
このリンクに行ってみると、

git://gitorious.org/beagleboard-validation/u-boot.git

が u-boot のリポジトリのようです。
こちらを入手しましょう。

$ git clone git://gitorious.org/beagleboard-validation/u-boot.git u-boot↓
$ cd u-boot↓
$ git checkout --track -b omap3 origin/master↓

ビルド方法も書かれています。

$ make CROSS_COMPILE=arm-none-linux-gnueabi- mrproper↓
$ make CROSS_COMPILE=arm-none-linux-gnueabi- omap3_beagle_config↓
$ make CROSS_COMPILE=arm-none-linux-gnueabi-↓

ところが、あれれ・・。
最後のリンクのところで、↓のようなエラーメッセージが出ちゃいます。

/home/beagle/CodeSourcery/Sourcery_G++_Lite_GNU/bin/../lib/gcc/arm-none-linux-gnueabi/4.5.2/armv4t/libgcc.a(_bswapsi2.o):(.ARM.exidx+0x0): undefined reference to `__aeabi_unwind_cpp_pr0'


いろいろ調べてみると、

http://lists.denx.de/pipermail/u-boot/2010-May/071363.html

のパッチを当てるといいみたいです。
diff --git a/arch/arm/lib/eabi_compat.c b/arch/arm/lib/eabi_compat.c
index 86eacf1..eb3e26d 100644
--- a/arch/arm/lib/eabi_compat.c
+++ b/arch/arm/lib/eabi_compat.c
@@ -16,3 +16,8 @@ int raise (int signum)
 	printf("raise: Signal # %d caught\n", signum);
 	return 0;
 }
+
+/* Dummy function to avoid linker complaints */
+void __aeabi_unwind_cpp_pr0(void)
+{
+};

↑の部分を u-boot/aeabi_unwind.patch というファイルで保存して、u-boot ディレクトリから

$ patch -p1 < aeabi_unwind.patch↓

を実行です。
この結果、arch/arm/lib/eabi_compat.c に↓のようなダミー関数が追加されます。

/* Dummy function to avoid linker complaints */
void __aeabi_unwind_cpp_pr0(void)
{
};

こんな方法でいいのか。
それではもう一度ビルドを。

今度は OK です。
u-boot.bin ができました。

それでは u-boot.bin を SD カードにコピーしてブートしてみましょう。


Texas Instruments X-Loader 1.4.4ss (Jul 17 2011 - 07:57:49)
Beagle Rev C4
Reading boot sector
Loading u-boot.bin from mmc


U-Boot 2010.03-dirty ( 7月 21 2011 - 01:21:06)

OMAP3530-GP ES3.1, CPU-OPP2 L3-165MHz
OMAP3 Beagle board + LPDDR/NAND
I2C: ready
DRAM: 0 kB
NAND: 256 MiB
*** Warning - bad CRC or NAND, using default environment


あれれ・・。
DRAM が 0 kB となっていますね。
SDRAM コントローラの初期化に失敗しているのかな・・。
おかげで Linux のブートにも失敗しちゃいます。


しょうがないので、http://elinux.org/BeagleBoard#U-Boot に書かれている方法で再チャレンジしましょう。

$ git clone git://git.denx.de/u-boot.git u-boot-main↓
$ cd u-boot-main↓
$ git checkout --track -b omap3 origin/master↓

別のリポジトリからソースコードを取ってきました。
ビルド方法は同じです。

$ make CROSS_COMPILE=arm-none-linux-gnueabi- mrproper↓
$ make CROSS_COMPILE=arm-none-linux-gnueabi- omap3_beagle_config↓
$ make CROSS_COMPILE=arm-none-linux-gnueabi-↓

今回も、無事 u-boot.bin ができました。
これを SD カードにコピーしてブートしてみましょう。

今度は RAM も 256 MB と認識されています。
Linux もちゃんとブートしますね。
液晶ディスプレイに繋いでも何も出ないのが、ちょっと難ですが・・。
angstrom のデモイメージの u-boot だと、u-boot 実行時点で液晶ディスプレイがオレンジ色になるんですけど・・・。

とりあえずですが、x-loader と u-boot をソースコードから作ることができました。
次は Linux です!


↓ ブートメッセージ

Texas Instruments X-Loader 1.4.4ss (Jul 17 2011 - 07:57:49)
Beagle Rev C4
Reading boot sector
Loading u-boot.bin from mmc


U-Boot 2011.06-00132-g74fac70 (Jul 21 2011 - 01:27:22)

OMAP3530-GP ES3.1, CPU-OPP2, L3-165MHz, Max CPU Clock 720 mHz
OMAP3 Beagle board + LPDDR/NAND
I2C: ready
DRAM: 256 MiB
NAND: 256 MiB
MMC: OMAP SD/MMC: 0
*** Warning - bad CRC, using default environment

In: serial
Out: serial
Err: serial
Beagle Rev C4
timed out in wait_for_pin: I2C_STAT=0
I2C read: I/O error
Unrecognized expansion board: 0
Die ID #0bea0004000000000403a38103023017
Hit any key to stop autoboot: 0
SD/MMC found on device 0
reading uEnv.txt

** Unable to read "uEnv.txt" from mmc 0:1 **
reading uImage

3213120 bytes read
Booting from mmc ...
## Booting kernel from Legacy Image at 82000000 ...
Image Name: Linux-2.6.32
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 3213056 Bytes = 3.1 MiB
Load Address: 80008000
Entry Point: 80008000
Verifying Checksum ... OK
Loading Kernel Image ... OK
OK

Starting kernel ...

Uncompressing Linux.................................................................................................................................................................................................................. done, booting the kernel.
[ 0.000000] Linux version 2.6.32 (koen@dominion) (gcc version 4.3.3 (GCC) ) #2 PREEMPT Tue May 10 10:02:30 CEST 2011

beagleboard を触ろう - x-loader ビルド [組み込みソフト]

それでは、ブートローダーをビルドしてみましょう。
beagleboard のブートローダーは、次のように3段階に分かれています。

第1段        第2段      第3段
Boot ROM ⇒ x-loader ⇒ u-boot

Boot ROM は、OMAP3530 の中にある内臓 ROM に収められているコードです。
どのような処理を行っているか知ることができないばかりか、書き換えることもできません。
リセットベクターのジャンプ先が Boot ROM です。
Boot ROM は、必ず最初に実行されます。

x-loader, u-boot は、両方ともソースコードが公開されているので、入手してビルドすることができます。
x-loader の役割は u-boot をロードすること、u-boot の役割は Linux をロードすることです。

x-loader の役割は u-boot をロードすることだけ?
x-loader 要らなくない?
Boot ROM が、x-loader をすっ飛ばして u-boot をロードした方がいいのでは?

と思えてしまいますが、そうはいきません。
Boot ROM は SDRAM を初期化しないので、SRAM しか使えないんです。
ところが、SRAM のサイズは 64KB しかありません。
u-boot のサイズは 280KB ほどありますので、SRAM に収まり切りません。

そこで、サイズの小さな x-loader が間に挟まれます。
x-loader のサイズは 22KB ほどなので、SRAM に収まります。
x-loader は SDRAM を初期化して、SDRAM に u-boot をロードします。
カーネルのロードは u-boot に任せます。

u-boot に任せる必要あるのか?
カーネルのロードを x-loader がやってしまえばいいのでは?

u-boot は tftp が使えたり、NAND の読み書きができたり、カーネルにパラメータ渡したりと、いろいろなサービスを提供しています。
必要最低限のサービスしか要らないというのであれば、x-loader を拡張して Linux をいきなりブートさせることは可能だと思います。


さて、Boot ROM の所在は 内臓 ROM でしたが、x-loader, u-boot は、SD カードもしくは NAND flash ROM に配置します。
x-loader は SD カード、u-boot は NAND flash(またはその逆)のような構成を取ることもできるはずですが、x-loader, u-boot とも書き込みのしやすい SD カードに置くことにしましょう。

SD カードに置く場合、x-loader のイメージは MLO、u-boot のイメージは u-boot.bin という名前でないといけません。

ソースコードビルドは、http://elinux.org/BeagleBoard#Source を参照します。
そこのリンクから http://gitorious.org/beagleboard-validation/ に行ってみましょう。
x-loader, u-boot, linux のソースコードが取得できそうですね。
x-loader だと、git://gitorious.org/beagleboard-validation/x-load.git という URL が記載されています。

git:// からソースコードを取得するには、git というツールを使います。
git は、Linux 版はもちろんありますし、Windows cygwin 版もあります。
これを使って、↓のようにソース取得します。

$ git clone git://gitorious.org/beagleboard-validation/x-load.git xloader↓

ローカルに xloader ディレクトリが作成され、xloader 以下にソースコードが取得されます。
http://elinux.org/BeagleBoard#Source を見ると、distclean して config して make すればいいと書いてあるので、この通りにします。

$ cd xloader↓
$ make distclean↓
$ make omap3530beagle_config↓
$ make↓

これを行うと、おお、さくっとビルドされますね。
xloader ディレクトリに x-load とか、x-load.bin とかが生成されます。

x-load は、ELF 実行形式ファイルです。
コード+データに、ELF ヘッダー、プログラムヘッダーなど、プログラムロードの際に必要な情報が付加されたものが ELF 実行形式ファイルです。
Linux 上における実行形式ファイルは ELF 形式ですね。
Linux カーネルは、ELF ヘッダー、プログラムヘッダーを解釈して適切にロードしてくれます。

x-loader のようなブートローダーは、カーネルがロードして実行してくれるわけではないので、ELF である必要はありません。
というより、前段ブートローダーである Boot ROM は、x-loader をアドレス 0x40200800 にロードして、その先頭である 0x40200800 にジャンプしてくるので、先頭に ELF ヘッダーがある ELF 実行形式ではうまくいきません。

ELF 実行形式から、objcopy を使って余分な情報を取り除いたのが x-load.bin になります。
BSS 領域の展開も行われるはずですね。
下の図では、BSS が展開されてデータ領域が少し大きくなった様子にしてみました。

x-loader_format.jpg

x-load.bin は、ファイル先頭からコードが配置される構造になっていて、ファイル先頭がエントリポイントになっています。
なので、Boot ROM が x-load.bin を 0x40200800 にロードして、0x40200800 にジャンプしてくると、ちょうど都合よく x-loader のエントリポイントから処理が開始されることになります。

SD ブートさせる場合、SD カードの中には MLO という名前で保存する必要があります。
これは、Boot ROM が SD カードから x-loader をロードする際には、x-load.bin という名前ではなく、MLO という名前でファイルを読み出すように出来ているからです。
MLO という名前は決め打ちになっており、変えることができません。

なお、MLO = x-load.bin ではありません。
x-load.bin の先頭に 8バイトのヘッダーを付加したものが、MLO になります。
この 8バイトヘッダーを付加するには、singGP というツールを使います。

http://beagleboard.googlecode.com/files/signGP.c

からソースコードを入手します。
普通にビルドしましょう。

$ gcc -o signGP signGP.c↓

のような感じです。
x-load.bin を signGP にかませると、x-load.bin.ift なるファイルが出来上がります。

$ ./signGP x-load.bin↓

x-load.bin.ift が、x-load.bin に 8バイトヘッダーを付加したファイルです。
x-load.bin.ift を、MLO という名前にリネームし、SD カードにコピーします。
SD カードは先頭 64MB が FAT32、残りが ext3 になっていますが、FAT32 の方にコピーです。

それではこの MLO でブートしてみましょう。
SD ブートさせるには、beagleboard に搭載されている 2つのボタンのうち、USER ボタンを押下しておく必要があります。
SD カードを刺して USER ボタンを押しながら、5V 電源オンです。


Texas Instruments X-Loader 1.4.4ss (Jul 17 2011 - 07:57:49)
Beagle Rev C4
Reading boot sector
Loading u-boot.bin from mmc


U-Boot 2011.03-rc1-00000-g5ace684-dirty (May 15 2011 - 18:50:56)

OMAP3530-GP ES3.1, CPU-OPP2, L3-165MHz, Max CPU Clock 720 mHz
OMAP3 Beagle board + LPDDR/NAND
I2C: ready
DRAM: 256 MiB
NAND: 256 MiB
MMC: OMAP SD/MMC: 0
*** Warning - bad CRC, using default environment

In: serial
Out: serial
Err: serial
Beagle Rev C4
timed out in wait_for_pin: I2C_STAT=0
No EEPROM on expansion board
Die ID #0bea0004000000000403a38103023017
Hit any key to stop autoboot: 2


めでたく次段ブートローダーである u-boot がブートできていますね。
x-loader のビルドは成功です。


ここで、MLO と x-load.bin の先頭を比べてみましょう。

x-loader_head.png

MLO の先頭には、0x58 0x5a 0x00 0x00 0x00 0x08 0x20 0x40 の 8バイトが付加されていることが分かります。
MLO の 8バイト目以降は、x-load.bin と同じですね。
MLO の先頭 8バイトは、4バイトのファイルサイズ、4バイトのロードアドレスというフォーマットです。

ファイルサイズ:0x5a58 (= 23128)
ロードアドレス:0x40200800

ファイルサイズは、x-load.bin のファイルサイズですね。
Boot ROM は、この情報を見て、x-loader をアドレス 0x40200800 から 23128 バイト分 SRAM にロードして、アドレス 0x40200800 にジャンプしていると思われます。

試しに、この 0x40200800 を違うアドレスに変えてみましょう。

変更できるアドレスは、SRAM のアドレスに限ります。
アドレス範囲は、0x40200000 - 0x4020FFFF の 64KB です。

ここでは、ロードアドレスを 0x40201000 に変更してみます。
そのために、2つの作業を行う必要があります。

・1つ目は、x-loader をビルドし直して開始アドレスを 0x40201000 に変えること
・2つ目は、MLO のヘッダー内のロードアドレスを 0x40201000 に変えること

まず 1つ目です。
x-loader は、デフォルトでは 0x40200800 が開始アドレスとなるようにビルドされます。
ビルドの様子を見てみると、最後のリンクで次のようなメッセージが出ています。


arm-none-linux-gnueabi-ld -Bstatic -T /home/beagle/beagleboard/xloader/board/omap3530beagle/x-load.lds -Ttext 0x40200800 ・・・


-Ttext 0x40200800 が、開始アドレスを決定しているところですね。
board/omap3530beagle/config.mk に TEXT_BASE = 0x40200800 とありますので、これを変更します。
TEXT_BASE = 0x40201000 に変更して、make clean して再ビルドしてみましょう。

arm-none-linux-gnueabi-ld -Bstatic -T /home/beagle/beagleboard/xloader/board/omap3530beagle/x-load.lds -Ttext 0x40201000 ・・・

リンクメッセージを見てみると、0x40200800 から 0x40201000 に変わっています。

次に 2つ目の作業です。
signGP.c を見てみると、ヘッダーのロードアドレスを変更するには、第3引数にロードアドレスを指定すればよいことが分かります。

$ ./signGP x-load.bin 0x40201000↓

とやると、ヘッダーには先頭アドレス 0x40201000 が書かれた x-load.bin.ift ができあがります。
一応先頭部分を確認してみましょう。

x-loader_head2.png

4バイト目のところが 0x00 0x10 0x20 0x40 になっているので、0x40201000 ですね。

これで準備 OK です。
x-load.bin.ift を MLO にリネームして、SD カードにコピーしましょう。
そして、SD カードを beagleboard に刺し、USER ボタンを押しながら、5V 電源オンです。

Texas Instruments X-Loader 1.4.4ss (Jul 17 2011 - 21:56:54)
Beagle Rev C4
Reading boot sector
Loading u-boot.bin from mmc


U-Boot 2011.03-rc1-00000-g5ace684-dirty (May 15 2011 - 18:50:56)


u-boot が普通にブートします。
OK です。

・・・

だから何?という気もしますが、まあそれはそれで・・

beagleboard を触ろう - 開発環境のインストール [組み込みソフト]

既存のバイナリをロード・実行することはできたので、次は自分でブートローダーと Linux カーネルをビルドしてみましょう。

その前に、まずは開発環境をインストールします。
GNU のツールチェーンを入れましょう。
x86-PC 上で動作し、ARM コードを生成するクロス開発環境を入れる必要があります。
クロス環境をソースコードからビルドして構築する手もありますが、CodeSourcery という非常にありがたいバイナリパッケージがありますので、それを利用します。

google で codesourcery で検索すると、CodeSourcery の ARM 用ページ

http://www.codesourcery.com/sgpp/lite/arm

が引っ掛かります。
ここから上の方にある DOWNLOADS をクリックして

http://www.codesourcery.com/sgpp/lite/arm/portal/subscription?@template=lite

に行き、Target OS が GNU/Linux の Sourcery G++ Lite をクリックすると、

http://www.codesourcery.com/sgpp/lite/arm/portal/release1803

に行きます。
ここに Linux 用と Windows 用のバイナリが、それぞれインストーラ版と tar-ball 版とであります。
ソースコードの tar-ball もありますね。
私は Linux のインストーラ版をダウンロードしました。

それではこれを使ってインストールしましょう。
root 権限でインストールしなくても大丈夫です。

インストーラを実行するといろいろ聞かれますが、私は基本的にデフォルトでやりました。即ち、ひたすら Enter とか Y とかです。
インストールディレクトリだけ、デフォルトの CodeSourcery/Sourcery_G++_Lite から CodeSourcery/Sourcery_G++_Lite_GNU へと変えました。

そうすると、自分のホームディレクトリに

CodeSourcery/Sourcery_G++_Lite_GNU

というディレクトリが作られ、そこにインストールされます。
必要なディスク容量は、364,719,214 bytes でした。

インストールした後、パスを通す作業は自分でやる必要があります。(確か)
~/.bashrc などに記述してパスを通しましょう。

PATH=$PATH:~/CodeSourcery/Sourcery_G++_Lite_GNU/bin

これで ARM 用のクロスビルドツールチェーン arm-none-linux-gnueabi-xxx が使えるようになります。
これにより、ブートローダー、Linux のビルドが可能です。
ついでに、Linux 上で動作する実行形式ファイルのビルドもできます。
一応確認しておきましょう。

test.c
#include <stdio.h>
int main(void)
{
  printf("hello, world\n");
}

$ arm-none-linux-gnueabi-gcc test.c↓


で a.out が出来上がります。
OK ですね。

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。