Edited at

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を追って実験すれば実装できるかもしれません。