Help us understand the problem. What is going on with this article?

Raspberry Pi ZeroでLチカする

More than 1 year has passed since last update.

既に世間には数多くのRaspberry Pi + Lチカの記事がたくさんありますが、GPU上でやるものが日本語記事にあまり見当たらなかったので書いてみました。

何をやるのか

GPU上で動くbootloaderのbootcode.binを作って、LEDを点滅させます。
以下を参考にしています。

開発環境

GPUであるVideoCoreIVは一般的なアーキテクチャではなく、独自の命令セットをもっています。その命令に対応するツールを準備する必要があります。今回は以下を使います。

ubuntu/debianなどはREADME.mdに従えば環境構築できると思います。
Gentoo Linuxの場合は、以下の手順に従ってinstallを行います。

guile-1.8のinstall

 # emerge guile:12/8

build

git cloneします。

 $ git clone --depath 1 --recursive https://github.com/puppeh/vc4-toolchain.git
 $ cd vc4-toolchain

Makefileの中にguileを実行する箇所があるんですが、guile-1.8もしくはguile1.8というコマンドを探して実行します。
Gentooの場合、guileのパスは/usr/bin/guileなのでsymbolic linkをはるか、以下を編集してください。

 $ vim binutils-vc4/opcodes/Makefile.in

661行目を変更

--- a/binutils-vc4/opcodes/Makefile.in       2018-08-05 14:51:17.748076392 +0900
+++ b/binutils-vc4/opcodes/Makefile.in       2018-08-05 14:51:04.248077012 +0900
@@ -608,7 +608,7 @@

 CGENDIR = @cgendir@
 CPUDIR = $(srcdir)/../cpu
-CGEN = "`if test -f ../guile/libguile/guile ; then echo ../guile/libguile/guile; else echo \`which 2> /dev/null guile{1.8,-1.8}\` ; fi` -l ${cgendir}/guile.scm -s"
+CGEN = "`if test -f ../guile/libguile/guile ; then echo ../guile/libguile/guile; else echo \`which 2> /dev/null guile{,1.8,-1.8}\` ; fi` -l ${cgendir}/guile.scm -s"
 CGENFLAGS = -v
 CGENDEPS = \
        $(CGENDIR)/desc.scm $(CGENDIR)/desc-cpu.scm \

あとはbuildするだけです。gcc,binutilsなどをbuildするので結構時間かかります。

 $ ./build-all.sh

ビルドが完了したらvc4-toolchain/prefix/binを環境変数PATHに追加します。

Lチカプログラムを作る

Makefile

TARGET  = bootcode.bin
THOST   = vc4-elf-

# flags
CFLAGS  = -nostdlib -std=c11 -msingle-float
ASFLAGS =
LDFLAGS = -nostdlib -nostartfiles --build-id=none

# tools
CC      = $(THOST)gcc
AS      = $(THOST)as
LD      = $(THOST)ld
OBJDUMP = $(THOST)objdump
OBJCOPY = $(THOST)objcopy

# intermediate files
ELF      = $(TARGET:%.bin=%.elf)
DISASMED = $(TARGET:%.bin=%.S)
AOBJS    = start.o
COBJS    = main.o
LDSCRIPT = boot.lds

.PHONY: all clean

#
# build
#

all: $(TARGET) $(DISASMED)

$(TARGET): $(ELF)
    $(OBJCOPY) $< -O binary $@

$(DISASMED): $(ELF)
    $(OBJDUMP) -D $< > $(DISASMED)

$(ELF): $(COBJS) $(AOBJS) $(LDSCRIPT)
    $(LD) $(LDFLAGS) $(AOBJS) $(COBJS) -T $(LDSCRIPT) -o $@

$(COBJS): $(COBJS:%.o=%.c)
    $(CC) $(CFLAGS) -c $< -o $@

$(AOBJS): $(AOBJS:%.o=%.s)
    $(AS) $(ASFLAGS) -c $< -o $@

#
# clean up
#

clean:
    @rm -vf $(COBJS) $(AOBJS) $(DISASMED) $(ELF) $(TARGET)

start.s

C言語のmainを起動するAssembly codeです。コードは0x200から実行されるため、.space 0x200を追加しています。

/* simply call main */
.space 0x200
.globl _start
.align 2
_start:
        /* set stack pointer */
        mov sp, #0x1c000

        /* jump to C code */
        bl main

hang:
        b hang

main.c

Raspberry Pi ZeroのLEDはGP_FSEL4を適切に設定し、GP_SET1/GP_CLR1の15bit目をONにすることでつけたりけしたりできます。

/* define hardware register */
#define HW_REGISTER_RW(addr) (*(volatile unsigned int *)(addr))

#define IO_BASE  (0x7e200000)
#define GP_FSEL4 HW_REGISTER_RW(IO_BASE + 0x10)
#define GP_SET1  HW_REGISTER_RW(IO_BASE + 0x2c)
#define GP_CLR1  HW_REGISTER_RW(IO_BASE + 0x20)

/* called by _start */
void main()
{
        /* activate LED */
        unsigned int ra = GP_FSEL4;
        ra &= ~(0x7 << 21);
        ra |=  (0x1 << 21);
        GP_FSEL4 = ra;

        for(unsigned int j = 0; ; ++j) {
                /* wait */
                for(unsigned int i = 0x8000; i != 0; --i)
                {
                        __asm__ volatile("nop");
                }

                /* led on/off */
                if((j % 2) == 0)
                {
                        GP_SET1 = (0x1 << 15);
                }
                else {
                        GP_CLR1 = (0x1 << 15);
                }
        }
}

boot.lds

linker scriptを作ります。rpi-open-firmwareを参考にしています。

SECTIONS
{
        . = 0x000;

        .text : {
                *(.text)
                *(.text.*)
                *(.gnu.warning)
        }

        .rodata : {
                *(.rodata)
                *(.rodata.*)
        }

        . = ALIGN(4096);

        .data : {
                *(.data)
                *(.data.*)
        }

        .bss : {
                *(.bss)
                *(.bss.*)
        }

        . = ALIGN(32 / 8);
}

build

 $ make

実行

出来上がったbootcode.binをmicroSDに入れて電源をつければLEDが点滅します。

参考資料

VideoCoreIVについての情報は以下が大変参考になります。

HDMIの初期化などはブラックボックスですが、Linux kernelを追って実験すれば実装できるかもしれません。

stkchp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away