" Благодаря GNU Make Вы можете собирать прошивку для любого микроконтроллера любым компилятором. "

Собрать прошивку компилятором IAR с помощью GNU Make — это не просто возможно, это стандартный подход для автоматизации сборки, например, на CI/CD серверах, где использование IDE неудобно. IAR поставляется с набором консольных утилит, которые делают этот процесс вполне прямолинейным.

В чем проблема?

Разработчики IDE-IAR все настройки компилятора, компоновщика и binutils засунули в GUI. Это удобно для разовой, единовременной, единичной разработки и учебных проектов в ВУЗах, но абсолютно непригодно для промышленного программирования, когда надо малым коллективом поддерживать на плаву широкий ассортимент электронной продукции.

У меня был случай, когда была одна Legacy IAR-IDE сборка 55-ю конфигурациями для разных клиентов. Надо было для всех конфигураций добавить ещё один программный модуль: пару *.c файлов. Дак вот... Сам код я написал за 2-3 часа. Однако у меня ушло две недели тупо на то, чтобы просто в каждую IAR конфигурацию мышкой в IDE-IAR добавить папку, прописать пути к этой папке и добавить макросы в GUI IDE. Всё это потом как-то попадало в .ewp файл. Вот так, господа… В GNU Make же такое делается изменением буквально одной строки, если не одного символа.

Главный вопрос:

Как собрать прошивку для микроконтроллера stm32f407ve компилятором IAR из самостоятельно написанных GNU Make скриптов? То есть вообще без использования пресловутого XML-образного .ewp файла. Как должен выглядеть Makefile для IAR и ключи к компилятору и опции компоновщику?

Необходимость этой задачи обусловлена прежде всего потребностями пере использования конфигов для электронных плат, микроконтроллеров и процессорных ядер при создании новых похожих сборок с приблизительно одинаковым функционалом. При сборке из любых скриптов обычно очень легко добавить новую сборку. Достаточно только написать ��онфиг и 3 строчки в отдельном Makefile и вот у вас новая специфическая сборка. Происходит наследование конфигов. Подробнее про это можно почитать в отдельном тексте .

Если коротко, то самостоятельно написанные make скрипты открывают вам двери в новые возможности разработки и повышения производительности труда. Начиная с автоматического выравнивая отступов, заканчивая автоматической отправкой артефактов потребителям по электронной почте. С make скриптами вы не будете привязаны к какой-то конкретной IDE и сможете писать код в любом текстовом редакторе, который вам импонирует. В MakeFile очень просто менять компиляторы. Это, буквально, заменить одну строчку в скриптах. С GCC на Clang или c GHS на IAR. Сборка двумя компиляторами поможет вам найти больше ошибок в проекте. При сборке из makefile вам вообще всё равно для какой целевой платформы собирать код прошивки. Вы можете минимальными изменениями в makefile собрать прошивку и крутить её даже на x86.

Вот у меня есть работающий проект прошивки, которая мигает светодиодом для STM32 собранный компилятором GCC. Я хочу пересобрать код прошивки компилятором IAR. IAR хорош за счет того, что делает проверку MISRA, поддерживает горы разных MCU, про которые большинство даже не слышали и имеет развитую систему оптимизации, которая прощает разработчику ряд ошибок программирования.

Вашим основным документом для этого является IAR C/C++ Development Guide Compiling and Linking for Arm Limited’s Arm Cores. Там буквально 736 страниц. В документе расписано, что значит каждая опция компилятора IAR. Обычно в репозитории для IAR лежит проект, состоящий из файлов расширений

Исходные файлы

Пояснение

*.icf

Настройки для компоновщика

*.s

Исходный ассемблер код

.c

Исходный C код

.h

Заголовочные файлы

К слову, ассемблер IAR отличается от ассемблера GCC. Для начала проанализируем лог сборки обыкновенной GUI-IDE. Я сгенерировал в STMCubeMX IAR проект, запустил сборку из-под IDE, дождался окончания и стал внимательно анализировать лог. И вот, что я там увидел.

Что же вызывает IAR-GUI система сборки?

Утилита

Файл на входе

Файл на выходе

Ключи

iasmarm.exe

*.s

*.o

-s+ -M<> -w+ -r --cpu Cortex-M4 --fpu VFPv4_sp

iccarm.exe

*.c

*.o

--no_cse --no_unroll --no_inline --no_code_motion --no_tbaa --no_clustering --no_scheduling --debug --endian=little --cpu=Cortex-M4 -e --fpu=VFPv4_sp
--dlib_config -On

ilinkarm.exe

*.o .icf

.map .out

--semihosting --entry __iar_program_start --vfe --text_out locale

ielftool.exe

.out

.hex

--ihex --verbose

Выглядит так, что IAR ToolChain на самом деле простой и примитивный. Состоит всего из 4х утилит. Все они после установки попадают в папку C:\Program Files (x86)\IAR Systems\Embedded Workbench 8.1\arm\bin. К слову, этот путь надо сразу прописать в переменную PATH.

Программа

Утилита

Ассемблер

iasmarm.exe

Компилятор

iccarm.exe

Компоновщик

ilinkarm.exe

Бинарный парсер (binutils)

ielftool.exe

Схематически процесс сборки c IAR можно изобразить так

Скрипт сборки

В скрипте сборки make надо определить переменные окружения на утилиты, которые и делают всю работу по сборке программы.


# binaries
#IAR_PATH=""
$(info IAR_PATH=$(IAR_PATH))

# The IAR compiler bin path can be either defined in make command via IAR_PATH
# variable (> make IAR_PATH=xxx)
# either it can be added to the PATH environment variable.
ifdef IAR_PATH
    $(info WithPath)
    PREPROCESSOR_TOOL =$(IAR_PATH)/$(PREFIX)iccarm.exe
    CC = $(IAR_PATH)/$(PREFIX)iccarm.exe
    AS = $(IAR_PATH)/$(PREFIX)iasmarm.exe
    ELF_TOOL = $(IAR_PATH)/$(PREFIX)ielftool.exe
    AR = $(IAR_PATH)/$(PREFIX)iarchive.exe
    LD = $(IAR_PATH)/$(PREFIX)ilinkarm.exe
else
    $(info WithOutPath)
    PREPROCESSOR_TOOL = $(PREFIX)iccarm.exe
    CC = $(PREFIX)iccarm.exe
    AS = $(PREFIX)iasmarm.exe
    ELF_TOOL = $(PREFIX)ielftool.exe
    AR = $(PREFIX)iarchive.exe
    LD = $(PREFIX)ilinkarm.exe
endif

MAIN_TARGET_FILE=$(TARGET).out

HEX = $(ELF_TOOL) --ihex --verbose
BIN = $(ELF_TOOL) --bin --verbose  

Это ключевые опции компилятора IAR

Ключ

Назначение

-e

Активировать IARовские расширения языка С

--cpu=Cortex-M4

Выбор процессорного ядра

--fpu=VFPv4_sp

Выбор FPU

--endian=little

Выбор порядка байт

--debug

Добавить отладочную информацию

--no_inline

Запрет вставных функций

в make скрипте это выглядит так



COMPILE_IAR_OPT += -DHAS_IAR
#COMPILE_IAR_OPT += -D HAS_IAR
COMPILE_IAR_OPT += -D__ICCARM__=1
#COMPILE_IAR_OPT += -D __ICCARM__

COMPILE_IAR_OPT +=--no_cse 
COMPILE_IAR_OPT +=--no_unroll 
COMPILE_IAR_OPT +=--no_inline 
COMPILE_IAR_OPT +=--no_code_motion 
COMPILE_IAR_OPT +=--no_tbaa 
COMPILE_IAR_OPT +=--no_clustering 
COMPILE_IAR_OPT +=--no_scheduling 
COMPILE_IAR_OPT +=--debug 
COMPILE_IAR_OPT +=--endian=little 
COMPILE_IAR_OPT +=--cpu=Cortex-M4  
COMPILE_IAR_OPT +=-e 
COMPILE_IAR_OPT +=--fpu=VFPv4_sp

COMPILE_IAR_OPT +=--diag_suppress=Pe029
COMPILE_IAR_OPT +=--diag_suppress=Pe1345
COMPILE_IAR_OPT +=--diag_suppress=Pe513
COMPILE_IAR_OPT +=--diag_suppress=Pe144
COMPILE_IAR_OPT +=--diag_suppress=Pe188

ASM_FLAGS +=  -s+ 
#ASM_FLAGS += -M<> 
ASM_FLAGS += -w+ 
ASM_FLAGS += -r 
ASM_FLAGS += --cpu Cortex-M4 
ASM_FLAGS += --fpu VFPv4_sp 



LDFLAGS +=--semihosting 
LDFLAGS +=--entry __iar_program_start 
LDFLAGS +=--vfe 
LDFLAGS +=--text_out locale 
LDFLAGS +=--redirect _Printf=_PrintfFull 
LDFLAGS +=--redirect _Scanf=_ScanfFull 
LDFLAGS +=--no_out_extension
LDFLAGS +=--map $(BUILD_DIR)/$(TARGET).map
LDFLAGS += --config $(LDSCRIPT)


CFLAGS += $(COMPILE_IAR_OPT)

Основной скрипт сборки

CSTANDARD = -std=c11


mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
$(info mkfile_path:$(mkfile_path) )
#$(info MAKE:$(MAKE))
#$(info MAKEFILE_LIST:$(MAKEFILE_LIST))
MK_PATH:=$(subst /cygdrive/c/,C:/,$(MK_PATH))
$(info MK_PATH=$(MK_PATH))

BUILD_DIR=build

EXTRA_TARGETS=


INCDIR := $(subst /cygdrive/c/,C:/, $(INCDIR))
#@echo $(error INCDIR=$(INCDIR))
#$(error SOURCES_DIAG_C=$(SOURCES_DIAG_C))
#$(error SOURCES_THIRD_PARTY_C=$(SOURCES_THIRD_PARTY_C))
SOURCES_TOTAL_C += $(SOURCES_C)
SOURCES_TOTAL_C += $(SOURCES_DIAG_C)
SOURCES_TOTAL_C += $(SOURCES_CONFIGURATION_C)
SOURCES_TOTAL_C += $(SOURCES_THIRD_PARTY_C)
SOURCES_TOTAL_C := $(subst /cygdrive/c/,C:/, $(SOURCES_TOTAL_C))
#@echo $(error SOURCES_TOTAL_C=$(SOURCES_TOTAL_C))

SOURCES_ASM := $(subst /cygdrive/c/,C:/, $(SOURCES_ASM))
#@echo $(error SOURCES_ASM=$(SOURCES_ASM))

LIBS  := $(subst /cygdrive/c/,C:/, $(LIBS))
LDSCRIPT := $(subst /cygdrive/c/,C:/, $(LDSCRIPT))
#@echo $(error SOURCES_ASM=$(SOURCES_ASM))


WORKSPACE_LOC := $(realpath  $(WORKSPACE_LOC))
WORKSPACE_LOC := $(subst /cygdrive/c/,C:/, $(WORKSPACE_LOC))

include $(WORKSPACE_LOC)/make_scripts/toolchain.mk
# CFLAGS
#https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html
AS_DEFS = 
AS_INCLUDES = 
# OPT_C += -O0

include $(WORKSPACE_LOC)/make_scripts/compiler_options.mk
include $(WORKSPACE_LOC)/make_scripts/compiler_errors.mk
include $(WORKSPACE_LOC)/make_scripts/warning_options.mk
include $(WORKSPACE_LOC)/make_scripts/linker_options.mk

MCAL_OPT += $(OPT_C)
CFLAGS += $(MCAL_OPT)

ASFLAGS += $(MCU)
ASFLAGS += $(AS_DEFS)
ASFLAGS += $(AS_INCLUDES)
ASFLAGS += $(MCAL_OPT)
ASFLAGS += $(COMPILE_OPT)
ASFLAGS += -Wall
ASFLAGS +=-fdata-sections
ASFLAGS += -ffunction-sections

#@echo $(error LDSCRIPT=$(LDSCRIPT))
LIBDIR = 


ARTIFACTS += $(BUILD_DIR)/$(TARGET).bin
ARTIFACTS += $(BUILD_DIR)/$(TARGET).hex
ARTIFACTS += $(BUILD_DIR)/$(MAIN_TARGET_FILE)

#@echo $(error ARTIFACTS=$(ARTIFACTS))


.PHONY: all

# default action: build all 
all: $(EXTRA_TARGETS) $(ARTIFACTS)

.PHONY: generate_definitions

 
# build the application
# list of objects
OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(SOURCES_TOTAL_C:.c=.o)))

vpath %.c $(sort $(dir $(SOURCES_TOTAL_C)))

# list of ASM program objects


#@echo $(error SOURCES_ASM=$(SOURCES_ASM))
ASM_OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(SOURCES_ASM:.s=.o)))
#@echo $(error ASM_OBJECTS=$(ASM_OBJECTS))
ELF_OBJECTS  += $(OBJECTS)


#@echo $(error ASM_OBJECTS=$(ASM_OBJECTS))
ELF_OBJECTS  += $(ASM_OBJECTS)
#@echo $(error ELF_OBJECTS=$(ELF_OBJECTS))

ifeq ($(IAR),Y)
    vpath %.s $(sort $(dir $(SOURCES_ASM)))
endif

TOTAL_FILES := $(words $(OBJECTS))
$(info TOTAL_FILES:$(TOTAL_FILES) )

#@echo $(error COMPILE_GCC_OPT=$(COMPILE_GCC_OPT))
ifeq ($(IAR),Y)
    $(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR) 
	@echo Compile Asm $@
	#@ $(CC) -c -MD $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@
	$(AS) -c  $(ASM_FLAGS)  $< -o $@
endif

$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR) 
	$(eval CURRENT_CNT=$(shell echo $$(($(CURRENT_CNT)+1))))
	@echo Compiling $(CURRENT_CNT)/$(TOTAL_FILES) $@
	#@ $(CC) -c -MD $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@
	$(CC) -c  $(CFLAGS)  $< -o $@

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


#@echo $(error MAIN_TARGET_FILE=$(MAIN_TARGET_FILE))
#@echo $(error LDFLAGS=$(LDFLAGS))
$(BUILD_DIR)/$(MAIN_TARGET_FILE): $(ELF_OBJECTS) Makefile $(BUILD_DIR)
	@echo GenerateMainArtifact $@
	$(LD) $(ELF_OBJECTS) $(LDFLAGS) -o $@
	$(SZ) $@

$(BUILD_DIR)/%.hex: $(BUILD_DIR)/$(MAIN_TARGET_FILE) | $(BUILD_DIR)
	@echo GenerateHex $@
	$(HEX) $< $@

#@echo $(error MAIN_TARGET_FILE=$(MAIN_TARGET_FILE))
$(BUILD_DIR)/%.bin: $(BUILD_DIR)/$(MAIN_TARGET_FILE)   $(BUILD_DIR)
	@echo GenerateBin $@
	$(BIN) $< $@

$(BUILD_DIR):
	mkdir -p $@

# clean up
.PHONY: clean

clean:
	-rm -fR $(BUILD_DIR)

# dependencies
-include $(wildcard $(BUILD_DIR)/*.d)

# *** EOF ***

Чтобы инициировать процесс построения прошивки надо запустить на исполнение файл build_from_make.bat

echo off
cls
call clean_temp.bat
make -i clean 2>&1 | tee clean_log.txt
make -i all  | tee build_log.txt

Тут скрипт файл clean_temp.bat просто делает удаление временных файлов

@echo off

del /S *.o
del /S *.obj
del /S *.d
del /S *.map
del /S *.hex
del /S *.elf
del /S *.lst
del /S *.bin
del /S *.su
del /S *.pp
del /S *.bak

Сравнение GCC и IAR

Между компиляторами GCC и IAR есть серия существенных отличий.

Параметр

GCC

IAR

Файл на выходе

.elf

.out

Компилятор (CC )

gcc

iccarm.exe

Ассемблер (AS)

gcc -x assembler-with-cpp

iasmarm.exe

Бинарный парсер (CP)

objcopy

ielftool.exe

Архиватор (AR )

ar

iarchive.exe

Компоновщик (LD )

gcc

ilinkarm.exe

Скрипт компоновщика

.ld

.icf

Отладка прошивки

Загружать и отлаживать программу можно при помощи Segger Ozone. Выбираем микроконтролер STM32F407VET6, интерфейс JTAG, скорость 4MHz, загружаем *.out файл и можно пошагово отлаживаться.

Как видите, прошивку, которую я собрал из Make скриптов можно прекраснейшим образом пошагово отлаживать в отладчике Ozone.

Итоги

Удалось собрать прошивку для STM32 при помощи компилятора IAR и системы сборки GNU Make. Это открывает дорогу для иcпользования мощных средств заложенных в компилятор IAR (например автоматическая проверка MISRA). Плюс позволяет легко и веерно мигрировать существующий функционал на другие платы и микроконтроллеры. Теперь вы можете забыть про *.ewp файл. Он вам больше не нужен. Всё, что вам надо это .c .h .mk .icf и .s файлы.

Сборка проекта двумя-тремя компиляторами (GCC+ IAR + MinGW) - это хорошая практика при разработке ответственных систем. Если один компилятор не обнаружил ни одной ошибки, то второй компилятор может найти какую-нибудь серьезную ошибку в программе.

Ссылки

Название

URL

How to set up IAR to use GNU make (makefile)?

https://stackoverflow.com/questions/31155117/how-to-set-up-iar-to-use-gnu-make-makefile

Конвеерум #30: Эволюция рабочего окружения для embedded разработки

https://www.youtube.com/watch?v=vmuO4bHjTSo&t=7s

CI/CD прошивок для микроконтроллеров в Wiren Board ( начало на 25:20)

https://www.youtube.com/watch?v=HEEVxZ4rBCo

Почему Сборка с Помощью GUI-IDE — это Тупиковый Путь

https://habr.com/ru/articles/794206/

Эффективное использование GNU Make

https://www.opennet.ru/docs/RUS/gnumake/

Основы по GNU Make

https://habr.com/ru/articles/748162/

Статический Анализ С-кода

https://habr.com/ru/articles/770506/

Почему важно собирать код из скриптов

https://habr.com/ru/articles/723054/

IAR Eclipse Setup Guide Part 3 — Makefile Project

https://www.jblopen.com/iar-eclipse-setup-guide-part-3-makefile-project/

Дымовая Завеса в Eclipse IDE

https://habr.com/ru/articles/835994/

EWARM_DevelopmentGuide.ENU.pdf

https://library.tsilikin.ru/Техника/Программирование/Embedded system/IAR/EWARM_DevelopmentGuide.ENU.pdf

Сборка firmware для CC2652 из Makefile

https://habr.com/ru/articles/726352/

Техникум: Автоматическое Aрхивирование Aртефактов

https://habr.com/ru/articles/826730/

Интеграция утилиты Artistic Style в скрипт сборки прошивки

https://habr.com/ru/articles/1004610/

Интеграция clang-format в Процесс Сборки

https://habr.com/ru/articles/833500/

Progress Bar для Сборки Программы

https://habr.com/ru/articles/966248/

Настройка ToolChain(а) для Win10+GCC+С+Makefile+ARM Cortex-Mx+GDB

https://habr.com/ru/articles/673522/

Интеграция Стилистического Анализа в общий Make Скрипт Сборки Проекта

https://habr.com/ru/articles/843746/

Обновление Прошивки из Make Скрипта

https://habr.com/ru/articles/857416/

IAR Embedded Workbench & Makefiles

https://www.jblopen.com/iar-embedded-workbench-makefiles/

Вопросы

--Зачем в make скриптах нужно ключевое слово VPATH? VPATH позволяет держать объектные файлы отдельно от исходников.

--Как отлаживать прошивку, если она заклинивает при старте и нет возможности воспользоваться пошаговым отладчиком? Использовать GPIO toggle и осциллограф.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Вы работали с компилятором IAR?
69.23%да9
30.77%нет4
Проголосовали 13 пользователей. Воздержался 1 пользователь.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Каким способом Вы собираете код компилятором IAR?
66.67%Из-под IAR-GUI-IDE.8
16.67%Из скриптов Make2
0%Из скриптов CMake0
0%Из скриптов Ninja0
16.67%другое2
Проголосовали 12 пользователей. Воздержались 3 пользователя.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Каким компилятором Вы собираете прошивки для STM32?
81.25%GCC13
37.5%IAR6
12.5%Clang2
6.25%GHS1
0%TCC0
25%Keil4
0%другой компилятор0
Проголосовали 16 пользователей. Воздержался 1 пользователь.