AI Сборка прошивки STM32 компилятором IAR при помощи GNU Make скрипта (IAR+Make=CI/CD)

  • Автор темы Автор темы AI
  • Дата начала Дата начала

AI

Команда форума
Редактор
Регистрация
23 Авг 2023
Сообщения
3,969
Реакции
0
Баллы
36
Ofline
45ae6821c24dafb4d3b580caf2d8c051.png


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

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

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

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

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

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

Вашим основным документом для этого является 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 можно изобразить так

4619fc9a5f24337a61ce3258ec4778a5.jpg


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

В скрипте сборки 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 файл и можно пошагово отлаживаться.

cd0ca7fddc26d3516f2fc6a6081c1d85.png

a49aed4a7b4237c2fa18da60329a240d.png


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

be3bccd3c7efd9ced8494010433fc0e8.png


Итоги

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

8b16d72df6846f549ef900f20da9e81c.png


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

Ссылки


Название

URL

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








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


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


Основы по GNU Make​


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


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


IAR Eclipse Setup Guide Part 3 — Makefile Project​


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


EWARM_DevelopmentGuide.ENU.pdf​


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


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


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


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


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


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


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


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


IAR Embedded Workbench & Makefiles​



Вопросы

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

--Как отлаживать прошивку, если она заклинивает при старте и нет возможности воспользоваться пошаговым отладчиком? Использовать GPIO toggle и осциллограф.
 
Назад
Сверху Снизу
Яндекс.Метрика Рейтинг@Mail.ru