STM32G4 GCCとIARのコンパイラによるビルド時間の比較(2)

前回の確認でSin関数の速度を確認してみたが、 今度は重い三角関数でかつよくセンサの処理などで利用するatan2の速度を確認してみたいと思う。

IARの三角関数のライブラリは最適化がよく聞いてそうだ。

では、ライブラリは高速だが、コードからコンパイルしたものはあんまり早くないとかあるかなって思って

github.com

にあったfast_atan2を拝借してfloat版にして実行してみた。

関数は各1000回実行した速度を測定している、※()内部の時間

なぜか、GCCのほうは 最初の500msecのチェックの時間がずれていてsin関数の時間が長くなってしまっている。 ここら辺は何が起こっているのかはわからないが、意図したとおりに動作してくれているIARはうれしい。

IAR : ===
check 500msec : 500851.00usec  (500.85msec)
sin : 0.60usec  (0.60msec)
cmsis sin : 0.40usec  (0.40msec)
        error : 0.000325
atan2 : 0.70usec  (0.70msec)
fast atan2 : 0.39usec  (0.39msec)
        error : 0.003900
GCC : ===
check 500msec : 250854.00usec  (250.85msec)
sin : 25.62usec  (25.62msec)
cmsis sin : 0.39usec  (0.39msec)
        error : 0.000325
atan2 : 1.41usec  (1.41msec)
fast atan2 : 0.40usec  (0.40msec)
        error : 0.003880

仕事で利用しているのだが、IARのatan2fが非常に高速なのには助かっている。 以前も仕事中にatan2f使うと重いかな~、、って思ってベンチマークしたんだが、 atan2f律速じゃないことを確認してそのまま利用することができたのでライブラリの速度は勝手に信頼していた。

どうやら、この実行結果だけを確認するとマイコンのコードを書くのにはIARの環境のほうが優秀な印象を受ける。(結構な値段するしだし当たり前か?)

とするとある条件でなぜGCCのほうが高速なプログラムを生成できたのか次に調べてみよう。

確認したプログラムは下記のとおり

github.com

STM32G4 GCCとIARのコンパイラによるビルド時間の比較(1)

あるプログラムをGCCで作成していたのだが、IARで同じコードをコンパイルして実行してみたら18usecで完了していた処理が23usecもかかってしまうようになった。

かってにIARのほうが有償だし、ARM用のコンパイラなのでGCCと比べて効率的にコンパイルしてくれるものだと思っていたので30%ぐらい遅くなっていたので少しびっくりしてしまった。

本当は使ったことがないMDK-ARMも比較しようと思ったのだが、なんか評価用ライセンスが、営業さん経由?じゃないともらえないみたいなのでやめてしまった。

一体GCCとIARのコンパイル後のバイナリの実行速度はどのぐらいになるのか比較していこうと思う。

比較環境

IAR

  • version 9.10.2

GCC

  • gcc-arm-none-eabi-9-2020-q2

まずは三角関数ベンチマークからしてみる。

    start = get_usec();
    for (size_t i = 0; i < buff_size; i++)
    {
        rslt_buff[i] = sinf(m_pi);
    }
    output_elapse("sin", start, buff_size);

1000回しての平均をとってみたら、実行してみたら。

IAR = 0.48usec

GCC = 0.04usec

さすがに何かがおかしい気がしたのでちょっと確認してみる。

引数に渡しているm_piという変数が毎回一緒なのでGCCのほうは最初っからコンパイルずみの数値をrslt_buffに突っ込んでいるのではないか?とおもって、 Sinに渡す引数をRNGをつかって数値を実行時に作って、計算前にバッファにためておいた引数を利用するように変更した。

    start = get_usec();
    for (size_t i = 0; i < buff_size; i++)
    {
        rslt_buff[i] = sinf(ix_buff[i]);
    }
    output_elapse("sin", start, buff_size);

IAR = 0.36usec

GCC = 0.46usec

なぜか、IARのほうがはやくなったしまったが、それっぽい結果になったNanoLibのSinよりもIAR同梱のSinのほうが最適化されているのでは?という当初の予想通りに結果になった。

ちなみにCMSISの高速なSin関数は どちらも 0.33usec だった。

とりあえず、実行環境ができたので、今日はここまで。

STM32G4 の開発環境構築 デバック環境

stm32g4 nucleoがdegikeyから届いたのでデバッグ環境を構築。

OPENOCDのビルド

配布されているOPENOCDはSTM32G4とSTLINK-V3がたいおうしていなかったので

github.com の対応版をダウンロードして自分でビルドすることにした。

適当なディレクトリ(自分は~/download内)に移動して

git clone https://github.com/mjbots/openocd.git
cd openocd 
./bootstrap 
export CFLAGS="-Wno-error"  
export CXXFLAGS="-Wno-error"

./configure
make -j8 
sudo make install

でinstall完了。

export CFLAGS="-Wno-error"と
export CXXFLAGS="-Wno-error"
glibcのバージョンが新しいとsrc/helper/options.c内で#include <sys/sysctl.h>がdeprecatedのwarningをはいてビルドできなかったのでつけました。

udevの設定

下記のリンク先を参考に

tomoyuki-nakabayashi.github.io

stm32g4 nucleoを接続して

lsusb

してみると

Bus 001 Device 005: ID 0483:374e STMicroelectronics STLINK-V3

と出てきましたので、/etc/udev/rule.d/に60-openocd.cfgを作成して

# STMicroelectronics STLINK-V3
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374d", MODE="660", GROUP="plugdev", TAG+="uaccess"
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374e", MODE="660", GROUP="plugdev", TAG+="uaccess"
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374f", MODE="660", GROUP="plugdev", TAG+="uaccess"
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3753", MODE="660", GROUP="plugdev", TAG+="uaccess"

と記入。

sudo udevadm control --reload-rules

nucleoボードを一度抜き刺しして、パーミッションを確認。

ls -l /dev/bus/usb/001/005      # usb/から先は先程lsusbで確認した番号を入力
crwxrwxrwx+ 1 root root 189, 4  6月 13 15:15 /dev/bus/usb/001/005

パーミッションも設定できたので,これでst-linkを利用可能なはず。

VSCODEの設定

vscodeプラグイン「Cortex-debug」をインストールする。

marketplace.visualstudio.com

launch.jsonの編集

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Cortex Debug",
            "cwd": "${workspaceRoot}",
            "executable": "build/(プログラム名).elf",
            "request": "launch",
            "type": "cortex-debug",
            "servertype": "openocd",
            "configFiles": [
                "interface/stlink.cfg",
                "target/stm32g4x.cfg"
            ],
            "preLaunchTask": "build"
        }
    ]
}

makefile

# debug build?
DEBUG = 1

にすれば設定は完了。

f:id:omonting:20200613153201p:plain

stm32g4 の開発環境構築 Linux編

先日Windows版の環境を作ったばっかりだが、デバッグに使うOPENOCDがG4対応していなくて自前でビルドしなくては行けないという情報を得てやっぱり自前でビルドするとなるとLinuxでやったほうがいいなと思ってLinux上に開発環境を構築することとした。

基本的な構築は前回の omonting.hatenablog.com とほとんどおなじなので同じ部分は割愛する。

必要なソフトウェア

GNU Arm Embedded Toolchain

GNU Arm Embedded toolchain は展開したフォルダを\opt内に配置

パスを通す

zshを使っているので、~/.zshrcの一番下に

export PATH="/opt/gc-arm-none-eabi-9-2020-q2-update/bin:$PATH"

を記載。

c_cpp_properties.json

下記に変更

            "compilerPath": "/opt/gc-arm-none-eabi-9-2020-q2-update/bin/arm-none-eabi-g++",

Makefileの編集

今回のプロジェクトは基本C++で書こうと思っている。
CubeMXの吐き出したMakefileソースコードはCソースコード吐き出しでMakefileもそのようになっていたので、C++もコンパルできるように修正。 ファイル名を
main.c => main.cpp
stm32g4xx_it.c => stm32g4xx_it.cpp
に変更。

# debug build?
DEBUG = 0
# optimization
OPT = -O3

に変更、MCUのファーム開発は僕は最適化効かせてdebug中に必要な箇所のみ一時的に最適化をきってdebugしている。

C_SOURCES 内からmain.cとstm32g4xx_it.cを削除 書きを追記

# CPP sources
CXX_SOURCES =  \
Src/main.cpp \
Src/stm32g4xx_it.cpp

cppのコンパイラを定義

CXX = $(GCC_PATH)/$(PREFIX)g++

CFLAGSの下に書きを追記

CXXFLAGS = $(MCU) $(C_DEFS) $(C_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections  -isystem -std=c++17

ほかはCFLAGSと同様に処理しておく。

ビルド設定の部分を下記のように書き換え

#######################################
# build the application
#######################################
# list of objects
OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o)))
vpath %.c $(sort $(dir $(C_SOURCES)))
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(CXX_SOURCES:.cpp=.o)))  #追記
vpath %.cpp $(sort $(dir $(CXX_SOURCES)))                                                        #追記
# list of ASM program objects
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.s=.o)))
vpath %.s $(sort $(dir $(ASM_SOURCES)))

$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR) 
    $(CXX) -c $(CXXFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@    # CC->CXXに変更

$(BUILD_DIR)/%.o: %.cpp Makefile | $(BUILD_DIR)                                               #追記
    $(CXX) -c $(CXXFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.cpp=.lst)) $< -o $@      #追記

$(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR)
    $(AS) -c $(CFLAGS) $< -o $@

$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) Makefile
    $(CXX) $(OBJECTS) $(LDFLAGS) -o $@                                    #CC->CXXに変更
    $(SZ) $@

$(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
    $(HEX) $< $@
    
$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
    $(BIN) $< $@ 
    
$(BUILD_DIR):
    mkdir $@    

以上で完了。

STM32G4のビルド環境構築

普段は仕事でIAR workbenchを利用しているんですが、ちょっと家でもなんか作ってみようかなと思ったので、ビルド環境を整えてみました。
昔STM32F1の環境を利用していた時とは周辺環境が変わってきたので最初から再設定。

環境

昔利用していた環境はmacだったのですが、今の環境はwindows10 64bitです。

必要なソフトウェア

Visual studio code

azure.microsoft.com
昔はeclipse使ったりしていたのですが、エディタは今はこれです。
IAR利用しているときもdebug以外はこれでやっています。
STから提供されているcube-ideという全部いりのIDEもあるんですが、IDEはなんか重くて好きくないのでvscodeを利用していきます。

プラグインには下記を利用しています。

C/C++ for Visual Studio Code

code.visualstudio.com

debugにも別のプラグイン入れようと思っていますが、今回はビルド環境のみ。

GNU Arm Embedded Toolchain

developer.arm.com
コンパイラGNU-ARMeclipse IARと違ってC++17にちゃんと対応しているのがとてもいいです。(IARはC++17対応とおもって飛びついたら標準ライブラリは未対応だったので悲しかった。)

STM32 CubeMx

https://www.st.com/ja/development-tools/stm32cubemx.html
マイコンの基本設定などはCubeMXを使っています。めちゃくちゃ便利で大好きですがある場所が設定できなかったりバグってたりしてたまにトラブりますが、それを加味しても好きだ。アップデートがあったらバグがつぶれていることを期待して必ず更新しています。

ビルドシステムにはmakeじゃなくてmesonというビルドシステムがあるみたいでlinux環境で使ってみてなかなかわかりやすかったので使ってみたかったが windwosでcrossでちゃんと動かす方法がわからなかったのでとりあえずcube-mxで出てくるmakefileを利用しようと思う。 ただ、cube-mxで編集後に吐き出したmakefileを都度編集するのがなんだかなって思ったのでそのうち使えるようにしてみたい。

make

www.msys2.org
makeを使えるようにmsys2をインストール後msys2を起動して

pacman -S mingw-w64-x86_64-make

でインストール。

環境変数の設定

環境変数のPATHに さっき落としてきたGNU Arm Embedded Toolchainを展開した中のbinフォルダを指定。
私はCドライブ直下にtoolsというフォルダを作って展開したので、
C:\tools\gcc-arm-none-eabi-9-2020-q2-update-win32\bin makeを呼べるために msysのpathを追加 C:\msys64\mingw64\bin

VSCodeでビルドできる環境の作成

CubeMxでProject Manager->Projectタブ内のToolChain/IDEmakefileに設定してコード生成。

ワークスペースファイルの作成。

f:id:omonting:20200608003424p:plain
ワークスペースディレクト
ディレクトリ内にvscodeワークスペースファイルを作成。 stm32g474.code-workspace

{
    "folders": [
        {
            "name": "プロジェクト名",
            "path": "./"
        }
    ],
    "settings": {
        "window.zoomLevel": 0,
        "C_Cpp.clang_format_fallbackStyle": "google",
        "editor.formatOnSave": true
    },
    "extensions": {}
}

task.jsonの設定

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "build",
            "type": "shell",
            "command": "mingw32-make all",
            "args": [],
            "group": "build",
            // Use the standard MS compiler pattern to detect errors, warnings and infos
            "problemMatcher": {
                "base": "$g++",
                "fileLocation": [
                    "relative",
                    "${workspaceRoot}/build"
                ]
            }
        },
        {
            "label": "clean",
            "type": "shell",
            "command": "mingw32-make clean",
            "args": [],
            "group": "build",
            // Use the standard MS compiler pattern to detect errors, warnings and infos
            "problemMatcher": {
                "base": "$g++",
                "fileLocation": [
                    "relative",
                    "${workspaceRoot}/build"
                ]
            }
        }
    ]
}

補完がきいてエディタでエラーが出ないように設定。

下記のようにc_cpp_properties.jsonを作成。

{
    "configurations": [
        {
            "name": "Win32",
            "includePath": [
                "${workspaceFolder}/**",
                "${workspaceFolder}/Inc/**",
                "${workspaceFolder}/Drivers/CMSIS/Core/include/**",
                "${workspaceFolder}/Drivers/CMSIS/Device/ST/STM32G4xx/include**",
                "${workspaceFolder}/Drivers/CMSIS/include**",
                "${workspaceFolder}/Drivers/STM32G4xx_HAL_Driver/inc**"
            ],
            "defines": [
                "_DEBUG",
                "UNICODE",
                "_UNICODE",
                "USE_FULL_LL_DRIVER",     # ここから下はcubemxで作成されたmakefileからコピーしてきた。
                "HSE_VALUE=24000000",
                "HSE_STARTUP_TIMEOUT=100",
                "LSE_STARTUP_TIMEOUT=5000",
                "LSE_VALUE=32768",
                "EXTERNAL_CLOCK_VALUE=12288000",
                "HSI_VALUE=16000000",
                "LSI_VALUE=32000",
                "VDD_VALUE=3300",
                "PREFETCH_ENABLE=0",
                "INSTRUCTION_CACHE_ENABLE=1",
                "DATA_CACHE_ENABLE=1",
                "STM32G474xx"
            ],
            "windowsSdkVersion": "10.0.17763.0",
            "compilerPath": "C:\\tools/gcc-arm-none-eabi-9-2020-q2-update-win32/bin/arm-none-eabi-g++.exe",
            "cStandard": "c11",
            "cppStandard": "c++17",
            "intelliSenseMode": "gcc-x64"
        }
    ],
    "version": 4
}

以上の設定が完了したら。 ctrl+shift+bでビルド候補が出てくるのでbuildを選べばbuild完了。

WSL2がもっと進んでビルド環境すべてLinux上で作れてgdbまで動いたら楽しそうだなと思いました。