HiFive1を使ったプログラムは、まだ時間が無くてLチカとUARTのテストくらいしか出来ていないが、早くもArduinoIDEに不満が出てきた。
可能ならばEmacsでプログラムを書きたいし、Makeを使ってコンパイルやダウンロードができるようになるとうれしい。
そこで、Arduinoのコンパイルフローをトレースすることで、これをMakefileに直接インポートし、ArduinoIDEを経由せずにC/C++でHiFive1用プログラムが書けるなろう。
まずは、C/C++のプログラムでUARTに書き込みを行えるようになるところからかな。
ArduinoIDEのコンパイルシーケンス
ArduinoIDEを使った場合の、HiFive1用プログラムのコンパイルシーケンスは過去の記事で確認した。
msyksphinz.hatenablog.com
UARTへのprintは結局何が起きているのか
これはHiFive1以外で同じことが言えるだろうが、UARTの書き込みは主に2つのシーケンスで成り立っている。
Serial.begin(baud)
UARTの初期化とボーレートの設定
Serial.write(char)
UARTへの書き込み
これらのプログラムは、結局以下のC++のコードで記述されている。
UARTClass::begin(unsigned long bauds)
{
GPIO_REG(GPIO_OUTPUT_XOR)&= ~(IOF0_UART0_MASK);
GPIO_REG(GPIO_IOF_SEL) &= ~(IOF0_UART0_MASK);
GPIO_REG(GPIO_IOF_EN) |= IOF0_UART0_MASK;
UART_REG(UART_REG_DIV) = F_CPU / bauds - 1;
UART_REG(UART_REG_TXCTRL) |= UART_TXEN;
UART_REG(UART_REG_RXCTRL) |= UART_RXEN;
}
UARTClass::write(const uint8_t uc_data)
{
sio_putchar(uc_data, 1);
return (1);
}
UARTClass::sio_putchar(char c, int blocking)
{
volatile uint32_t *val = UART_REGP(UART_REG_TXFIFO);
uint32_t busy = (*val) & 0x80000000;
if (blocking) {
while (*val & 0x80000000);
} else if (busy) {
return 1;
}
UART_REG(UART_REG_TXFIFO) = c;
return 0;
}
じゃあ、これをそのままCのコードに持ってくればよかろう。
#include "./platform.h"
#include "./variant.h"
#include "./uart.h"
#include "./gpio.h"
void setup()
{
int bauds = 9600;
GPIO_REG(GPIO_OUTPUT_XOR)&= ~(IOF0_UART0_MASK);
GPIO_REG(GPIO_IOF_SEL) &= ~(IOF0_UART0_MASK);
GPIO_REG(GPIO_IOF_EN) |= IOF0_UART0_MASK;
UART_REG(UART_REG_DIV) = F_CPU / bauds - 1;
UART_REG(UART_REG_TXCTRL) |= UART_TXEN;
UART_REG(UART_REG_RXCTRL) |= UART_RXEN;
}
void loop()
{
const int blocking = 1;
volatile uint32_t *val = UART_REGP(UART_REG_TXFIFO);
uint32_t busy = (*val) & 0x80000000;
if (blocking) {
while (*val & 0x80000000);
} else if (busy) {
return;
}
UART_REG(UART_REG_TXFIFO) = 'A';
return;
}
setup()
とloop()
を使っているのは、まだArduinoの生成するcore.a
を使うからだ。これは、最終的には取り外せて直接main()
から記述出来るようになりたい。
以下のようなMakefileを作成した。
- riscv32-unknown-elf-gccはgithubで公開されているものを使おうとしたが、何故か
-march=rv32imac
が認識されずに、Arduino用に(自動)ダウンロードされたgccを利用した。手動インストールしたものは古いからだろうか?もう一度最新版にアップデートしてやり直してみたい。
- リンカスクリプトとして
$(PACKAGE_BASE)/freedom-e-sdk/bsp/env/freedom-e300-hifive1/link.lds
を指定している。これはArduinoが指定しているリンカスクリプトをそのまま利用している。
F_CPU=16000000
はこちらもArduinoIDEでのコンパイルオプションで定義してあったので追加した。だけどHiFiveって320MHz動作じゃなかったっけ?
TARGET = uart_test
ARCH = ~/.arduino15/packages/sifive/tools/riscv32-unknown-elf-gcc/3f7b3696217548bc31aeccf9a0c89bdfa4e16a8f/bin/riscv32-unknown-elf-
CC = $(ARCH)g++
OBJ_FILES = entry.o init.o start.o
PACKAGE_BASE = ~/.arduino15/packages/sifive/hardware/riscv/1.0.2
LINKER = $(PACKAGE_BASE)/freedom-e-sdk/bsp/env/freedom-e300-hifive1/link.lds
INC_PATH += $(PACKAGE_BASE)/freedom-e-sdk/bsp/env/freedom-e300-hifive1/
INC_PATH += $(PACKAGE_BASE)/freedom-e-sdk/bsp/env/
INC_PATH += $(PACKAGE_BASE)/freedom-e-sdk/bsp/include/
INC_PATH += $(PACKAGE_BASE)/freedom-e-sdk/bsp/include/sifive/devices/
INC_PATH += $(PACKAGE_BASE)/variants/standard/
INC_PATH += $(PACKAGE_BASE)/cores/arduino/
DEF_PARAM += F_CPU=16000000
DEF_PARAM += FREEDOM_E300_HIFIVE1
DEF_PARAM += __riscv_float_abi_soft
INCLUDE = $(addprefix -I, $(INC_PATH))
DEFINE = $(addprefix -D, $(DEF_PARAM))
CFLAGS += -march=rv32imac $(INCLUDE) $(DEFINE)
all: $(TARGET)
%.o: %.c
$(CC) $(CFLAGS) -c $<
%.o: %.S
$(CC) $(CFLAGS) -c $<
$(TARGET): $(TARGET).o
$(CC) $(CFLAGS) -nostartfiles -nostdlib -Wl,-N -Wl,--gc-sections -Wl,--wrap=malloc -Wl,--wrap=free -Wl,--wrap=sbrk -Wl,--start-group "~/Arduino/core/core.a" -lm -lc -lgloss -Wl,--end-group -lgcc -T$(LINKER) $^ -o $@
clean:
rm -rf *.o $(TARGET)
最後にcore.a
を追加しているが、これは以前ArduinoIDEでプログラムをコンパイルした際に自動的に生成されたもので、こちらにいくつか定数が入っていたのでとりあえずやむを得ず追加した。
最終的にはこれも除去し、必要最低限のファイル群でプログラムを生成できるようになりたい。
core.a
は以下のように作られている。でも、entry.S
, init.S
, start.S`があればとりあえず十分じゃないかなあ?glibcなどを使い始めると面倒だけど。
"/home/msyksphinz/.arduino15/packages/sifive/tools/riscv32-unknown-elf-gcc/3f7b3696217548bc31aeccf9a0c89bdfa4e16a8f/bin/riscv32-unknown-elf-ar" rcs "/home/msyksphinz/Arduino/core/core.a" "/home/msyksphinz/Arduino/core/entry.S.o"
"/home/msyksphinz/.arduino15/packages/sifive/tools/riscv32-unknown-elf-gcc/3f7b3696217548bc31aeccf9a0c89bdfa4e16a8f/bin/riscv32-unknown-elf-ar" rcs "/home/msyksphinz/Arduino/core/core.a" "/home/msyksphinz/Arduino/core/init.S.o"
"/home/msyksphinz/.arduino15/packages/sifive/tools/riscv32-unknown-elf-gcc/3f7b3696217548bc31aeccf9a0c89bdfa4e16a8f/bin/riscv32-unknown-elf-ar" rcs "/home/msyksphinz/Arduino/core/core.a" "/home/msyksphinz/Arduino/core/start.S.o"
"/home/msyksphinz/.arduino15/packages/sifive/tools/riscv32-unknown-elf-gcc/3f7b3696217548bc31aeccf9a0c89bdfa4e16a8f/bin/riscv32-unknown-elf-ar" rcs "/home/msyksphinz/Arduino/core/core.a" "/home/msyksphinz/Arduino/core/WInterrupts.c.o"
"/home/msyksphinz/.arduino15/packages/sifive/tools/riscv32-unknown-elf-gcc/3f7b3696217548bc31aeccf9a0c89bdfa4e16a8f/bin/riscv32-unknown-elf-ar" rcs "/home/msyksphinz/Arduino/core/core.a" "/home/msyksphinz/Arduino/core/hooks.c.o"
"/home/msyksphinz/.arduino15/packages/sifive/tools/riscv32-unknown-elf-gcc/3f7b3696217548bc31aeccf9a0c89bdfa4e16a8f/bin/riscv32-unknown-elf-ar" rcs "/home/msyksphinz/Arduino/core/core.a" "/home/msyksphinz/Arduino/core/itoa.c.o"
"/home/msyksphinz/.arduino15/packages/sifive/tools/riscv32-unknown-elf-gcc/3f7b3696217548bc31aeccf9a0c89bdfa4e16a8f/bin/riscv32-unknown-elf-ar" rcs "/home/msyksphinz/Arduino/core/core.a" "/home/msyksphinz/Arduino/core/malloc.c.o"
"/home/msyksphinz/.arduino15/packages/sifive/tools/riscv32-unknown-elf-gcc/3f7b3696217548bc31aeccf9a0c89bdfa4e16a8f/bin/riscv32-unknown-elf-ar" rcs "/home/msyksphinz/Arduino/core/core.a" "/home/msyksphinz/Arduino/core/sbrk.c.o"
"/home/msyksphinz/.arduino15/packages/sifive/tools/riscv32-unknown-elf-gcc/3f7b3696217548bc31aeccf9a0c89bdfa4e16a8f/bin/riscv32-unknown-elf-ar" rcs "/home/msyksphinz/Arduino/core/core.a" "/home/msyksphinz/Arduino/core/wiring.c.o"
"/home/msyksphinz/.arduino15/packages/sifive/tools/riscv32-unknown-elf-gcc/3f7b3696217548bc31aeccf9a0c89bdfa4e16a8f/bin/riscv32-unknown-elf-ar" rcs "/home/msyksphinz/Arduino/core/core.a" "/home/msyksphinz/Arduino/core/wiring_analog.c.o"
"/home/msyksphinz/.arduino15/packages/sifive/tools/riscv32-unknown-elf-gcc/3f7b3696217548bc31aeccf9a0c89bdfa4e16a8f/bin/riscv32-unknown-elf-ar" rcs "/home/msyksphinz/Arduino/core/core.a" "/home/msyksphinz/Arduino/core/wiring_digital.c.o"
"/home/msyksphinz/.arduino15/packages/sifive/tools/riscv32-unknown-elf-gcc/3f7b3696217548bc31aeccf9a0c89bdfa4e16a8f/bin/riscv32-unknown-elf-ar" rcs "/home/msyksphinz/Arduino/core/core.a" "/home/msyksphinz/Arduino/core/wiring_shift.c.o"
"/home/msyksphinz/.arduino15/packages/sifive/tools/riscv32-unknown-elf-gcc/3f7b3696217548bc31aeccf9a0c89bdfa4e16a8f/bin/riscv32-unknown-elf-ar" rcs "/home/msyksphinz/Arduino/core/core.a" "/home/msyksphinz/Arduino/core/drivers/fe300prci/fe300prci_driver.c.o"
"/home/msyksphinz/.arduino15/packages/sifive/tools/riscv32-unknown-elf-gcc/3f7b3696217548bc31aeccf9a0c89bdfa4e16a8f/bin/riscv32-unknown-elf-ar" rcs "/home/msyksphinz/Arduino/core/core.a" "/home/msyksphinz/Arduino/core/drivers/plic/plic_driver.c.o"
"/home/msyksphinz/.arduino15/packages/sifive/tools/riscv32-unknown-elf-gcc/3f7b3696217548bc31aeccf9a0c89bdfa4e16a8f/bin/riscv32-unknown-elf-ar" rcs "/home/msyksphinz/Arduino/core/core.a" "/home/msyksphinz/Arduino/core/Print.cpp.o"
"/home/msyksphinz/.arduino15/packages/sifive/tools/riscv32-unknown-elf-gcc/3f7b3696217548bc31aeccf9a0c89bdfa4e16a8f/bin/riscv32-unknown-elf-ar" rcs "/home/msyksphinz/Arduino/core/core.a" "/home/msyksphinz/Arduino/core/UARTClass.cpp.o"
"/home/msyksphinz/.arduino15/packages/sifive/tools/riscv32-unknown-elf-gcc/3f7b3696217548bc31aeccf9a0c89bdfa4e16a8f/bin/riscv32-unknown-elf-ar" rcs "/home/msyksphinz/Arduino/core/core.a" "/home/msyksphinz/Arduino/core/WMath.cpp.o"
"/home/msyksphinz/.arduino15/packages/sifive/tools/riscv32-unknown-elf-gcc/3f7b3696217548bc31aeccf9a0c89bdfa4e16a8f/bin/riscv32-unknown-elf-ar" rcs "/home/msyksphinz/Arduino/core/core.a" "/home/msyksphinz/Arduino/core/WString.cpp.o"
"/home/msyksphinz/.arduino15/packages/sifive/tools/riscv32-unknown-elf-gcc/3f7b3696217548bc31aeccf9a0c89bdfa4e16a8f/bin/riscv32-unknown-elf-ar" rcs "/home/msyksphinz/Arduino/core/core.a" "/home/msyksphinz/Arduino/core/abi.cpp.o"
"/home/msyksphinz/.arduino15/packages/sifive/tools/riscv32-unknown-elf-gcc/3f7b3696217548bc31aeccf9a0c89bdfa4e16a8f/bin/riscv32-unknown-elf-ar" rcs "/home/msyksphinz/Arduino/core/core.a" "/home/msyksphinz/Arduino/core/main.cpp.o"
"/home/msyksphinz/.arduino15/packages/sifive/tools/riscv32-unknown-elf-gcc/3f7b3696217548bc31aeccf9a0c89bdfa4e16a8f/bin/riscv32-unknown-elf-ar" rcs "/home/msyksphinz/Arduino/core/core.a" "/home/msyksphinz/Arduino/core/new.cpp.o"
"/home/msyksphinz/.arduino15/packages/sifive/tools/riscv32-unknown-elf-gcc/3f7b3696217548bc31aeccf9a0c89bdfa4e16a8f/bin/riscv32-unknown-elf-ar" rcs "/home/msyksphinz/Arduino/core/core.a" "/home/msyksphinz/Arduino/core/wiring_pulse.cpp.o"
これでプログラムをコンパイルできた。uart_test
はちゃんとRISC-V互換バイナリとなっている。
$ file uart_test
uart_test: ELF 32-bit LSB executable, UCB RISC-V, version 1 (SYSV), statically linked, not stripped
次は、OpenOCDを使ってHiFive1ボードへ書き込みを行う。