はじめに...
第4回は、前回繰り越した model.rtw とテンプレート Makefile についてです。
テンプレート Makefile のセクションで、片仮名の「メイクファイル」と英字の「Makefile」を区別して使っています。この使い分けは本記事独自のものなので、定義をここに記します。
-
makefile
make ユーティリティを -f オプションなしで実行する際のメイクファイル -
ターゲット Makefile
テンプレート Makefile から生成される、make ユーティリティに -f オプションで渡すメイクファイル -
テンプレート Makefile
後述 -
メイクファイル
これらの総称
model.rtw
Simulink モデルのブロックやコンフィギュレーション パラメータ等の情報が書かれている「コード生成」段階で作られるファイルです。このファイルの情報が tlc ファイルやテンプレート Makefile へ展開されて、ソースファイルやターゲット Makefile といった成果ファイル (次図の淡い赤色) が出来上がります。

model.rtw は、階層構造を持つ JSON のような書式で記述されたデータファイルです。ただ、使われている用語やデータアクセス方法など、異なるところもありますので、MATLAB ドキュメンテーション 「model.rtw ファイルとスコープ」に目を通されることをお勧めします。
ファイルは、ビルドフォルダ ―モデル名が「mymodel」の場合、ここでは「mymodel_mytarget_ert」― 内に作られますが、デフォルト動作ではビルドプロセス終了時に消されてしまいます。ファイルを残すには、コンフィギュレーションパラメータ ウィンドウ [コード生成] ペイン ⇨ [詳細設定パラメータ] ⇨ [.rtw ファイルを残す] パラメータにチェックを付けます。
次は model.rtw の冒頭です。
CompiledModel {
Name "mymodel"
OrigName "mymodel"
PrmModelName SLDataModelName(mymodel)
IsPreCodeGenPhase 0
ModelReferenceTargetType "NONE"
...
先頭行の「CompiledModel」が、第3回の「mytarget_file_process.tlc (3)」や「mytarget_srmain.tlc (2)」に出てきたそれです。
%assign SAMPLETIME = CompiledModel.FundamentalStepSize
%assign intType = LibGetDataTypeNameFromId(::CompiledModel.tSS_INTEGER)
%assign uint8Type = LibGetDataTypeNameFromId(::CompiledModel.tSS_UINT8)
ここで、FundamentalStepSize は CompiledModel 内にレコード 実存しますが、tSS_INTEGER や tSS_UINT8 は見当たりません。これらは内部処理で用いられる列挙値のようなもので、model_reduced.rtw に載っています。
CompiledModel {
...
tSS_UINT8 3
...
tSS_INTEGER 10
...
LibGetDataTypeNameFromId() は、この列挙値に割り当てられた数値を頼りに、model.rtw ファイルの CompiledModel.CGTypes.CGType[n].Name(※) レコードの値を取得して返します。ここで「n」は列挙値に割り当てられた数値です。
CompiledModel {
…
CGTypes {
...
CGType { /* CGType[3] */
Name "uint8_T"
SLTypeIdx 3
}
...
CGType { /* CGType[10] */
Name "int_T"
SLTypeIdx 10
}
}
結果、intType
に「int_T」が、uint8Type
に「uint8_T」が設定されます。
(※) ドット記法については、MATLAB ドキュメンテーション「model.rtw ファイルとスコープ」を参照。
テンプレート Makefile (mytarget.tmf)
テンプレート Makefile は、ターゲット Makefile を生成するためのテンプレートファイルで、生成は「コード生成」段階で行われます。
ターゲット Makefile のファイル名は「モデル名.mk」となります。
「テンプレート Makefile」にもテンプレートが用意されており、その場所は [matlabroot]¥toolbox¥coder¥compile¥tmf フォルダです。ert ベースのそれは幾つかあり (次図の色付き) 、カスタム ラピッドプロトタイピング環境の外部開発ツールに合わせてファイルを選びます。
用意されているテンプレートはネイティブ開発環境向けで、クロス開発環境向けへの手直しはほぼ全修正です。
Arduino IDE のそれは UNIX ベースですから、ここでは「ert_UNIX.tmf」のコピーを Target フォルダ (mytarget¥mytarget) へ「mytarget.tmf」というファイル名で保存して編集します。
テンプレート Makefile の中身は、メイクファイルそのものですが、所々に Coder 固有の「トークン」が使われています。「トークン」は |>○○○<|
という形をしており、これが具体的な文字列に置き換えられて、ターゲット Makefile となります。詳細は MATLAB ドキュメンテーション 「テンプレート makefile のカスタマイズ」 をご覧ください。
mytarget.tmf (1):Macros read by make_rtw
##################################################
# mytarget.tmf
#
# Template File : ert_unix.tmf
# Copyright 1994-2022 The MathWorks, Inc.
#
##### Macros read by make_rtw ####################
MAKECMD = "|>MATLAB_BIN<|/|>ARCH<|/gmake"
HOST = PC
BUILD = yes
SYS_TARGET_FILE = mytarget.tlc
次はこのパートのターゲット Makefile 展開例です。
MAKECMD = "C:/Program Files/MATLAB/R2024b/bin/win64/gmake"
HOST = PC
BUILD = yes
SYS_TARGET_FILE = mytarget.tlc
- MAKECMD
make ユーティリティのパスを指定します。ここでは MATLAB に備わっている gmake を使用しました。当 MATALB インストール環境では、|>MATLAB_BIN<|
の展開フォルダ名に空白文字を含むため、二重引用符で囲っています。 - HOST
MATLAB がインストールされている PC のプラットフォームを指定します。 - BUILD
ビルドの有無を指定します。有効な値は「yes」「no」です。 - SYS_TARGET_FILE
システムターゲットファイルを指定します。ここでは「mytarget.tlc」です。
mytarget.tmf (2):Tokens expanded by make_rtw
##### Tokens expanded by make_rtw ####################
#
MODEL = |>MODEL_NAME<|
MODULES = |>MODEL_MODULES<|
PRODUCT = |>PRODUCT<|
MAKEFILE = |>MAKEFILE_NAME<|
MATLAB_ROOT = |>MATLAB_ROOT<|
ALT_MATLAB_ROOT = |>ALT_MATLAB_ROOT<|
START_DIR = |>START_DIR<|
S_FUNCTIONS_LIB = |>S_FUNCTIONS_LIB<|
NUMST = |>NUMST<|
NCSTATES = |>NCSTATES<|
COMPUTER = |>COMPUTER<|
BUILDARGS = |>BUILDARGS<|
MULTITASKING = |>MULTITASKING<|
INTEGER_CODE = |>INTEGER_CODE<|
MAT_FILE = |>MAT_FILE<|
ALLOCATIONFCN = |>GENERATE_ALLOC_FCN<|
ONESTEPFCN = |>COMBINE_OUTPUT_UPDATE_FCNS<|
TERMFCN = |>INCLUDE_MDL_TERMINATE_FCN<|
ENABLE_SLEXEC_SSBRIDGE = |>ENABLE_SLEXEC_SSBRIDGE<|
MULTI_INSTANCE_CODE = |>MULTI_INSTANCE_CODE<|
CLASSIC_INTERFACE = |>CLASSIC_INTERFACE<|
MODELREFS = |>MODELREFS<|
GEN_SAMPLE_MAIN = |>GEN_SAMPLE_MAIN<|
TARGET_LANG_EXT = |>TARGET_LANG_EXT<|
SHRLIBTARGET = |>SHRLIBTARGET<|
MAKEFILEBUILDER_TGT = |>MAKEFILEBUILDER_TGT<|
STANDALONE_SUPPRESS_EXE = |>STANDALONE_SUPPRESS_EXE<|
OPTIMIZATION_FLAGS = |>OPTIMIZATION_FLAGS<|
ADDITIONAL_LDFLAGS = |>ADDITIONAL_LDFLAGS<|
INTERLEAVED_COMPLEX_FLAGS = |>INTERLEAVED_COMPLEX_FLAGS<|
DEFINES_CUSTOM = |>DEFINES_CUSTOM<|
DEFINES_OTHER = |>DEFINES_OTHER<|
COMPILE_FLAGS_OTHER = |>COMPILE_FLAGS_OTHER<|
SYSTEM_LIBS = |>SYSTEM_LIBS<|
MODEL_HAS_DYNAMICALLY_LOADED_SFCNS = |>MODEL_HAS_DYNAMICALLY_LOADED_SFCNS<|
MODEL = mymodel
MODULES = mymodel_data.c
PRODUCT = mymodel.exe
MAKEFILE = mymodel.mk
MATLAB_ROOT = C:\Program Files\MATLAB\R2024b
ALT_MATLAB_ROOT = C:\PROGRA~1\MATLAB\R2024b
START_DIR = Z:\qiita\model
S_FUNCTIONS_LIB =
NUMST = 1
NCSTATES = 0
COMPUTER = PCWIN64
BUILDARGS = GENERATE_ERT_S_FUNCTION=0 GENERATE_ASAP2=0 EXT_MODE=0 EXTMODE_STATIC_ALLOC=0 EXTMODE_STATIC_ALLOC_SIZE=1000000 EXTMODE_TRANSPORT=0 TMW_EXTMODE_TESTING=0 DOWNLOAD_TO_ARDUINO=0 OPTS="-DTID01EQ=0"
MULTITASKING = 0
INTEGER_CODE = 1
MAT_FILE = 0
ALLOCATIONFCN = 0
ONESTEPFCN = 1
TERMFCN = 1
ENABLE_SLEXEC_SSBRIDGE = 0
MULTI_INSTANCE_CODE = 0
CLASSIC_INTERFACE = 0
MODELREFS =
GEN_SAMPLE_MAIN = 1
TARGET_LANG_EXT = c
SHRLIBTARGET = 0
MAKEFILEBUILDER_TGT = 0
STANDALONE_SUPPRESS_EXE = 0
OPTIMIZATION_FLAGS =
ADDITIONAL_LDFLAGS =
INTERLEAVED_COMPLEX_FLAGS = -R2018a
DEFINES_CUSTOM =
DEFINES_OTHER = -DHAVESTDIO
COMPILE_FLAGS_OTHER =
SYSTEM_LIBS =
MODEL_HAS_DYNAMICALLY_LOADED_SFCNS = 0
テンプレート (ert_UNIX.tmf) の流用です。各トークンの説明は次をご覧ください。
MATLAB ドキュメンテーション 「テンプレート makefile とトークン」
⇨ 表 [make_rtw によって展開されるテンプレート makefile トークン]
⇨ カテゴリ [一般的な目的]
(※) 表に載っていないトークンも幾つか在ります。
mytarget.tmf (3):Model and reference models
##### Model and reference models ####################
MODELLIB = |>MODELLIB<|
MODELREF_LINK_LIBS = |>MODELREF_LINK_LIBS<|
RELATIVE_PATH_TO_ANCHOR = |>RELATIVE_PATH_TO_ANCHOR<|
MODELREF_TARGET_TYPE = |>MODELREF_TARGET_TYPE<|
ifneq ($(MATLAB_ROOT),$(ALT_MATLAB_ROOT))
MATLAB_ROOT := $(ALT_MATLAB_ROOT)
endif
MODELLIB =
MODELREF_LINK_LIBS =
RELATIVE_PATH_TO_ANCHOR = ..
MODELREF_TARGET_TYPE = NONE
ifneq ($(MATLAB_ROOT),$(ALT_MATLAB_ROOT))
MATLAB_ROOT := $(ALT_MATLAB_ROOT)
endif
ここもテンプレート (ert_UNIX.tmf) の流用です。各トークンの説明は次をご覧ください。
MATLAB ドキュメンテーション 「テンプレート makefile とトークン」
⇨ 表 [make_rtw によって展開されるテンプレート makefile トークン]
⇨ カテゴリ [モデル参照のサポート]
mytarget.tmf (4):Tool Specifications
##### Tool Specifications ####################
include z:/qiita/mytarget/arduino_ide/dev_tools.mk
第1回 ラピッドプロトタイピング環境用フォルダ「DevTool フォルダ」で述べた、クロス開発環境ツール固有の設定を記述したメイクファイルをインクルードします。
この先のコードブロックで目にする「ARDUINO_○○○」というメイクファイル変数は、この dev_tools.mk で記述しています。内容は下方の dev_tools.mk をご覧ください。
このパートは、テンプレート Makefile とターゲット Makefile で違いがないため、展開例を省略します。
mytarget.tmf (5):Include Path
##### Include Path ####################
INCLUDES = -I. \
-I$(RELATIVE_PATH_TO_ANCHOR) \|>START_EXPAND_INCLUDES<|
-I$(subst \,/,|>EXPAND_DIR_NAME<|) \|>END_EXPAND_INCLUDES<|
$(addprefix -I,$(ARDUINO_INCDIRS)) \
$(addprefix -I,$(USER_INCDIRS))
INCLUDES = -I. \
-I$(RELATIVE_PATH_TO_ANCHOR) \
-I$(subst \,/,$(START_DIR)) \
-I$(subst \,/,$(START_DIR)\mymodel_mytarget_ert) \
-I$(subst \,/,$(MATLAB_ROOT)\extern\include) \
-I$(subst \,/,$(MATLAB_ROOT)\simulink\include) \
-I$(subst \,/,$(MATLAB_ROOT)\rtw\c\src) \
-I$(subst \,/,$(MATLAB_ROOT)\rtw\c\src\ext_mode\common) \
-I$(subst \,/,$(MATLAB_ROOT)\rtw\c\ert) \
$(addprefix -I,$(ARDUINO_INCDIRS))
トークンが複数の要素へ展開される構文です。|>START_EXPAND_INCLUDES<|
と |>END_EXPAND_INCLUDES<|
で囲んだ箇所が繰り返されます。この「囲んだ箇所」には改行文字も入っています。この例の繰り返しは、
⏎ -I$(subst \,/,|>EXPAND_DIR_NAME<|) \
です。
「START_DIR」は、ビルドする Simulink モデルの置かれたフォルダを指し、「Tokens expanded by make_rtw」でトークン拡張が行われます。
この構文で利用できる他のトークンは次をご覧ください。
MATLAB ドキュメンテーション 「テンプレート makefile とトークン」
⇨ 表 [make_rtw によって展開されるテンプレート makefile トークン]
⇨ カテゴリ [S-Function とビルド情報のサポート]
mytarget.tmf (6):Flags
このパートは、テンプレート Makefile とターゲット Makefile で違いがないため、展開例を省略します。
##### Flags ####################
CFLAGS = $(DEBUG) $(OPTIM) $(C_WARN) $(C_OPTS) $(ARDUINO_DEVTYPE) $(DEFINES) $(INCLUDES)
CXXFLAGS = $(DEBUG) $(OPTIM) $(CXXWARN) $(CXXOPTS) $(ARDUINO_DEVTYPE) $(DEFINES) $(INCLUDES)
### DEBUG
USER_DEBUG =
ifneq ($(USER_DEBUG),)
DEBUG := $(USER_DEBUG)
else
DEBUG := $(ARDUINO_DEBUG)
endif
### 最適化
USER_OPTIM =
ifneq ($(OPTIMIZATION_FLAGS),)
OPTIM := $(OPTIMIZATION_FLAGS)
else
ifneq ($(USER_OPTIM),)
OPTIM := $(USER_OPTIM)
else
OPTIM := $(ARDUINO_OPTIM)
endif
endif
### WARNING
C_WARN = $(ARDUINO_C_WARN)
CXXWARN = $(ARDUINO_CXXWARN)
### OTHERS
C_OPTS = $(ARDUINO_C_OPTS) $(OPTS)
CXXOPTS = $(ARDUINO_CXXOPTS) $(OPTS)
### DEFINES
DEFINES = $(DEFINES_REQUIRE) $(ARDUINO_C_DEFS)
DEFINES_REQUIRE = -DMODEL=$(MODEL) \
-DNUMST=$(NUMST) \
-DNCSTATES=$(NCSTATES) \
-DMAT_FILE=$(MAT_FILE) \
-DINTEGER_CODE=$(INTEGER_CODE) \
-DONESTEPFCN=$(ONESTEPFCN) \
-DTERMFCN=$(TERMFCN) \
-DMULTI_INSTANCE_CODE=$(MULTI_INSTANCE_CODE) \
-DCLASSIC_INTERFACE=$(CLASSIC_INTERFACE) \
-DALLOCATIONFCN=$(ALLOCATIONFCN)
### LDFLAGS, OBJCOPY_FLAGS, OBJDUMP_FLAGS
ifeq ($(INTEGER_CODE),0)
PRINT_LIB := $(PRINT_LIB_FLOAT)
else
#PRINT_LIB := $(PRINT_LIB_MIN)
PRINT_LIB :=
endif
LDFLAGS = $(ARDUINO_OPTIM) $(ARDUINO_DEVTYPE) $(ARDUINO_LDFLAGS) $(PRINT_LIB)
OBJCOPY_FLAGS = $(ARDUINO_OBJCOPY_FLAGS)
OBJDUMP_FLAGS = $(ARDUINO_OBJDUMP_FLAGS)
mytarget.tmf (7):Additional Libraries
##### Additional Libraries ####################
LIBS = $(S_FUNCTIONS_LIB)
展開例省略します。
mytarget.tmf (8):Source Files
##### Source Files ####################
MODEL_OBJS = $(addsuffix .o, $(basename $(MODEL_SRCS)))
MODEL_SRCS = ert_main.c \
$(MODEL).$(TARGET_LANG_EXT) \
$(MODULES)
VPATH := . \
$(RELATIVE_PATH_TO_ANCHOR) \|>START_EXPAND_RULES<|
$(subst \,/,|>EXPAND_DIR_NAME<|) \|>END_EXPAND_RULES<|
$(ARDUINO_SRCDIRS)
MODEL_OBJS = $(addsuffix .o, $(basename $(MODEL_SRCS)))
MODEL_SRCS = ert_main.c \
$(MODEL).$(TARGET_LANG_EXT) \
$(MODULES)
VPATH := . \
$(RELATIVE_PATH_TO_ANCHOR) \
$(subst \,/,Z:\qiita\mytarget\blocks) \
$(subst \,/,$(MATLAB_ROOT)\rtw\c\src) \
$(subst \,/,$(MATLAB_ROOT)\simulink\src) \
$(subst \,/,$(MATLAB_ROOT)\toolbox\simulink\blocks\src) \
$(ARDUINO_SRCDIRS)
複数要素展開パターンのトークンです。展開規則は「mytarget.tmf (5)」をご覧ください。
mytarget.tmf (9):Rules
##### Rules ####################
PRODUCT := $(MODEL).hex
ARTIFACT := $(MODEL).elf
LOCAL_MODULE := arduino.a
.PHONY : all
all : $(PRODUCT)
@echo.&echo ### Created $< successfully (or it was already up to date)
$(PRODUCT): $(ARTIFACT)
@echo.&echo ### Creating $@
$(OBJCOPY) $(OBJCOPY_FLAGS) $< $@
@echo ### Created $@ ###
@echo.&echo ### Project size
@$(SIZE) $<
@$(SIZE) $< > code_size_$(TARGET_LANG_EXT).txt
@$(OBJDUMP) $(OBJDUMP_FLAGS) $< > disassembly.txt
$(ARTIFACT): $(MODEL_OBJS) $(LOCAL_MODULE) $(LIBS) $(MODELREF_LINK_LIBS)
@echo.&echo ### Creating $@
$(LD) -o $@ $(LDFLAGS) $^
@echo ### Created $@ ###
展開例省略します。
「@echo.&echo ### Creating $@
」は、メッセージ「### Creating ○○○」の上に空行を挿入する、コマンドプロンプト限定のステートメントです。ビルド情報を MATLAB コマンドウィンドウで表示させると次のようになります。
mytarget.tmf (10):Support for building modules
##### Support for building modules ####################
%.o : %.c
@echo.&echo Compiling: $<
$(CC) -c -o $@ $(CFLAGS) $<
%.o : %.cpp
@echo.&echo Compiling: $<
$(CXX) -c -o $@ $(CXXFLAGS) $<
展開例省略します。
mytarget.tmf (11):Libraries
##### Libraries ####################
ARFLAGS = $(ARDUINO_ARFLAGS)
$(LOCAL_MODULE) : $(MAKEFILE) rtw_proj.tmw $(ARDUINO_OBJS)
@echo.&echo ### Creating $@
$(AR) $(ARFLAGS) $@ $(ARDUINO_OBJS)
@echo ### Created $@ ###
展開例省略します。
二回目以降のビルド時間を短縮するため、Arduino 関連のオブジェクトファイルをアーカイブしています。
mytarget.tmf (12):Dependencies
##### Dependencies ####################
$(MODEL_OBJS) : $(MAKEFILE) rtw_proj.tmw
展開例省略します。
dev_tool.mk
何の変哲もないメイクファイルです。コードの掲載に留めます。
####################################################################
MCU := atmega328p
F_CPU := 16000000
VARIANT := standard
ARDUINO_ROOT := c:/ArduinoIDE/arduino-1.8.19
ARDUINO_VER := 106
####################################################################
CC = $(avr_bin)/avr-gcc
CXX = $(avr_bin)/avr-g++
LD = $(avr_bin)/avr-gcc
AR = $(avr_bin)/avr-ar
SIZE = $(avr_bin)/avr-size
OBJCOPY = $(avr_bin)/avr-objcopy
OBJDUMP = $(avr_bin)/avr-objdump
ARDUINO_OBJS = $(addsuffix .o,$(basename $(ARDUINO_SRCS)))
ARDUINO_SRCS = $(filter-out main.cpp,$(notdir $(avr_srcs)))
ARDUINO_SRCDIRS = $(src_dirs)
ARDUINO_INCDIRS = $(inc_dirs)
ARDUINO_DEVTYPE = -mmcu=$(MCU)
ARDUINO_DEBUG = -g
ARDUINO_OPTIM = -Os
ARDUINO_C_WARN = -Wall -Wstrict-prototypes
ARDUINO_CXXWARN = -Wall
ARDUINO_C_DEFS = -DF_CPU=$(F_CPU)UL \
-DARDUINO=$(ARDUINO_VER) \
-DEXIT_FAILURE=1
ARDUINO_CXXDEFS = -DF_CPU=$(F_CPU)UL \
-DARDUINO=$(ARDUINO_VER)
ARDUINO_C_OPTS = -std=gnu99 \
-ffunction-sections \
-fdata-sections \
-fpack-struct \
-fshort-enums \
-funsigned-char
ARDUINO_CXXOPTS = -ffunction-sections \
-fdata-sections \
-fpack-struct \
-fshort-enums \
-funsigned-char
#-------------------------------------------------------------------
ARDUINO_LDFLAGS = -Wl,--gc-sections,-Map,mapFile.map,--cref
ARDUINO_ARFLAGS = rcs
ARDUINO_OBJCOPY_FLAGS = -O ihex -R .eeprom
ARDUINO_OBJDUMP_FLAGS = -d -S
### printf() リンカオプション
PRINT_LIB_MIN = -Wl,-u,vfprintf -lprintf_min
PRINT_LIB_FLOAT = -Wl,-u,vfprintf -lprintf_flt -lm
####################################################################
avr_bin := $(ARDUINO_ROOT)/hardware/tools/avr/bin
avr_dir := $(ARDUINO_ROOT)/hardware/arduino/avr
avr_cores := $(avr_dir)/cores/arduino
avr_variant := $(avr_dir)/variants/$(VARIANT)
avr_libs := $(avr_dir)/libraries/Wire/src \
$(avr_dir)/libraries/Wire/src/utility \
$(avr_dir)/libraries/SPI/src
avr_srcs := $(wildcard $(avr_cores)/*.c) \
$(wildcard $(avr_cores)/*.cpp) \
$(wildcard $(addsuffix /*.c,$(avr_libs))) \
$(wildcard $(addsuffix /*.cpp,$(avr_libs)))
src_dirs := $(subst \,/,$(patsubst %/,%,$(sort $(dir $(avr_srcs)))))
inc_dirs := $(subst \,/,$(src_dirs) $(avr_variant))
終わりに...
第4回は以上です。トークンは、ご覧いただいて分かるように、用法そのものは理解に難くないですが、モデルの何とどのトークンが関連しているかを、ドキュメンテーションから判断しづらいものもあります。この辺りのことを別の投稿で紹介できたらいいなと思っています。
ここまでに登場したファイルとその格納先フォルダ、ならびにコード生成時のモデルフォルダ内容を下に示して締めくくります。最後までお読みくださり、ありがとうございました。
参考文献
コード生成プロセスの概要
TLC プログラム
AVR GCC Makefile 見本
GNU Make 第3版