タイトルの通り、C言語へコンパイルをする Nim といふ言語をやってみました。基本的にマイコンはC言語で書くんだから Nim でもできるやろ(鼻ホジ)ということでやってみました。
やったこと
構造体も含めてほぼ全て Nim で書きたいなあと思いました。ですので、スタートアップルーチン意外は全て Nim で書きました。
基本的に Nim のオンラインマニュアルと、tattaka さんのブログ記事を存分に参考にしました。ありがとうございます。
また、STM32F303K8T6 で実際に動作をさせました。本記事では RCC
と SysTick
と GPIOA
の設定をレジスタ直打ちでとりあえず出来る所までやるということを目標にします。
プロジェクトの作成
Makefile
何らかのプロジェクトディレクトリを作成し、そこのルートに以下の Makefile
をおいてください。
CC=arm-none-eabi-gcc
SIZE=arm-none-eabi-size
CFLAGS= -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -ffunction-sections -fdata-sections -Os -g
OBJS=$(notdir $(subst .c,.o,$(wildcard src/*.c)) $(subst .c,.o,$(wildcard src/nimcache/*.c)) $(subst .s,.o,$(wildcard src/*.s)))
all: output.elf
$(SIZE) $^
output.elf: $(OBJS)
$(CC) $(CFLAGS) -Wl,-T,LinkerScript.ld,-Map=output.map,--gc-sections -o $@ $^
%.o: src/%.c
$(CC) $(CFLAGS) -c -o $@ $^
%.o: src/nimcache/%.c
$(CC) $(CFLAGS) -c -o $@ $^
%.o: src/%.s
$(CC) $(CFLAGS) -c -o $@ $^
flash:
echo output.elf | xargs -I {} openocd -f interface/cmsis-dap.cfg -f target/stm32f3x.cfg -c 'init;program {};reset;exit'
clean:
$(RM) *.o *.elf *.map
リンカスクリプト
後は空気読んでやってよ IDE や CubeMX 等で作成したプロジェクトからリンカスクリプトをパクり、同じくルートに LinkerScript.ld
と言う名で置きます。なお /DISCARD/
の部分は削除しておいてください。
ソースファイル
まずは src
というディレクトリを作成し、そこにスタートアップルーチンを .s
という拡張子で置いてください。後は上のリンクから tattaka さんのブログ記事に飛び、panicoverride.nim
、nimbase.h
の処理をします。
僕は以下のような Nim のファイルを書きました。文法の理解も結構時間かかったなあ。main.nim
としてくださいね。
Nim の第一引数は外に出してカッコヨク書けるということでイキりました。普通に ms_wait(200)
でいいですよ。
コンパイルはこちら。
$ nim cc -c --cpu=arm --d:release --gc:none --os:standalone --deadCodeElim:on --nimcache:src/nimcache src/main.nim
これで nim ファイルがC言語にコンパイルされました。続いて make
とすることでバイナリ生成完了です。あとはプログラマで書き込むなり、openocd
使うなりしてください。
追記(2019/1/14):
いつからか知りませんが、少なくとも現行のバージョンでは --nimcache:
を指定しないと .c ファイルがホームディレクトリの隠しフォルダに配置されるようになったようです。ですのでこの引数を上のコマンドに追加しました。
これがないと src/nimcache が更新されず、コードを手直ししても反映されないため、追加しました。
知見
{.volatile.}
は数値への属性ではなく、変数への属性であるconst
は所謂マクロ的な扱いのため、コンパイル時に定数展開される。
macro
を極めていれば多分もっとコストの低いプログラムが書けた。- 現状では
GPIOA
ならGPIOA
のポインタをメモリ上に配置してしまっている。これはもったいない。 - マクロを使い場合は
volatileStore
等を使うことになるが、ちょっと一癖ある。 - 何らかのプロシージャ内に記述しなければ、なんとグローバルスコープに代入式が記述されるという悲惨な事態に。
- 現状では
ms_wait
でさり気なくテンプレートを使った。型制約もきれいに出来て良さげである。