読者です 読者をやめる 読者になる 読者になる

FPGA開発日記

FPGAというより、コンピュータアーキテクチャかもね! カテゴリ別記事インデックス https://sites.google.com/site/fpgadevelopindex/

HiFive1のプログラムコンパイルをArduinoIDEを使わずに制御したい(1. C/C++コードのコンパイル)

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;

  //F_Baud = f_in/(div+1)

  UART_REG(UART_REG_DIV) = F_CPU / bauds - 1;
  UART_REG(UART_REG_TXCTRL) |= UART_TXEN;
  UART_REG(UART_REG_RXCTRL) |= UART_RXEN;


//  sio_setbaud(bauds);
}

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

以下のようなMakefileを作成した。

  • riscv32-unknown-elf-gccgithubで公開されているものを使おうとしたが、何故か-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 = riscv32-unknown-elf-
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 $(OBJ_FILES)
$(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ボードへ書き込みを行う。