Система сборки (подключение инструментов)
В этой статье мы рассмотрим, как подключить к системе сборки новый toolchain и использовать его в работе над проектом простого приложения для платы Longan Nano на базе микроконтроллера GD32VF103CBT6 от компании GigaDevice.
Система сборки создавалась для обеспечения параллельной сборки множества прошивок для линеек различных устройств, построенных на CPU с разными архитектурами. Однако если возникает необходимость, к ней можно потключать новые устройства. Именно этот процесс мы и рассмотрим.
Ведь у каждого Bare-metal разработчика наверняка в шкафу лежит масса всяческих плат и проводочков, но для каждого из них проекты исходников разбросаны по разным каталогам и найти нужный со временем становится все труднее и труднее.
Если в проектах, основанных на Yocto или BuildRoot, вы поставляете свой код в чужой репозиторий, то здесь наоборот, вы подключаете систему сборки к своему дереву каталогов, где хранятся ваши личные проекты. Одним словом, система сборки дает вам несколько иной уровень независимости, ну и про импортозамещение тоже забывать нельзя.
Подключение инструмента
Для добавления нового целевого устройства в систему сборки необходимо выполнить три простых действия путем редактирования файлов constants.mk и build-config.mk.template:
Описать новое устройство, присвоив ему уникальное имя и идентификатор, в файле constants.mk:
####### RISC-V devices:
HARDWARE_LONGAN_NANO = longan-nano
### |---HW-spec-handy-ruler-----------------------|
LONGAN_NANO_SPEC = GigaDevice GD32VF103 \(Newlib based\)
LONGAN_NANO_USE_BUILT_GCC_LIBS = no
LONGAN_NANO_ENABLE_STATIC = yes
LONGAN_NANO_ID_STD = 0500
Дать описание toolchain‑а (если его еще нет в системе):
# ======= RISCV-NEWLIB =======================================================
TOOLCHAIN_RISCV_NEWLIB = riscv-newlib
RISCV_NEWLIB_ARCH = riscv64-none-elf
RISCV_NEWLIB_VERSION = 1.11.3
RISCV_NEWLIB_DIR = riscv64-NONE-elf-newlib
RISCV_NEWLIB_PATH = $(TOOLCHAINS_BASE_PATH)/$(RISCV_NEWLIB_DIR)
RISCV_NEWLIB_TARBALL = $(TOOLCHAINS_FTP_BASE)/$(RISCV_NEWLIB_VERSION)/$(RISCV_NEWLIB_DIR)-$(RISCV_NEWLIB_VERSION).$(TARBALL_SUFFIX)
RISCV_NEWLIB_ARCH_DEFS = -D__RISCV_NEWLIB__=1
RISCV_NEWLIB_ARCH_FLAGS =
RISCV_NEWLIB_HARDWARE_VARIANTS := $(HARDWARE_LONGAN_NANO)
Разрешить сборку путем добавления переменной в файл build-config.mk.template:
# TARGET: riscv64-none-elf:
ENABLE_LONGAN_NANO = true
Полный `svn diff` представлен на следующем листинге:
Index: build-config.mk.template
===================================================================
--- build-config.mk.template (revision 67)
+++ build-config.mk.template (working copy)
@@ -81,6 +81,9 @@
# TARGET: arm-at91sam7s-eabi:
ENABLE_AT91S = true
+# TARGET: riscv64-none-elf:
+ENABLE_LONGAN_NANO = true
+
# TARGET: arm-imx6-linux-gnueabihf:
ENABLE_NIT6Q = true
ENABLE_OKMX6DL_C = false
Index: constants.mk
===================================================================
--- constants.mk (revision 67)
+++ constants.mk (working copy)
@@ -309,8 +309,15 @@
AT91S_USE_BUILT_GCC_LIBS = no
AT91S_ENABLE_STATIC = yes
+####### RISC-V devices:
+HARDWARE_LONGAN_NANO = longan-nano
+### |---HW-spec-handy-ruler-----------------------|
+LONGAN_NANO_SPEC = GigaDevice GD32VF103 \(Newlib based\)
+LONGAN_NANO_USE_BUILT_GCC_LIBS = no
+LONGAN_NANO_ENABLE_STATIC = yes
+
####### i.MX6 devices:
####### -------------
@@ -666,6 +673,7 @@
CB3X_ID_STD = 0301
AT91S_ID_STD = 0400
+ LONGAN_NANO_ID_STD = 0500
NIT6Q_ID_STD = 0601
OKMX6DL_C_ID_STD = 0602
@@ -1153,7 +1161,23 @@
AT91SAM7S_NEWLIB_HARDWARE_VARIANTS := $(HARDWARE_AT91S)
+# ======= RISCV-NEWLIB =======================================================
+TOOLCHAIN_RISCV_NEWLIB = riscv-newlib
+
+RISCV_NEWLIB_ARCH = riscv64-none-elf
+RISCV_NEWLIB_VERSION = 1.11.3
+RISCV_NEWLIB_DIR = riscv64-NONE-elf-newlib
+RISCV_NEWLIB_PATH = $(TOOLCHAINS_BASE_PATH)/$(RISCV_NEWLIB_DIR)
+RISCV_NEWLIB_TARBALL = $(TOOLCHAINS_FTP_BASE)/$(RISCV_NEWLIB_VERSION)/$(RISCV_NEWLIB_DIR)-$(RISCV_NEWLIB_VERSION).$(TARBALL_SUFFIX)
+
+RISCV_NEWLIB_ARCH_DEFS = -D__RISCV_NEWLIB__=1
+RISCV_NEWLIB_ARCH_FLAGS =
+
+RISCV_NEWLIB_HARDWARE_VARIANTS := $(HARDWARE_LONGAN_NANO)
+
+
+
# ======= IMX6-GLIBC ======================================================
TOOLCHAIN_IMX6_GLIBC = imx6-glibc
Переменная RISCV_NEWLIB_HARDWARE_VARIANTS содержит список целевых устройств, которые требуют наличия toolchain‑а riscv-newlib. Поэтому, при добавлении нового устройства, для которого будет нужен toolchain riscv-newlib, достаточно добавить его имя в список RISCV_NEWLIB_HARDWARE_VARIANTS:
RISCV_NEWLIB_HARDWARE_VARIANTS := $(HARDWARE_LONGAN_NANO) $(HARDWARE_NEW_SUPER_MEGA_DEVICE)
Здесь важно отметить следующее.
Поскольку toolchain riscv-newlib позволяет собирать код для целого ряда вариантов архитектур CPU, мы оставили значение переменной RISCV_NEWLIB_ARCH_FLAGS пустым. Сделано это для того, чтобы при создании сценария сборки можно было переопределить ARCH_FLAGS, в зависимости от целевой архитектуры, прямо в сценарии так, чтобы эти флаги не фигурировали глобально и не влияли на другие устройства, использующие этот toolchain.
Данный подход разрешен системой сборки, поскольку toolchain‑ы такого рода не являются основными и предназначены либо для сборки обособленных проектов, либо как вспомогательные для сборки firmware встроенных в SoC микроконтроллеров. Так, например, во время сборки u-boot на Amlogic s805 (ARM Cortex-A5), помимо основного компилятора, требуется еще и toolchain arc-S8XX-elf32-newlib (архитектура ARC).
Сборка проекта
Перед началом работы, как всегда [предыдущая статья], необходимо загрузить исходный код системы сборки и проекта для нового устройства:
svn checkout svn://radix-linux.su/radix/build-system/branches/build-system-1.11.x build-system
svn checkout svn://radix-linux.su/radix/system/branches/radix-1.11/doc doc
Приготовить систему сборки к работе можно с помошью следующих комманд:
cd build-system
make -j16
Исходный проект для устройства Longan Nano (GD32VF103CBT6) находится в каталоге doc/examples/hello-longan/.
Здесь, Makefile — представляет собой сценарий сборки, а в подкаталоге src/ находится наш исходный проект, в котором даны два примера: hello_led и hello_display. Первый показывает как можно помигать светодиодами, а второй — как работать с LCD-дисплеем.
Оба проекта имеют собственные Make-файлы, построенные с учетом парадигмы GNU, то есть они подразумевают наличие переменных окружения: CC, OBJCOPY, SIZE, …, так как это принято:
doc/examples/hello-longan/hello_led/Makefile:
# GD32VF103 Linker Script:
LDFLAGS += -T../device/gd32vf103xb.ld
# Source files:
AS_SRC = ../device/gd32vf103xb_boot.S
C_SRC = main.c
C_SRC += ../device/n200_func.c
# Header file directories:
INCLUDE = -I../device/include
# Object files to build:
OBJS = $(AS_SRC:.S=.o)
OBJS += $(C_SRC:.c=.o)
# Default rule to build the whole project:
.PHONY: all
all: main.bin
# Rule to build assembly files:
%.o: %.S
$(CC) -x assembler-with-cpp -c $(CFLAGS) $(INCLUDE) $< -o $@
# Rule to compile C files:
%.o: %.c
$(CC) -c $(CFLAGS) $(INCLUDE) $< -o $@
# Rule to create an ELF file from the compiled object files:
main.elf: $(OBJS)
$(CC) $^ $(LDFLAGS) -o $@
# Rule to create a raw binary file from an ELF file:
main.bin: main.elf
$(OBJCOPY) -S -O binary $< $@
$(SIZE) $<
# Rule to clear out generated build files:
.PHONY: clean
clean:
rm -f $(OBJS)
rm -f main.elf
rm -f main.bin
В данном Make-файле не определены переменные CC, OBJCOPY, SIZE, так как подразумевается возможность сборки с помощью разных toolchain‑в.
Точно также организовано большинство GNU-проектов, основанных на Autoconf/Automake. Такой подход обеспечивает переносимость проектов GNU на большое количество архитектур. Главным приемуществом такого подхода является то, что все управления компилятором, как и путь к самому компилятору, передаются посредством переменных окружения (CC, OBJCOPY, SIZE). Имена этих переменных фиксированы и, в отличие от CMake, Meson, Qmake, пользователю не приходится составлять так называемые cross‑файлы, в которых необходимо определять условия сборки (или, хуже того, править ошибки в оригинальных управляющих файлах сторонних проектов).
В нашем случае все эти переменные будут поставляться автоматически системой сборки.
Сценарий сборки
Рассмотрим теперь сценарий сборки этого простого проекта для микроконтроллера GD32VF103CBT6:
doc/examples/hello-longan/Makefile:
COMPONENT_TARGETS = $(HARDWARE_LONGAN_NANO)
include ../../../build-system/constants.mk
# ======= __END_OF_REQUIRES__ =======
sources = src
SRC_DIR = $(TARGET_BUILD_DIR)/src
src_done = $(TARGET_BUILD_DIR)/.source_done
led_target = $(TARGET_BUILD_DIR)/led.bin
display_target = $(TARGET_BUILD_DIR)/display.bin
info_target = $(TARGET_BUILD_DIR)/.info-done
USE_TARGET_DEST_DIR_SYSROOT = no
ifeq ($(HARDWARE),$(HARDWARE_LONGAN_NANO))
OPTIMIZATION_FLAGS = -O0
ARCH_FLAGS := -march=rv32imac_zicsr -mabi=ilp32 -mcmodel=medlow
endif
BUILD_TARGETS = $(led_target) $(display_target) $(info_target)
include ../../../build-system/core.mk
ifeq ($(HARDWARE),$(HARDWARE_LONGAN_NANO))
CFLAGS += -Wall -fmessage-length=0 --specs=nosys.specs
LDFLAGS += -Wall $(ARCH_FLAGS) -Wl,--no-relax -Wl,--gc-sections -nostdlib -nostartfiles -lc -lgcc --specs=nosys.specs
endif
$(src_done): $(sources)
@cp -a $^ $(TARGET_BUILD_DIR)/
@touch $@
$(led_target): $(src_done)
@( cd $(SRC_DIR)/hello_led/ ; $(BUILD_ENVIRONMENT) $(MAKE) )
@cp -a $(SRC_DIR)/hello_led/main.bin $@
@touch $@
$(display_target): $(src_done)
@( cd $(SRC_DIR)/hello_display/ ; $(BUILD_ENVIRONMENT) $(MAKE) )
@cp -a $(SRC_DIR)/hello_display/main.bin $@
@touch $@
$(info_target): $(led_target) $(display_target)
@echo "==================================="
@echo "======= Build Info: ======="
@echo "==================================="
@echo "======= CC = '$(CC)'"
@echo "======= CFLAGS = '$(CFLAGS)'"
@echo "======= LDFLAGS = '$(LDFLAGS)'"
@echo "======= ARCH_FLAGS = '$(ARCH_FLAGS)'"
@echo ""
@echo "#"
@echo "# Please find images: `basename $(led_target)`, `basename $(display_target)` in the $(TARGET_BUILD_DIR)/ directory."
@echo "#"
@echo ""
@touch $@
Вот таким образом можно создавать собственные сценарии сборки.
Переменная COMPONENT_TARGETS задает список целевых устройств, для которых пригоден текущий сценарий сборки.
Переменная BUILD_TARGETS также является одной из ключевых переменных системы сборки. Она определяет список целей, которые надо выполнить в рамках сценария.
Директивы:
include ../../../build-system/constants.mk
include ../../../build-system/core.mk
подключают систему сборки.
Все остальные зависимости, пользователь может выстраивать как в обычном Make-файле. Таким образом, в каталоге doc/examples/hello-longan/ работа ни чем не отличается от той, которую разработчик ведет на персональной машине с использованием родного компилятора.
Система сборки предоставляет ему все необходимое окружение для cross‑компиляции посредством переменной BUILD_ENVIRONMENT, значением которой является всё необходимое окружение.
Здесь важно обратить внимание на блоки:
ifeq ($(HARDWARE),$(HARDWARE_LONGAN_NANO))
. . .
endif
Мы ранее говорили о том, что toolchain riscv-newlib является универсальным и мы сознательно не задали значение переменной RISCV_NEWLIB_ARCH_FLAGS в файле build-system/constants.mk.
Поэтому в нашем сценарии мы самостоятельно задаем значение переменной ARCH_FLAGS и дополняем значения переменных CFLAGS и LDFLAGS.
В случае обычных проектов, использующих основные целевые устройства, нам не приходится вручную определять флаги управления, так как все необходимые параметры уже определены.
Если в каталоге doc/examples/hello-longan/ выполнить:
make help
то на экран будет выведена подсказка по доступным командам, а также список целевых устройств, для которых предназначен данный сценарий сборки.
Для нашего сценария нам достаточно команд:
make local_all
make local_clean
Если же речь идет о более сложных проектах, то система сборки позволяет автоматизировать такие действия, как:
загрузка и распаковка исходных пакетов программ;
наложение патчей;
сборка программ и пакетов программ для инсталляции в rootfs целевой OS;
параллельная сборка системы сразу на множество целевых машин;
ускорение повторных сборок посредством ccache;
отслеживание межпакетных зависимостей;
создание списков инсталляции пакетов;
создание загрузочных образов;
построение деревьев межпакетных зависимостей;
разделение больших проектов на подмножества, подобно Yocto layers;
. . .
По сути весь репозиторий radix-1.9 является примером использования системы сборки, насчитывающим более тысячи уроков.
Но вернемся к нашему проекту для Longan Nano.
Для сборки прошивок led.bin и display.bin достаточно, в каталоге doc/examples/hello-longan/ выполнить команду:
make
Результат будет находиться в каталоге $(TARGET_BUILD_DIR)/.
Прошивка
Прежде всего необходимо подключить адаптер SiPEED к плате Longan Nano, подключить разъем USB-C к источнику питания или компьютеру, подключить USB адаптер SiPEED к компьютеру, где установлены пакеты gd32-dfu-utils, riscv-openocd.
Рис. 1. Longan Nano с адаптером SiPEED.
Для прошивания устройства Longan Nano, надо подключить оба USB-разъема к компьютеру. После этого нажать на устройстве кнопку BOOT, и, удерживая ее, кратко нажать и отпустить кнопку RESET, затем отпустить кнопку BOOT.
Проверить связь можно с помощью команды:
sudo dfu-util -l
которая выведет на экран примерно следующее:
dfu-util 0.9
Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2016 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/
Found DFU: [28e9:0189] ver=0100, devnum=24, cfg=1, intf=0, path="5-1.2", alt=1, name="@Option Bytes /0x1FFFF800/01*016Be", serial="3CBJ"
Found DFU: [28e9:0189] ver=0100, devnum=24, cfg=1, intf=0, path="5-1.2", alt=0, name="@Internal Flash /0x08000000/128*001Kg", serial="3CBJ"
Теперь можно прошить устройство, например, так:
sudo dfu-util -d 28e9:0189 -a 0 --dfuse-address 0x08000000:leave -D .riscv-newlib/longan-nano/display.bin
После этого можно отключить USB-разъем адаптера SiPEED, а также разъем USB-C, питающий плату Longan Nano.
Наше устройство успешно прошито и, при подаче питания на USB-C разъем, экран будет последовательно заливаться разными цветами в бесконечном цикле.
Для тех, кто впервые знакомится с RISC-V микроконтроллерами, мы приготовили пакеты gd32-dfu-utils и riscv-openocd, которые можно найти на FTP-сервере. Краткое руководство по прошивке Longan Nano представлено в README.md файле в корне каталога doc/examples/hello-longan/.
Литература:
Если вам понадобится дополнительная информация о системе сборки, пишите:
Enjoy.