compiler creation with crosstool-ng

Наверное каждый, кто занимается программированием FPGA однажды задумывался о создании своего собственного процессора. Дело это несомненно интересное. Найти и использовать готовые реализации процессоров уже сейчас довольно просто. Сама компания Intel предлагает использовать в проектах софт процессор Nios. Компания Wave Computing дает исходные тексты процессора MIPS. Есть opensource процессор ARM v2a, у нас в блоге целый раздел ему посвящен. Есть OpenRisc и другие.

Я и сам постоянно думаю о создании своего собственного процессора (нетрадиционной архитектуры, с динамически переконфигурируемым ядром).

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

Что такое toolchain? Это набор программ, утилит для преобразования текста программы в исполняемый код. Tool - инструмент, chain - цепочка. Предполагается набор утилит для преобразования файлов. Компилятор создает из текста программы на языке C/C++ ассемблерный текст. Этот файл передается следующему звену - ассемблеру, результат ассемблера передается линковщику и так далее по цепочке.

Часто используют cross-toolchain, это когда к примеру программа компилируется на персональном компьютере с процессором архитектуры x86-64, а результирующий бинарный код должен исполняться на процессоре другой архитектуры, например, ARM или MIPS. Тут вообще-то бывает несколько случаев, тулчейны создаются под заданную "триаду": build / host / target. Build - на какой платформе собирается тулчейн, Host - на какой платформе он будет работать, Target - под какую целевую платформу тулчейн будет собирать итоговую программу. Самые распространенные случаи x86-64/x86-64/arm или x86-64/x86-64/aarch64 или x86-64/x86-64/mips. То есть тулчен генерируют и используют обычно на интел совместимых персональных компьютерах, а результат компиляции запускают на arm или aarch64 или mips, nios, zinq.

Как же создается такой инструмент toolchain? Где его взять? Вопрос конечно интересный. Обычно производители процессоров сами и выпускают к нему компилятор/тулчейн. Есть компании, которые дают готовые тулчейны для известных архитектур процессоров, например, вот компания Bootlin. Выбирайте архитектуру процессора (список впечатляет), и тип подключаемой библиотеки C: glibc/musl/uclibc и скачивайте.

Однако, существует и opensource платформа crosstool-ng для генерации своего собственного toolchain в среде Linux, но опять же только для известных архитектур. Crosstool-ng строит тулчейн с использованием GCC, GNU Compiler Collection - набор компиляторов C, C++, Objective-C, Fortran, Ada, Go, D и стандартных библиотек типа libstd++ и других.

Что же там такого хитрого в тулчейне? Если речь идет о компиляторе для barebone, чистого кода, для работы без операционной системы (в микроконтроллере или начальный загрузчик SoC), то это проще: тут можно и самому написать простейший компилятор, хотя бы просто ассемблер, и минимальные библиотеки к нему. А вот если скомпилированная программа должна работать под управлением операционной системы, то тут все сложнее.

Почти все трудности возникают из-за необходимости использования стандартных библиотек: выделение памяти, функции файлового ввода-вывода, многопоточность и синхронизация, сетевые функции. То есть библиотека должна знать как взаимодействовать с ядром. А компилятор должен знать, как взаимодействовать с билиотекой libc. Тут возникают циклические зависимости: GCC должен уметь использовать libc, а для сборки libc необходим GCC. Поэтому, при построении тулчейна конечный компилятор может собираться в несколько этапов! Первый раз собирается только минимальный компилятор "core pass 1 compiler" с помощью которого извлекаются заголовки ядря Linux и генерируются стартовые минимальные run-time библиотеки C. С этими библиотеками собирается второй компилятор "core pass 2 compiler" и с его помощью компилируются полная библиотека C. Уже после этого собирается целевой компилятор для Target.

Это еще не все. Для полного тулчейна могут понадобиться библиотеки:

  • GMP - арифметика с заданной точностью, GNU Multiple Precision Arithmetic Library;
  • MPFR - библиотека C, плавающая точка с заданной точностью и округлением, multiple-precision floating-point computations with correct rounding;
  • MPC - библиотека C для комплексных чисел, complex numbers.

Для поддержки оптимизации циклов (Loop Optimisation, GRAPHITE) и оптимизации на этапе линковки (Link Time Optimisation, LTO) нужны библиотеки:

  • PPL - Parma Polyhedra Library;
  • ISL - Integer Set Library;
  • CLooG/PPL - Chunky Loop Generator.

Еще:

  • libelf - поддержка LTO, доступ к объектным файлам типа ELF;
  • elf2flt - утилита создания "плоских" исполняемых файлов;
  • gettext/libiconv - библиотеки локализации и поддержки различных языков.

Ну и наверняка еще многое другое, про которое я не знаю или забыл написать. Во всяком случае, инструмент crosstool-ng знает, какие библиотеки нужны для основных платформ и сам может создать правильную конфигурацию. Кроме того, есть средства менять настройки, если это очень нужно. Но это уже тонкая настройка, этого касаться не буду.

Ну и что нам со всем этим знанием делать?
Рассмотрим практическую задачу: у нас на сайте есть раздел, рассказывающий на про систему на кристалле AMBER, ARM v2a. Там есть статья "Установка кросс-компилятора ARM". В статье рассказывается откуда скачать компилятор, но к сожалению, ссылка на страницу скачивания устарела и более не работает. То есть тулчейн Sourcery Codebench Lite Edition на самом деле скачать не откуда.

Попробую сам сгенерировать toolchain для системы Amber ARM v2a (все нижеописанное я делаю на Linux Ubuntu 18.0 в командной строке терминала):

1. Скачиваю инструмент crosstool-ng со страницы http://crosstool-ng.org/download/crosstool-ng/
Я сзял файл crosstool-ng-1.24.0.tar.bz2

2. распаковываю файл архива и перехожу в папку командами
tar -xf crosstool-ng-1.24.0.tar.bz2
cd crosstool-ng-1.24.0/

3. конфигурирую и собираю сам crosstool-ng ( флаг enable-local нужен, чтобы использовать инструмент прямо в локальной папке, без установки в систему)
./configure --enable-local
make

4. смотрим какие процессоры, библиотеки и EABI поддерживает crosstool-ng
./ct-ng list-samples
Status Sample name
[L...] aarch64-rpi3-linux-gnu
[L..X] aarch64-unknown-linux-android
[L...] aarch64-unknown-linux-gnu
[L...] aarch64-unknown-linux-uclibc
[L...] alphaev56-unknown-linux-gnu
[L...] alphaev67-unknown-linux-gnu
[L...] arc-arc700-linux-uclibc
[L...] arc-multilib-elf32
[L...] arc-multilib-linux-uclibc
[L...] arm-bare_newlib_cortex_m3_nommu-eabi
[L...] arm-cortex_a15-linux-gnueabihf
[L..X] arm-cortexa5-linux-uclibcgnueabihf
[L...] arm-cortex_a8-linux-gnueabi
[L..X] arm-cortexa9_neon-linux-gnueabihf
[L..X] x86_64-w64-mingw32,arm-cortexa9_neon-linux-gnueabihf
[L...] armeb-unknown-eabi
[L...] armeb-unknown-linux-gnueabi
[L...] armeb-unknown-linux-uclibcgnueabi
[L...] arm-multilib-linux-uclibcgnueabi
[L...] arm-nano-eabi
[L...] arm-unknown-eabi
[L...] arm-unknown-linux-gnueabi
[L..X] arm-unknown-linux-musleabi
[L...] arm-unknown-linux-uclibcgnueabi
[L..X] arm-unknown-linux-uclibcgnueabihf
[L...] armv6-nommu-linux-uclibcgnueabi
[L...] armv6-rpi-linux-gnueabi
[L...] armv7-rpi2-linux-gnueabihf
[L...] armv8-rpi3-linux-gnueabihf
[L...] avr
[L...] i586-geode-linux-uclibc
[L...] i686-centos6-linux-gnu
[L...] i686-centos7-linux-gnu
[L...] i686-nptl-linux-gnu
[L...] i686-ubuntu12.04-linux-gnu
[L...] i686-ubuntu14.04-linux-gnu
[L...] i686-ubuntu16.04-linux-gnu
[L..X] i686-w64-mingw32
[L...] m68k-unknown-elf
[L...] m68k-unknown-uclinux-uclibc
[L...] powerpc-unknown-linux-uclibc,m68k-unknown-uclinux-uclibc
[L...] mips64el-multilib-linux-uclibc
[L...] mips-ar2315-linux-gnu
[L...] mipsel-multilib-linux-gnu
[L...] mipsel-sde-elf
[L...] mipsel-unknown-linux-gnu
[L...] mips-malta-linux-gnu
[L...] mips-unknown-elf
[L...] mips-unknown-linux-uclibc
[L..X] moxiebox
[L..X] moxie-unknown-elf
[L..X] x86_64-multilib-linux-uclibc,moxie-unknown-moxiebox
[L..X] msp430-unknown-elf
[L...] nios2-altera-linux-gnu
[L..X] i686-w64-mingw32,nios2-spico-elf
[L...] nios2-unknown-elf
[L...] powerpc-405-linux-gnu
[L...] powerpc64le-unknown-linux-gnu
[L...] powerpc64-multilib-linux-gnu
[L...] powerpc64-unknown-linux-gnu
[L...] powerpc-8540-linux-gnu
[L...] powerpc-860-linux-gnu
[L...] powerpc-e300c3-linux-gnu
[L...] powerpc-e500v2-linux-gnuspe
[L...] x86_64-multilib-linux-uclibc,powerpc-unknown-elf
[L...] powerpc-unknown-linux-gnu
[L...] powerpc-unknown-linux-uclibc
[L...] powerpc-unknown_nofpu-linux-gnu
[L..X] riscv32-hifive1-elf
[L..X] riscv32-unknown-elf
[L..X] riscv64-unknown-elf
[L..X] riscv64-unknown-linux-gnu
[L..X] s390-ibm-linux-gnu
[L...] s390x-ibm-linux-gnu
[L...] sh-multilib-linux-gnu
[L...] sh-multilib-linux-uclibc
[L...] sh-unknown-elf
[L...] sparc64-multilib-linux-gnu
[L...] sparc-leon-linux-uclibc
[L...] sparc-unknown-linux-gnu
[L...] x86_64-centos6-linux-gnu
[L...] x86_64-centos7-linux-gnu
[L...] x86_64-multilib-linux-gnu
[L..X] x86_64-multilib-linux-musl
[L...] x86_64-multilib-linux-uclibc
[L..X] x86_64-w64-mingw32,x86_64-pc-linux-gnu
[L...] x86_64-ubuntu12.04-linux-gnu
[L...] x86_64-ubuntu14.04-linux-gnu
[L...] x86_64-ubuntu16.04-linux-gnu
[L...] x86_64-unknown-linux-gnu
[L...] x86_64-unknown-linux-uclibc
[L..X] x86_64-w64-mingw32
[L..X] xtensa-fsf-elf
[L...] xtensa-fsf-linux-uclibc
L (Local) : sample was found in current directory
G (Global) : sample was installed with crosstool-NG
X (EXPERIMENTAL): sample may use EXPERIMENTAL features
B (BROKEN) : sample is currently broken
O (OBSOLETE) : sample needs to be upgraded

5. выбираю архитектуру ARM, пусть будет arm-unknown-eabi и собираю тулчейн
./ct-ng arm-unknown-eabi
./ct-ng build

6. Смотрим на результат в папке ~/x-cross
ls ~/x-tools/arm-unknown-eabi/ -l
total 748
dr-xr-xr-x 7 nick nick 4096 Jul 26 05:02 arm-unknown-eabi
dr-xr-xr-x 2 nick nick 4096 Jul 26 05:12 bin
-r--r--r-- 1 nick nick 738949 Jul 26 05:12 build.log.bz2
dr-xr-xr-x 2 nick nick 4096 Jul 26 05:12 include
dr-xr-xr-x 4 nick nick 4096 Jul 26 05:12 lib
dr-xr-xr-x 3 nick nick 4096 Jul 26 05:12 libexec
dr-xr-xr-x 4 nick nick 4096 Jul 26 05:12 share

здесь в папке lib будут все собранные для тулчейна библиотеки, в папке include - все заголовочные файлы, ну и в папке bin - все компиляторы и утилиты:

ls ~/x-tools/arm-unknown-eabi/bin -l
total 90332
-r-xr-xr-x 1 nick nick 5124736 Jul 26 04:54 arm-unknown-eabi-addr2line
-r-xr-xr-x 2 nick nick 5356128 Jul 26 04:54 arm-unknown-eabi-ar
-r-xr-xr-x 2 nick nick 8214568 Jul 26 04:54 arm-unknown-eabi-as
-r-xr-xr-x 2 nick nick 1104712 Jul 26 05:12 arm-unknown-eabi-c++
lrwxrwxrwx 1 nick nick 20 Jul 26 05:12 arm-unknown-eabi-cc -> arm-unknown-eabi-gcc
-r-xr-xr-x 1 nick nick 5075792 Jul 26 04:54 arm-unknown-eabi-c++filt
-r-xr-xr-x 1 nick nick 1100616 Jul 26 05:12 arm-unknown-eabi-cpp
-r-xr-xr-x 1 nick nick 4095 Jul 26 04:50 arm-unknown-eabi-ct-ng.config
-r-xr-xr-x 1 nick nick 266264 Jul 26 04:54 arm-unknown-eabi-elfedit
-r-xr-xr-x 2 nick nick 1104712 Jul 26 05:12 arm-unknown-eabi-g++
-r-xr-xr-x 2 nick nick 1100616 Jul 26 05:12 arm-unknown-eabi-gcc
-r-xr-xr-x 2 nick nick 1100616 Jul 26 05:12 arm-unknown-eabi-gcc-8.3.0
-r-xr-xr-x 1 nick nick 27096 Jul 26 05:12 arm-unknown-eabi-gcc-ar
-r-xr-xr-x 1 nick nick 27096 Jul 26 05:12 arm-unknown-eabi-gcc-nm
-r-xr-xr-x 1 nick nick 27096 Jul 26 05:12 arm-unknown-eabi-gcc-ranlib
-r-xr-xr-x 1 nick nick 659464 Jul 26 05:12 arm-unknown-eabi-gcov
-r-xr-xr-x 1 nick nick 503680 Jul 26 05:12 arm-unknown-eabi-gcov-dump
-r-xr-xr-x 1 nick nick 528320 Jul 26 05:12 arm-unknown-eabi-gcov-tool
-r-xr-xr-x 1 nick nick 5804856 Jul 26 04:54 arm-unknown-eabi-gprof
-r-xr-xr-x 4 nick nick 6684488 Jul 26 04:54 arm-unknown-eabi-ld
-r-xr-xr-x 4 nick nick 6684488 Jul 26 04:54 arm-unknown-eabi-ld.bfd
-r-xr-xr-x 2 nick nick 5169776 Jul 26 04:54 arm-unknown-eabi-nm
-r-xr-xr-x 2 nick nick 5976376 Jul 26 04:54 arm-unknown-eabi-objcopy
-r-xr-xr-x 2 nick nick 7114616 Jul 26 04:54 arm-unknown-eabi-objdump
-r-xr-xr-x 2 nick nick 5356160 Jul 26 04:54 arm-unknown-eabi-ranlib
-r-xr-xr-x 2 nick nick 2133272 Jul 26 04:54 arm-unknown-eabi-readelf
-r-xr-xr-x 1 nick nick 5112488 Jul 26 04:54 arm-unknown-eabi-size
-r-xr-xr-x 1 nick nick 5111128 Jul 26 04:54 arm-unknown-eabi-strings
-r-xr-xr-x 2 nick nick 5976376 Jul 26 04:54 arm-unknown-eabi-strip

Никто не мешает нам собрать с помощью crosstool-ng несколько тулчейнов с разными конфигурациями, все они попадут в соответствующую папку ~/x-tools/ ну и потом можно попробовать каждый из компиляторов, выбрать какой-то один, которым пользоваться.

Теперь вопрос, как пользоваться? Для сборки программ из проекта AMBER нужно экспортировать несколько важных переменных окружения вот так:

export CROSS_COMPILE=arm-unknown-eabi-
export ARCH=arm
export PATH=$PATH:$HOME/x-tools/arm-unknown-eabi/bin
export AMBER_CROSSTOOL=arm-unknown-eabi

Ну попробую скомпилировать какую нибудь программу для процессора ARM v2a и запустить ее на процессоре в симуляторе Icarus Verilog. Напомню, что исходные тексты Verilog системы на кристалле с процессором ARM v2a можно взять на моей странице github: https://github.com/marsohod4you/Amber-Marsohod2

Пробую компилировать так:cd Amber-Marsohod2/sw/hello-world-my$
make clean
make

Результат компиляции, файл с расширением mem переношу в папку со скриптами Verilog симуляции
cp hello-world.mem ../../hw/marsohod2/my_tb/hello-world/
cd ../../hw/marsohod2/my_tb/hello-world/

Компилирую скрипты симуляции Verilog, они будут читать mem файл нашей скомпилированной СИ-шной программы и исполнять ее на процессорве в симуляторе:

iverilog -o qqq -g2005 -DICARUS=1 -DAMBER_A23_CORE=1 -DNOMEMORY=1 -csrclist -I../../../vlog/system/ -I../../../vlog/amber23/ -I../../../vlog/tb/

Запускаю симулятор Icarus Verilog:

vvp qqq
VCD info: dumpfile out.vcd opened for output.
reseting..
Load boot memory from hello-world.mem
Read in 919 lines
log file tests.log, timeout 0, test name my ARM simulation
go..

Marsohod2: Hello, World!!!!!
*** 0 ***
*** 1 ***
*** 2 ***

Работает!!!

Конечно, все довольно просто, если вы создаете процессор с известной системой команд. А вот если вы для своего FPGA создаете свой собственный и уникальный супер мега процессор, то прежде всего нужно позаботиться о поддержке процессора в компиляторе GCC..

 


Добавить комментарий