Недавно компания Terasic начала продажи весьма интересной платы DE0-Nano-SoC Kit. Интересна она тем, что за весьма скромную цену предлагается очень мощный и функционально-насыщенный комплект разработчика на основе чипа Altera Cyclone V SoC FPGA со встроенным двухъядерным процессором ARM Cortex-A9. Кроме того, производитель в комплекте с платой даёт ОС Linux, развёрнутую на карту памяти MicroSD.
Но получив эту плату в своё распоряжение, я довольно быстро наткнулся на несколько проблем, обусловленых тем, что Linux был скомпилирован из исходников Yocto Project. В основном все проблемы были связаны с отсутствием общедоступных репозиториев, из которых можно было бы добавить в систему недостающие компоненты. Например, для того, чтобы получить доступ с этого устройства через сеть к расшаренным ресурсам Windows-сервера, в ядре не хватало модуля поддержки файловой системы Cifs.
Поэтому прежде всего было решено обновить ядро, заменить Yocto на более привычный Debian Wheezy и доустановить всё, что необходимо для доступа к расшаренным ресурсам Windows-сервера.
Процесс сборки изучался мной и выполнялся следуя рекомендациям из этой статьи, за что её автору Des333 огромное спасибо!
Полная переделка в мои планы не входила, поэтому загрузчики на карточке было решено оставить родные — от образа Linux 3.13, идущего в комплекте с платой. Так что раздел с типом A2 было решено не трогать совсем.
Задачи
- Обновить ядро Linux
- Заменить RootFS на Debian 7
- Доточить образ так, чтобы его можно было апдейтить из Интернета
- Примонтировать директорию, расшаренную на сервере Windows
Сборка ядра
Так как основной моей рабочей средой по жизни является Windows, то все действия по сборке Linux выполнялись из-под ОС Linux Mint 17.2 Cinnamon, установленной на виртуальную машину.
1. Запускаем терминалку и входим в root-режим — чтобы не набирать каждый раз команду sudo:
sudo -i
При этом /root будет нашей домашней директорией — всё будем делать в ней.
2. Компилировать ядро будем с помощью кросс-компилятора, входящего в пакет Altera SoC Embedded Design Suite (EDS). Поэтому скачиваем и устанавливаем самый свежий пакет Altera SoC EDS. На данный момент времени Altera SoC EDS имеет версию 15.0. Скачать этот пакет можно прямо с сайта Альтеры.
Altera SoC EDS установится в директорию /root/altera/15.0.
3. Устанавливаем build-essential:
apt-get install build-essential
4. Установливаем libncurses:
apt-get install libncurses5-dev
5. Скачиваем исходники linux-socfpga из репозиториев Альтеры и распаковываем их в домашнюю директорию:
- Заходим в релизы linux-socfpga в репозиториях Альтеры
- Находим нужный релиз. Я выбрал версию 4.1 — так как это была самая свежая стабильная версия на данный момент времени
- Скачиваем архив с исходниками
- Распаковываем исходники в домашнюю директорию
В результате появляется директория /root/linux-socfpga-4.1 с исходниками ядра Linux версии 4.1.
6. Запускаем альтеровский скрипт, который запустит новый BASH и подправит в нём некоторые переменные окружения (например, PATH). Все действия по компиляции будем проводить не выходя из этого BASH:
cd /root/altera/15.0/embedded
./embedded_command_shell.sh
7. Создаём несколько переменных окружения:
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
export LOADADDR=0x8000
8. Создаём дефолтную конфигурацию для socfpga:
cd /root/linux-socfpga-4.1
make socfpga_defconfig
При этом будет создан конфигурационный файл .config, заточенный для компиляции под ARM.
9. Добавляем недостающие компоненты в конфигурацию ядра (или удаляем лишние):
make menuconfig
При этом откроется псевдографическое окно с менюшками.
Нам нужно добавить драйвер файловой системы CIFS — чтобы иметь возможность заходить на сетевые расшаренные ресурсы. Существует два способа добавления драйверов в систему — добавить прямо в ядро или добавить в виде внешних подключаемых модулей.
Итак, идём по пути File Systems -> Network File Systems, становимся на CIFS Support и нажимаем клавишу пробел — напротив строки CIFS Suport должна появиться буква M — значит будет использоваться подключаемый внешний модуль. Нужно будет позднее скомпилировать его отдельно и положить в директорию внешних модулей. Если же нажать клавишу пробел ещё раз, то буква M изменится на символ звёздочки — значит драйвер будет встроен прямо в ядро.
Примечание: в дальнейшем, при проверке работоспособности системы, выяснилось, что внешний модуль cifs крэшится при попытке копирования файла с расшаренного диска сервера Windows. Встроенный же в ядро драйвер cifs работал совершенно нормально. Хотя при использовании внешних модулей с другими версиями ядра (например, 3.19) подобных проблем не возникало. Причину происходящего мне так и не удалось выяснить.
Также нужно включить поддержку HighMem — иначе система не сможет использовать верхние 256 мегабайт ОЗУ. Для этого идём по пути Kernel Features -> High Memory Support и также нажимаем клавишу пробел.
Выходим из меню — нажимаем EXIT пока не выйдем. На вопрос — надо ли сохранять конфигурацию — отвечаем Yes.
10. Компилируем ядро:
make uImage
В моём случае виртуальной машине было отдано только одно ядро. Процесс компиляции занял около 20 минут. Если же компилирование будет выполняться в машине с несколькими ядрами, то для скорости можно распараллелить процесс компиляции на несколько ядер. Для этого надо явно задать количество ядер через опцию -j. Например, для компиляции силами трёх ядер:
make -j 3 uImage
11. Компилируем dtb-файл, соответствующий нашему устройству. Если воспользоваться старым dtb-файлом, то или устройство повиснет при загрузке или будут страшные глюки при работе:
- Ищем все файлы, имеющие в названии cyclone5 и заканчивающиеся на dts:
find ~/linux-socfpga-4.1 -name "*cyclone5*dts"
- Выбираем наиболее подходящий из найденых файлов. Я просто просматривал их содержимое и в одном из них увидел слово terasic. Мне подумалось, что это самый подходящий файл для данного устройства — вот его и использовал. Файл назывался socfpga_cyclone5_sockit.dts.
- Запускаем компиляцию socfpga_cyclone5_sockit.dtb:
make socfpga_cyclone5_sockit.dtb
В результате компиляции создалось два файла:
/root/linux-socfpga-4.1/arch/arm/boot/zImage
/root/linux-socfpga-4.1/arch/arm/boot/dts/socfpga_cyclone5_sockit.dtb
12. Если на этапе конфигурации был выбран вариант использования внешних модулей, то необходимо скомпилировать их.
Компилируем модуль CIFS:
make M=fs/cifs
и компилируем модули криптографии — они понадобятся при монтировании расшаренных ресурсов Windows:
make M=crypto
13. Копируем файлы ядра и dtb на карточку. Исходно карточка была нарезана так, что ядро и DTB-файл лежали на отдельном партишене FAT32. Вот на него эти файлы и записываем. Единственное замечание: DTB-файл нужно переименовать — чтобы он назывался также, как тот, который уже лежит на разделе FAT32 карточки:
- Подключаем карточку к виртуальной машине. Мне пришлось воспользоваться внешним кардридером, подключенным прямо к порту USB2 компьютера. Сделать то-же самое через встроенный в компьютер кардридер почему-то не удалось. Также не удалось подсоединить внешний кардридер к виртуальной машине, если подключать его через порт USB3.
- Произойдёт автомонтирование разделов карточки — нельзя размонтировать разделы через GUI, потому что в этом случае происходит полное отключение кардридера от виртуальной машины.
- Смотрим названия примонтированных разделов:
mount
Увидим нечто в этом роде:
/dev/sdb1 on /media/user/F725-1429 type vfat (rw,nosuid,nodev,uid=1000,gid=1000,shortname=mixed,dmask=0077,utf8=1,showexec,flush,uhelper=udisks2) /dev/sdb2 on /media/user/41cad05c-898e-49a3-9d00-02b92fa817ba type ext3 (rw,nosuid,nodev,uhelper=udisks2)
Раздел типа vfat (первая строка) — то, что нас интересует в данный момент.
- Смотрим, что лежит на разделе vfat:
ll /media/user/F725-1429
Видим нечто в этом роде:
-rw-r--r-- 1 user user 1164128 Apr 20 20:23 de0_nano_soc.rbf -rw-r--r-- 1 user user 15274 Jul 26 17:08 socfpga.dtb -rw-r--r-- 1 user user 176 Apr 20 19:59 u-boot.scr -rw-r--r-- 1 user user 3371472 Jul 29 16:50 zImage
Значит dtb-файл называется socfpga.dtb.
- Копируем наши файлы на карточку:
cp /root/linux-socfpga-4.1/arch/arm/boot/zImage /media/user/F725-1429/ cp /root/linux-socfpga-4.1/arch/arm/boot/dts/socfpga_cyclone5_sockit.dtb /media/user/F725-1429/socfpga.dtb
Сборка файловой системы
Этот подраздел во многом повторяет то, что написано в этой статье, но тем не менее я привожу его полностью, чтобы в дальнейшем было проще пользоваться этим руководством.
Собирать будем Debian 7 Wheezy:
1. Устанавливаем пакеты, которые понадобятся для сборки файловой системы:
apt-get install debootstrap qemu-user-static binfmt-support
2. Создаем директорию и загружаем в неё все необходимые файлы:
cd /root
mkdir debian7
debootstrap --arch armel --foreign wheezy debian7 http://ftp.debian.org/debian
3. Чтобы запускать приложения, собранные под ARM-архитектуру, будем использовать qemu static. Для этого скопируем файл в нашу директорию debian7:
cp /usr/bin/qemu-arm-static debian7/usr/bin/
4. Переходим в нашу новую файловую систему:
chroot debian7 /bin/bash
5. Если приглашение интерпретатора изменилось на «I have no name!@hostname:/#», значит всё прошло успешно.
Заканчиваем процесс сборки:
/debootstrap/debootstrap --second-stage
6. В /etc/inittab оставляем следующие строки:
/etc/inittab
id:5:initdefault:
si::sysinit:/etc/init.d/rcS
~~:S:wait:/sbin/sulogin
l0:0:wait:/etc/init.d/rc 0
l1:1:wait:/etc/init.d/rc 1
l2:2:wait:/etc/init.d/rc 2
l3:3:wait:/etc/init.d/rc 3
l4:4:wait:/etc/init.d/rc 4
l5:5:wait:/etc/init.d/rc 5
l6:6:wait:/etc/init.d/rc 6
z6:6:respawn:/sbin/sulogin
S:2345:respawn:/sbin/getty 115200 console
7. Устанавливаем пароль для root-аккаунта:
passwd
8. Запаковываем новую файловую систему в архив:
tar -cpzf debian7.tar.gz --exclude=debian7.tar.gz /
9. Выходим из chroot:
exit
10. Размонтируем и затем форматируем раздел ext3 на карточке (названия разделов смотрим в пункте 13 из сборки ядра):
umount /dev/sdb2
mkfs.ext3 /dev/sdb2
11. Монтируем раздел ext3:
mount /dev/sdb2 /mnt/
12. Распаковываем архив с файловой системой на карточку в раздел ext3:
tar -xzf /root/debian7/debian7.tar.gz -C /mnt/
13. Если при сборке ядра был выбран вариант использования внешних модулей, то необходимо записать на карточку внешние модули, скомпилированные на этапе 12 процесса сборки ядра:
cd /mnt/lib
mkdir modules
mkdir modules/4.1.0
mkdir modules/4.1.0/extra
mkdir modules/4.1.0/kernel
mkdir modules/4.1.0/kernel/crypto
cp /root/linux-socfpga-4.1/fs/cifs/cifs.ko ./modules/4.1.0/extra/
cp /root/linux-socfpga-4.1/crypto/*.ko ./modules/4.1.0/kernel/crypto/
14. Размонтируем разделы:
umount /dev/sdb1
umount /dev/sdb2
На этом всё — карточка готова, можно устанавливать её в устройство и загружаться.
Окончательная доводка
После загрузки устройства дотачиваем образ на месте:
1. Логинимся в Debian на устройстве, подключившись к нему через встроенный serial-порт.
2. Если при сборке ядра был выбран вариант использования внешних модулей, то необходимо сгенерить файлы с информацией о внешних модулях ядра:
depmod -a
3. Добавляем в список репозиториев репозиторий Debian 7 (я добавил немецкий сервер):
echo "deb http://ftp.de.debian.org/debian wheezy main" > /etc/apt/sources.list
4. Подключаем устройство к Ethernet-сети. Получаем адрес по DHCP:
dhclient -4 eth0
5. Поднимаем NTP, так как с неправильным временем не удастся примонтировать расшаренные ресурсы:
apt-get update
apt-get install ntp
6. Устанавливаем наш часовой пояс:
dpkg-reconfigure tzdata
7. Для проверки, что всё собралось нормально, монтируем серверную шару. Например, в моём случае я делал это так:
mount //192.168.48.4/distrib /mnt -o username=jok40
8. Устанавливаем SSH-сервер. Пользоваться serial-портом неудобно, так как при работе через него происходит заворот набираемых команд на начало строки после достижения колонки 80:
apt-get install openssh
9. Назначаем статический адрес интерфейсу eth0 — чтобы в дальнейшем проще было подключаться к устройству по SSH. Для этого редактируем файл interfaces:
nano /etc/network/interfaces
В моём случае он стал выглядеть вот так:
# interfaces(5) file used by ifup(8) and ifdown(8)
auto lo
iface lo inet loopback
# The primary network interface
allow-hotplug eth0
iface eth0 inet static
address 192.168.48.27
netmask 255.255.255.0
gateway 192.168.48.1
network 192.168.48.0
broadcast 192.168.48.255
auto eth0
10. Редактируем файл resolv.conf — чтобы нормально работал DNS-клиент:
nano /etc/resolv.conf
Добавляем в него строки:
nameserver 192.168.48.1
nameserver 8.8.8.8
11. Перезагружаем устройство.
12. Для проверки, что всё сделано правильно
подключаемся к устройству по SSH через Ethernet:
Всем спасибо за внимание!
leshabirukov
А как вы собираетесь использовать эту плату в перспективе, какая функция планируется для ПЛИС?
jok40
Мы используем подобные платы в разных целях. Бывает — для реверс-инжиниринга. Например, раньше на основе аналогичной платы DE0-Nano (без SoC) был реализован аппаратный дебаггер-эмулятор для процессоров Intel 80186/80188 (нужно было отловить пару багов в довольно древних дивайсах). Также когда-то очень давно подобный кит был использован для взлома транспондеров. Ну а чаще всего используем подобные дивайсы в качестве каких-нибудь контроллеров, когда нужно быстро и по-возможности без паяльника организовать управление какими-нибудь внешними устройствами, или, допустим, поснифферить какую-нибудь быструю шину. Что будем делать конкретно с этой платой — пока не знаю. Время покажет.
leshabirukov
Именно «nano» это потому что цеплять к целям удобнее из-за компактности, или случайно получилось? Раньше как обходились без soc, использовали ли soft — ядро?
jok40
Нет, почему-же, не именно «nano». Когда-то мы использовали киты на других чипах. Например, Spartan-3AN на Xilinx-е. Но с Ксилинкса мы в конце-концов перешли на Альтеру в основном из-за нещадной глючности среды разработки.
Nano нас привлекло прежде всего очень маленькими размерами и исключительной дешевизной. Но у неё был серьёзный недостаток — отстутствие на борту порта для связи с компьютером. Единственный USB был задействован для JTAG. И хотя находились умельцы, которые этот USB таки приспосабливали для общения с компьютером, ни о каких хоть сколько-нибудь приличных скоростях обмена говорить не приходилось. Поэтому для Nano пришлось изготовить плату-нахлобучку с полноценным USB-портом и написать модуль-трансивер для упрощения сопряжения этой нахлобучки с остальной схемой. Ну вот теперь появилась другая Nano, у которой этого недостатка нет.
А по поводу софт-ядер — да, был и такой опыт. Например, были взяты исходники от 16F84 и переделаны в 16F877. Использовалось данное ядро в нескольких проектах.