ラベル Raspberry Pi の投稿を表示しています。 すべての投稿を表示
ラベル Raspberry Pi の投稿を表示しています。 すべての投稿を表示

2017/02/19

AArch64 armasm の勉強 システムレジスタの値を取得する実習

前々回前回で、システムレジスタについてと、 C 言語から armasm のサブルーチンを呼び出す方法が分かった気がする。 なので、実際にプログラムを作成してレジスタの値が取得できるかを確かめる。

前提

この辺の作業で使った環境を引き続き使用している。

作成したプログラム

main.c

#include <string.h>
#include <types.h>
#include <uart.h>

#include "SystemRegisters.h"

#define BUF_LENGTH 255

int main() {

    char cbuf[BUF_LENGTH];

    uint32 daifFlags = daif();
    ltoa(daifFlags, cbuf, BUF_LENGTH, 2);
    print("daifFlags = ");
    println(cbuf);

    uint32 nzcvFlags = nzcv();
    ltoa(nzcvFlags, cbuf, BUF_LENGTH, 2);
    print("nzcvFlags = ");
    println(cbuf);

    while (1);

    return 0;
}

SystemRegisters.h

#include <types.h>

extern uint32 daif();
extern uint32 nzcv();

SystemRegisters.S

.global daif
daif:
    MRS X0, DAIF
    RET
.global nzcv
nzcv:
    MRS X0, NZCV
    RET

とりあえずこれで確認してみる。

実行結果は下記。

daifFlags = 1111000000
nzcvFlags = 1100000000000000000000000000000

printf を実装してないから表示がちょっと残念だけど、 下記、条件フラグの記載を見る限り、 DAIF フラグが全部立ってるので、割り込みはすべて Disable であることと、 Z, C フラグが立ってることを確認できた???

とりあえず今日はここまで。 明日はこれでいろいろなレジスタの状態を確認していってみようか。

2017/02/16

AArch64 armasm の勉強 サブルーチン編

前回(親知らずの日記: AArch64 armasm の勉強 システムレジスタ編)、システムレジスタの値を MRS, MSR で読み書きできそうということが分かったので、取得した値を表示してみたい。

そのためには、 C 言語から armasm のサブルーチンを呼び出せるようにしないといけない。

そのあたりの勉強をする。

座学

ARM Information Center - ARM® コンパイラ armasm ユーザガイド を見てみたが、 AArch64 の章に、サブルーチンに関する話が載っていなかった。

仕方がないので ARM Information Center - 6.3 サブルーチン呼び出しでのレジスタの使用方法 を参照する。

これによると、

  • レジスタ 0 から 3 までを引数用に使用
  • レジスタ 0 を 戻り値用に使用
  • サブルーチン呼び出しには BL 命令を使用
  • サブルーチンからの復帰には BX LR 命令を使用

のようだ。

それぞれの命令の意味を確認しようと思ったら、 ARM Information CenterBX 命令がない...。「復帰」で検索かけたら RET 命令が見つかった。AArch64 では RET 命令でサブルーチンからの復帰を行うみたい。

ここまでわかったので、実際作ったプログラムをアセンブリプログラムに変換して確かめてみる。

実践

検証環境

ツールチェインは gcc-linaro-6.2.1-2016.11-i686-mingw32_aarch64-linux-gnu

検証用プログラムの作成

caller.c

extern int getOne(void);
extern int add(int a, int b);

int main() {

    int one = getOne();

    int number = add(1, 2);

    while (1);

    return 0;
}

callee.c

int getOne() {
    return 1;
}

int add(int a, int b) {
    return a + b;
}

上記の C コードをアセンブリコードへ変換する。

アセンブリコードの確認

aarch64-linux-gnu-gcc -S ./caller.c -o caller.S
aarch64-linux-gnu-gcc -S ./callee.c -o callee.S

で、結果が下記。

caller.S

    .arch armv8-a
    .file   "caller.c"
    .text
    .align  2
    .global main
    .type   main, %function
main:
    stp x29, x30, [sp, -32]!
    add x29, sp, 0
    bl  getOne
    str w0, [x29, 28]
    mov w1, 2
    mov w0, 1
    bl  add
    str w0, [x29, 24]
.L2:
    b   .L2
    .size   main, .-main
    .ident  "GCC: (Linaro GCC 6.2-2016.11) 6.2.1 20161016"
    .section    .note.GNU-stack,"",@progbits

callee.S

    .arch armv8-a
    .file   "callee.c"
    .text
    .align  2
    .global getOne
    .type   getOne, %function
getOne:
    mov w0, 1
    ret
    .size   getOne, .-getOne
    .align  2
    .global add
    .type   add, %function
add:
    sub sp, sp, #16
    str w0, [sp, 12]
    str w1, [sp, 8]
    ldr w1, [sp, 12]
    ldr w0, [sp, 8]
    add w0, w1, w0
    add sp, sp, 16
    ret
    .size   add, .-add
    .ident  "GCC: (Linaro GCC 6.2-2016.11) 6.2.1 20161016"
    .section    .note.GNU-stack,"",@progbits

まとめ

ドキュメントに書いてある通り、引数と戻り値を設定しているみたい。 そして、復帰は RET 使っている。

サブルーチン側では、

  1. スタック領域確保する
  2. スタックに引数を積み上げる
  3. サブルーチンの処理結果をレジスタ 0 に格納する
  4. 確保したスタック領域を返却する

のような流れっぽい。 スタックは、アドレスの少ないほうに伸びていくっぽい。

本日はここまで。 明日、余裕があったら引数の数増やして確認してみる。

2017/02/15

AArch64 armasm の勉強 システムレジスタ編

BareMetalで遊ぶ Raspberry Pi - 達人出版会 を参照しながら Raspberry Pi 3 Model B+ でベアメタルタイマー割り込みやろうとしたのだけど、 AArch64 でやろうとするといろいろ勝手が違うらしいのでそこのところを勉強する。

元ネタ資料はこれら

レジスタ概要を把握するため、ARM Information Center - 章 4 AArch64 状態の概要 を一通り読んだ。

不正確な箇条書きで書くとこんな感じらしい。

  • レジスタ概要
    • 汎用レジスタ 30 本
      • プログラム内で好き勝手適当に使えるレジスタ群
      • X30 で 64 bit レジスタとしてアクセス
      • W30 で 32 bit レジスタとしてアクセス(上 32 bit はゼロクリアされる)
      • X30, W30 はリンクレジスタ
    • PC(プログラムカウンタ)
      • 次に実行するアドレスが格納されるレジスタ
      • 直接値を変更することはできない、特定の命令で書き換えるらしい
    • SP(スタックポインタレジスタ)
      • スタックを実現するために使うレジスタ
      • 命令一つで格納とインクリメントをしてくれるとかそういうの???
      • サブルーチンの引数なんかをスタックするのに使うらしい
      • 例外レベル 0 から 3 までにそれぞれ専用のレジスタをもつ
      • アセンブラの命令によっては X31, W31 でアクセスできるらしい
    • SPSR(セーブドプログラムステータスレジスタ)
      • 例外発生時にその時点での状態を記録してくれるレジスタ
      • 例外レベル 1 から 3 までに専用のレジスタが一つずつある
      • 例外が発生すると、現在のステータスが遷移先の SPSR に格納される
      • SPSR が格納する情報は下記の通り
        • N, Z, C, V フラグ
        • D, A, I, F 割り込み Disable ビット
        • レジスタ幅
        • 実行モード
        • IL, SS ビット
    • LR(リンクレジスタ)
      • サブルーチン呼び出し時に復帰アドレスを格納する
    • ELR(例外リンクレジスタ)
      • 例外発生時に復帰アドレスを格納する
      • 例外レベル 1 から 3 にそれぞれ専用のレジスタがある
        • 設定によっては例外レベルでレジスタを使い分けないこともできるらしい
  • 例外レベルとは
    • それぞれのレベルで下記のような感じらしい。それぞれの意味はよく分からない。
      • EL0 : アプリケーション
      • EL1 : 特権付き
      • EL2 : ハイパーバイザ
      • EL3 : セキュアモニタ
  • AArch64 でのプロセス状態書き込み・読み込み方法
    • プロセス状態としてアクセスできる情報は下記の通り
      • N,Z,C,V フラグ(NZCV)
      • 現在のレジスタ幅(nRW)
      • スタックポインタ選択ビット(SPSel)
      • 割り込み Disable フラグ(DAIF)
      • 現在の例外レベル(EL)
      • シングルステッププロセス状態ビット(SS)
      • 不正な例外復帰状態ビット(IL)
    • MSR 命令で書き込めるもの
      • NZCV
      • DAIF
      • SPSel(EL1以上)
    • MRS 命令で読み取れるもの
      • NZCV
      • DAIF
      • CurrentEL の例外レベルビット(EL1 以上)
      • SPSel(EL1 以上)

BareMetalで遊ぶ Raspberry Pi - 達人出版会 内で CPSR にアクセスしている部分は、大体 MSR, MRS で置き換えられるのかな?

ARM Information Center - ARM Cortex-A53 MPCore Processor Technical Reference Manual に書かれているレジスタなんかも、 MSR, MRS 命令で読み書きするみたい。

とりあえず今日はここまで。

2017/01/22

Windows 上で Raspberry Pi 3 Model B+ 64 bit の U-Boot イメージをビルド

Windows 上で Raspberry Pi 3 Model B+ の U-Boot イメージをビルドする手順を書いていく。

ただし、 U-Boot イメージのビルドには Bash on Ubuntu on Windows を使用する。

前提条件

U-Boot の準備

ネットワークブートに必要な U-Boot のビルドと設定を行う。 MSYS2 上でのビルドに成功しないので、この作業は Bash on Ubuntu on Windows 上で行う。 このセクションの作業はすべて Bash on Ubuntu on Windows 上で行う。

必要パッケージのインストール

sudo apt install git gcc-aarch64-linux-gnu device-tree-compiler

U-Boot の取得とビルド

mkdir ~/project
cd ~/project
git clone git://git.denx.de/u-boot.git
cd ~/project/u-boot
export CROSS_COMPILE=aarch64-linux-gnu-
make rpi_3_defconfig
make all

ブートスクリプトの作成

Raspberry Pi 起動時に、 TFTP サーバーへ接続してイメージのロードと実行を行うスクリプトを作成する。

boot.txt を、下記の通り作成する。

dhcp 0x80000 192.168.2.100:kernel8.img
go 0x80000

boot.txt を、 boot.scr に変換する。

/PATH/TO/u-boot/tools/mkimage -A arm -O linux -T script -C none -a 0 -e 0 -n "PXE Boot" -d boot.txt ./boot.scr

ここで作成した boot.scr を、 U-Boot 起動時に読み込ませることで、 TFTP サーバー 192.168.2.100kernel8.img をイメージとして取得・ロードしてくれるようになる。

ネットワーク設定

ノート PC の無線 LAN を WAN に、有線を Raspberry Pi 3 に接続する。 そのままだと、有線が優先されるせいか WAN に出れないので、有線 LAN アダプタの設定を行う。

  1. 「タスクトレイのネットワークアイコンを右クリック -> ネットワーク共有センターを開く」選択
  2. 「アダプターの設定の変更」を選択
  3. 「イーサネットを右クリック -> プロパティ」を選択
  4. 「インターネットプロトコル バージョン 4(TCP/IPv4)」をダブルクリック
  5. 「次の IP アドレスを使う」を選択
  6. 下記通りに設定
    • IP アドレス : 192.168.2.100
    • サブネットマスク : 255.255.255.0
    • デフォルトゲートウェイ : 192.168.2.1
    • 他は空欄のままでよい

SD カードの準備

  1. ビルドした成果物の u-boot.bin を SD カードのルートにコピーする
  2. 下記ページのファイルをすべて SD カードのルートにコピーする。
  3. boot.scr を SD カードのルートにコピーする
  4. config.txt を書き換える

config.txt の書き換え後の内容

enable_uart=1
arm_control=0x200
kernel=u-boot.bin

TFTPD の準備

TFTPD の取得

下記ページから tftpd64 standard edition (zip) をダウンロードする。

TFTPD の設定

ダウンロード・展開してできた tftpd64.exe を起動し、 Setting ボタンを押下。

グローバル設定画面で、 TFTP Server と DHCP Server を使用するように設定。

TFTPD 設定画面でいろいろ設定する。

  • Base Directory
    • TFTP で公開するディレクトリのルート( kernel8.img を置く。今回は C:\\Users\\mikoto\\project\\raspberrypi_bare_metal\\ftproot を設定)
  • 下記チェックボックスにチェック
    • Option negotiation
    • Show Progress bar
    • Translate Unix file names
    • Bind TFTP to this address
      • 192.168.2.100 (自分が Listen する IP アドレスを入力)

DHCPD 設定画面でもいろいろ設定する。

  • この辺直感で設定したので根拠なし。誰かツッコミあればお願いしますって感じで。

OK ボタンを押下。

これで、TFTP のファイルを公開すると同時に、 PC と Raspberry Pi を繋げたときに、 Raspberry Pi に対して IP を設定してくれるようになる。

物理的な準備

  1. SD カードを Raspberry Pi に挿入
  2. PC と Raspberry Pi を LAN ケーブルで繋げる
  3. PC と Raspberry Pi を UART ケーブルでつなげる

ファイルの準備

実行したいプログラム( kernel8.img ) を TFTP のルートディレクトリにコピー

実行

Raspberry Pi の電源を入れる

Raspberry Pi を再起動するたび、 TFTP のルートディレクトリに格納した kernel8.img が実行されるようになっている。

感想

  • U-Boot, 積極的に使っていきたい。
  • とりあえず目的は達成できたので満足。
  • U-Boot のビルドに Ubuntu を使ったのが悔しくて仕方ない。

参考文献

以上。

2016/12/25

Raspberry Pi Model B+ でベアメタル UART 出力ユーティリティ化

前回、 親知らずの日記: Raspberry Pi 3 Model B+ でベアメタル UART 出力 では、 UART で受信した文字をエコーバックするプログラムを作成した。

プログラム内で任意の文字列を出力させるため、ユーティリティ化したい。

目的

デバッグ目的で UART に任意文字列を出力したい。

こんな感じで使いたい。

int main(void) {

    println("Hello, World!");

    error_code = function();
    print("error_code: ");
    print(error_code);
    println(".");

    return 0;
}

要素技術

  1. UART 出力
  2. '数値 -> 文字列' 変換

UART 出力

こんな感じになった。

uart.h

#ifndef __UART_H__
#define __UART_H__

/**
 * Mini Uart 送受信に使用するレジスタ。
 *
 * 書き込むと送信 FIFO にプッシュ,
 * 読み込むと 受信 FIFO からポップしてくれる素敵仕様らしい。
 */
#define MU_IO (*(volatile unsigned int *)0x3F215040)

/**
 * Mini Uart 送信 FIFO の状態確認をするための情報が入ったレジスタ。
 *
 * 6 ビット目 : Transmitter idle, アイドル状態か?
 *              1: アイドル状態, 0: ビジー状態
 * 5 ビット目 : Transmitter empty, 1 バイト以上送信受付可能か?
 *              1: 可能, 0: 不可能
 * 0 ビット目 : Data ready, 1 バイト以上受信しているか?
 *              1: 受信している, 0: 受信していない
 */
#define MU_LSR          (*(volatile unsigned int *)0X3F215054)

/* Transmitter idle のビットマスク */
#define MU_LSR_TX_IDLE  (1U << 6)

/* Transmitter empty のビットマスク */
#define MU_LSR_TX_EMPTY (1U << 5)

/* Data ready のビットマスク*/
#define MU_LSR_RX_RDY   (1U << 0)

void println(const char* chars);
void print(const char* chars);
void uart_put(const char c);

#endif // __UART_H__

uart.c

#include "uart.h"

/* あとで types.h に追い出す */
typedef unsigned int size_t;

void println(const char* chars) {
    print(chars);
    print("\n\r");
}

void print(const char* chars) {
    char* char_ptr = (char*) chars;

    while (*char_ptr != '\0') {
        uart_put(*char_ptr);
        char_ptr++;
    }
}

void uart_put(const char c) {
    // 送信受付可能状態になるまでビジーループ
    while (!(MU_LSR & MU_LSR_TX_IDLE) && !(MU_LSR & MU_LSR_TX_EMPTY));

    // IO レジスタにデータ書き込み
    MU_IO = c;
}

'数値 -> 文字列' 変換

これはこんな感じ。

string.h

#ifndef __STRING_H__
#define __STRING_H__

unsigned char ltoa_10(const long l, char* str, const int max_length);

#endif // __STRING_H__

string.c

#include "string.h"

/**
 * @fn 10 進数値を文字列へ変換する
 * @param (l)          変換対象数値
 * @param (str)        変換後の文字列を格納するバッファへのポインタ
 * @param (max_length) 変換後の文字列を格納するバッファの長さ
 * @return エラーコード
 *         0 : 正常終了
 *         1 : 変換後文字列を格納するバッファの長さが足りなかった
 */
unsigned char ltoa_10(const long l, char* str, const int max_length) {
    unsigned int  i;
    unsigned long tmp_long_value = l < 0 ? -l : l;
    unsigned char tmp_digit_value;
    unsigned int  length = 0;

    // 桁の数値と文字を対応付けする配列
    const char cmap[]
            = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };

    // ゼロ判定, ゼロならさっさと返却する
    if (l == 0) {
        if (max_length < 2) {
            return 1;
        }

        str[0] = '0';
        str[1] = '\0';

        return 0;
    }

    // 桁数判定
    while (tmp_long_value != 0) {
        tmp_long_value = tmp_long_value / 10;
        length++;
    }
    if (l < 0) {
        length++;
    }

    // バッファ長上限判定(NULL 文字を含めるので +1 する)
    // 上限を超えていたら 1 を返却
    if (length + 1 > max_length) {
        return 1;
    }

    // 文字列変換

    // 文字列変換開始インデックス
    i = length - 1;

    // NULL 文字追加
    str[length] = '\0';

    // 各桁の変換
    tmp_long_value = l < 0 ? -l : l;
    while (tmp_long_value != 0) {
        tmp_digit_value = tmp_long_value % 10;
        str[i] = cmap[tmp_digit_value];
        i--;
        tmp_long_value = tmp_long_value / 10;
    }

    // 負数処理
    if (l < 0) {
        str[0] = '-';
    }

    return 0;
}

使い方

こんな感じで使えるようになった。

#include <string.h>
#include <uart.h>

#define BUF_LENGTH 5

int main(void) {

    char cbuf[BUF_LENGTH];

    println("Hello, World!");

    error_code = function();
    ltoa_10(error_code, cbuf, BUF_LENGTH);

    print("error_code: ");
    print(error_code);
    println(".");

    return 0;
}

成果物

この辺りをユーティリティとしてまとめたうえで、 gcc で挙動テスト(string.c のみ)するようにしたものが下記 URL にある。

これでデバッグプリントできるようになったので、 HDMI 出力できない問題を追いかけていく。 (しかし SD カード抜き差しが非常に厳しい...)

以上。

2016/12/24

Raspberry Pi 3 Model B+ でベアメタル UART 出力

前回に引き続き、今回も下記サイトを参考にベアメタルプログラミングをしていく。

今回は UART でのデータ送受信。

参考ページは下記。

必要なハードウェアの調達

UART するには通信ケーブルが必要なので調達した。

ドライバは下記ページを参考にした。

UART を使用するための設定

config.txt を作成し、 enable_uart=1 を記述。 あとで SD カードのルートに格納する。

この辺は今のところ「おまじない」としておく。

このおまじないをすると、 GPU が Mini UART を使えるようにしてくれるようになるらしい。 そのため、我々は、 UART の初期化コードを書くことなくプログラミングできるっぽい。

UART を使うためのレジスタの確認

使うレジスタ

BCM-ARM-Peripherals.pdf の 8 ページ目から UART のレジスタについて説明されている。

今回使うのは、 AUX_MU_LSR_REG(Mini Uart Line Status)AUX_MU_IO_REG(Mini Uart I/O Data)

Raspberry Pi 3 なので、上位アドレスを読みかえ、それぞれ 0x3F215054, 0x3F215040 となる。

ステータスレジスタについて

AUX_MU_LSR については、BCM-ARM-Peripherals.pdf の 15 ページ目に説明が書いてあるみたい。

プログラム作成

/**
 * Mini Uart 送受信に使用するレジスタ。
 *
 * 書き込むと送信 FIFO にプッシュ,
 * 読み込むと 受信 FIFO からポップしてくれる素敵仕様らしい。
 */
#define MU_IO (*(volatile unsigned int *)0x3F215040)

/**
 * Mini Uart 送信 FIFO の状態確認をするための情報が入ったレジスタ。
 *
 * 6 ビット目 : Transmitter idle, アイドル状態か?
 *              1: アイドル状態, 0: ビジー状態
 * 5 ビット目 : Transmitter empty, 1 バイト以上送信受付可能か?
 *              1: 可能, 0: 不可能
 * 0 ビット目 : Data ready, 1 バイト以上受信しているか?
 *              1: 受信している, 0: 受信していない
 */
#define MU_LSR          (*(volatile unsigned int *)0X3F215054)
#define MU_LSR_TX_IDLE  (1U << 6) /* Transmitter idle のビットマスク */
#define MU_LSR_TX_EMPTY (1U << 5) /* Transmitter empty のビットマスク */
#define MU_LSR_RX_RDY   (1U << 0) /* Data ready のビットマスク*/

int main(void) {

    // 受信文字列格納用
    volatile char ch;

    while (1) {
        // 受信するまでビジーループ
        while (!(MU_LSR & MU_LSR_RX_RDY));

        // 受信データ記録
        ch = (char)MU_IO;

        // 送信受付可能状態になるまでビジーループ
        while (!(MU_LSR & MU_LSR_TX_IDLE) && !(MU_LSR & MU_LSR_TX_EMPTY));

        // IO レジスタにデータ書き込み
        MU_IO = (unsigned int)ch;
    }

    return 0;
}

配線

元ネタのページにも記載されているが、ピン番号はこちらで確認した。

まじめな人はこれと BCM2835-ARM-Peripherals.pdf を見比べて、 仕様書の見方に慣れていくのが良いかと。 (多分 102 ページ目からの説明とか 176 ページ目からの説明とかが対応する記述なのだと思う。)

購入した変換ケーブルの説明を見ると、下記のようになっている。

ブラックケーブル ----- GND
グリーンケーブル ----- TXD
ホワイトケーブル ----- RXD
レッドケーブル ------- VCC - 5V

というわけで、下記感じで配線する。

PIN  6(GND) ----- ブラック(GND)
PIN  8(GPIO14) -- ホワイト(RXD)
PIN 10(GPIO15) -- グリーン(TXD)

動作確認

  1. SD カードに bootcode.bin, start.elf, config.txt, kernel8.img をコピーして、Raspberry Pi に挿す
  2. PC に変換ケーブルを挿す
  3. シリアルコンソールを起動し、 Com の設定を行う(後述)
  4. Raspberry Pi に電源供給
  5. 入力文字がエコーバックされれば OK

シリアルコンソールについて

今回は、 RLogin を使用することとした。 sixel 関係で使っていて、使い慣れていたので。

設定は、下記のようにすれば OK のはず。

  • ビット/秒 : 115200
  • データビット : 8
  • パリティ : なし
  • ストップビット : 1
  • フロー制御 : ハードウェア

以上。

2016/12/19

Raspberry Pi 3 Model B+ で、 OS 無し L チカした

Raspberry Pi でベアメタルプログラミンしたかったので、 いろいろ調べた作業記録を残す。

参考資料を探す

とりあえず、自分のレベルに合っていて、 かつ、わかりやすそうなサイトを探す。

...で、見つかったのがこのサイト。

基本ここを参考に環境構築とかしていくこととする。

必要なハードウェア類の調達

L チカに必要なものを買い集める。

必要なソフトウェア類の調達

ビルド環境構築

ダウンロードした gcc-linaro-6.2.1-2016.11-i686-mingw32_aarch64-linux-gnu.tar.xz を適当な場所に展開し、 bin ディレクトリにパスを通す。

L チカプログラムの作成

プログラムを作ってビルドする。

ファイル作成

こんな配置でファイルを作成する。

led_blink_GPIO16
├── main.c
├── Makefile
└── start.S

start.S

/* 現時点ではおまじないスタート地点を調整しているはず */
mov sp, #0x80000
bl  main

main.c

#define GPFSEL1 0x3F200004 /* GPIO のピン設定をするためのレジスタ */
#define GPSET0  0x3F20001C /* GPIO を HIGH にするためのレジスタ */
#define GPCLR0  0x3F200028 /* GPIO を LOW  にするためのレジスタ */

typedef unsigned char bool;

#define TRUE  1
#define FALSE 0

#define WAIT_COUNT 3000000

/*
 * @fn wait_count だけビジーウェイトする
 * @param (wait_count) このカウント数だけビジーウェイトする
 */
void busy_wait(int wait_count);

/*
 * @fn L チカする
 */
int main(void) {

    // GPIO 出力に設定。
    // Peripheral specification の
    // 92 ページ、Field Name が FSEL16 の行の
    // Bit(s) を見ると、18 から 20 ビット目までに
    // 0x001 を設定すればよいことがわかる感じ。
    *(volatile unsigned int*)GPFSEL1 = (1 << (18));

    // セットして待つ、クリアして待つ、を繰り返す。
    while (1) {
        *(volatile unsigned int*)GPSET0 = (1 << 16);
        busy_wait(WAIT_COUNT);
        *(volatile unsigned int*)GPCLR0 = (1 << 16);
        busy_wait(WAIT_COUNT);
    }

    return 0;
}

void busy_wait(int wait_count) {
    volatile unsigned int i;

    for (i = 0; i < WAIT_COUNT; i++);
}

Makefile

CC=aarch64-linux-gnu-gcc
LD=aarch64-linux-gnu-ld
AS=aarch64-linux-gnu-as
OBJCOPY=aarch64-linux-gnu-objcopy

OBJECTS=start.o main.o

all: build link objcopy

objcopy: kernel8.img

kernel8.img: kernel8.elf
    $(OBJCOPY) -O binary kernel8.elf kernel8.img


link: kernel8.elf

kernel8.elf: $(OBJECTS)
    $(LD) -Ttext 0x80000 -o kernel8.elf $(OBJECTS)


build: $(OBJECTS)

start.o: start.S
    $(AS) -o start.o start.S

main.o: main.c
    $(CC) -c -o main.o main.c


clean:
    rm *.o *.elf *.img

ビルド

make して kernel8.img を作成する。

$ make
aarch64-linux-gnu-as -o start.o start.S
aarch64-linux-gnu-gcc -c -o main.o main.c
aarch64-linux-gnu-ld -Ttext 0x80000 -o kernel8.elf start.o main.o
C:\Users\mikoto\app\gcc-linaro-6.2.1-2016.11-i686-mingw32_aarch64-linux-gnu\bin\aarch64-linux-gnu-ld.exe: warning: cannot find entry symbol _start; defaulting to 0000000000080000
aarch64-linux-gnu-objcopy -O binary kernel8.elf kernel8.img

リンカスクリプトを作っていないため、 警告が出ているけど、今回動かす分には問題ない。

Raspberry Pi の準備

ファイルの配置

  1. ダウンロードした firmware_armstub.zip を展開し、bootcode.bin, start.elf を SD カードのルートにコピーする
  2. ビルドした、 kernel8.img を SD カードのルートにコピーする

最終的に、 SD カードに下記感じでファイルを配置する。

SD カード
├── bootcode.bin
├── start.elf
└── kernel8.img

配線

下記感じで配線する。

PIN 36(GPIO16) ─ 抵抗 ─ LED
PIN 6(GND) ────────┘

実行

  1. Raspberry Pi に SD カードを入れる
  2. Raspberry Pi に電源供給

以上。

参考資料

この辺を見て試したはず...。ぬけもれあるかも。