Новости и статьи

Среда разработки встроенных систем или дистрибутивов на основе GNU/Linux. Здесь вы можете найти новости проекта, статьи и заметки, касающиеся разработки дистрибутива Radix cross Linux.

Система сборки (подключение инструментов)

27 ноября 2024 г.

В этой статье мы рассмотрим, как подключить к системе сборки новый toolchain и использовать его в работе над проектом простого приложения для платы Longan Nano на базе микроконтроллера GD32VF103CBT6 от компании GigaDevice.

Build System

Система сборки создавалась для обеспечения параллельной сборки множества прошивок для линеек различных устройств, построенных на CPU с разными архитектурами. Однако если возникает необходимость, к ней можно подключать новые устройства. Именно этот процесс мы и рассмотрим.

Ведь у каждого Bare-metal разработчика наверняка в шкафу лежит масса всяческих плат и проводочков, но для каждого из них проекты исходников разбросаны по разным каталогам и найти нужный со временем становится все труднее и труднее.

Если в проектах, основанных на Yocto или BuildRoot, вы поставляете свой код в чужой репозиторий, то здесь наоборот, вы подключаете систему сборки к своему дереву каталогов, где хранятся ваши личные проекты. Одним словом, система сборки дает вам несколько иной уровень независимости, ну и про импортозамещение тоже забывать нельзя.

tags

Подключение инструмента

Для добавления нового целевого устройства в систему сборки необходимо выполнить три простых действия путем редактирования файлов 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.

Fig.1. Longan Nano with SiPEED Adapter.

Для прошивания устройства 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/.

Литература:


Если вам понадобится дополнительная информация о системе сборки, пишите: