Графический VGA-контроллер на SoC без знаний HDL

  • Tutorial

Всем привет!
В одной из предыдущих статей мой коллега Des333 реализовал фреймбуфер для LCD, работающего на графическом контроллере ILI9341. Однако, его написание потребовало существенного опыта в разработке RTL-кода.

К тому же, не у каждого под рукой есть embedded LCD-дисплей, зато наверняка есть монитор с VGA-входом.Что же делать, если опыта разработки под FPGA мало, но есть SoC, а сделать что-то интересное хочется?

В этой статье мы расскажем, как разработать графический контроллер, имея на руках плату с SoC (Altera Cyclone V), дисплей с VGA и минимальные знания языков HDL (в нашем случае — Verilog).

Для примера будем использовать наши платки, но всё описанное заработает и на других.
Кому интересно, прошу под кат.



Я планирую рассказывать в следующем порядке:
  1. Сначала немного про архитектуру взаимодействия
  2. Кратко про подключение к VGA
  3. Как получить прошивку, используя только Quartus Qsys
  4. Как объяснить ядру, что есть графический контроллер. Расскажу, что нужно добавить в dtb и собрать драйвера
  5. Как получить терминал и иксы на дисплее


Я буду использовать отладочную плату CB-CV-SOM, работающую вместе с SoDIMM-модулем CV-SE-SOM:

К этой отладочной плате у нас есть шилд, на котором помимо VGA есть много интересного (см. metrotek.spb.ru/cbcvsom.html)

Архитектура



Для вывода изображения на дисплей нам нужны фреймбуффер, драйвер и модуль развёртки, который обеспечит связку между процессором и дисплеем, а также обеспечит непрерывное обновление кадров.

В SoC'е к ARM ( также называется HPS — Hard Processing System ) подключенна DDR3 память (1 GB в нашем случае), в ней и будет находится наш фреймбуффер. А в FPGA будет модуль, который нам нужно будет сделать с помощью Qsys.

Вот схема:



Работать все будет так:
  • Драйвер настраивает модуль в FPGA: после этого модуль готов получать данные из SDRAM-контроллера и «разворачивать» их на дисплей
  • В /dev/fb0 записываем картинку. Данные попадают во фреймбуфер в DDR
  • Модуль в FPGA непрерывано читает фреймбуффер и обновляет экран


Как работает VGA



VGA ( Video Graphics Array ) — это видео интерфейс, использующий аналоговый сигнал для передачи цветовой информации. Формат сигналов и их поведение похожи на тевелизионный сигнал.
Список сигналов:
vga_vs_o — вертикальная синхронизация
vga_hs_o — горизонтальная синхронизация
vga_r_o — данные красной составляющей пикселя
vga_g_o — данные зеленой составляющей пикселя
vga_b_o — данные синий составляющей пикселя

Shield поддерживает 16 бит на цвет, а это значит, что на синий и красный выделяется по 5 бит, а на зеленый 6. ЦАП сделан по схеме R2R.

У мониторов различные разрешения и частота обновления изображения. Эти параметры регулируются в VGA с помощью вертикальной и горизонтальной сихронизаций. У которых есть следующие параметры:
  1. Тактовая частота появления новых пикселей.
  2. Front porch — время гашения синхроимпульса.
  3. Back porch — время нарастания синхроимпульса.
  4. Sync — длительность синхронизации.
  5. Display Area — это момент времени, когда передаётся информация.


Времянки выглядят так:



Мы будем использовать режим VGA 800x600 на 60 Hz, cледовательно параметры следующие (параметры для других режимов):
Тактовая частота 40 MHz
Горизонтальная синхронизация:
  • Front porch 40 pixels
  • Back porch 88 pixels
  • Sync 128 pixels
  • Display Area 800 pixels

Вертикальная синхронизация:
  • Front porch 1 lines
  • Back porch 23 lines
  • Sync 4 lines
  • Display Area 600 lines


Прошивка для FPGA



Чтобы получить прошивку, нам в Qsys потребуются следующие модули:
  • HPS — это наш процессор.
  • Frame Reader — это IP-ядро читает кадры, сохраненные во внешней памяти, и выводит их в виде видеопотока.
  • Clocked Video Output — это IP-ядро, из Avalon-ST делает вывод в VGA подобном формате.
  • Altera PLL — PLL для изменения тактовой частоты: нам нужно получить 40 МГц из 25 МГц, которые есть на плате.


Из процессора выходят AXI интерфейсы H2F и F2H, у IP-ядер Альтеры интерфейсы Avalon-ST и Avalon-MM , поэтому нужен еще модуль межсоединения ( Interconnect ), который должен конвертировать из одного интерфейса в другой и мультиплексировать потоки данных. Он появится автоматически при генерации файлов.

Подробнее про Frame Reader и Clocked Video Output можно посмотреть тут.
Как собрать прошивку и какие настройки нужны для HPS можно прочитать в этой статье.

Altera PLL


Настройки PLL.
Altera PLL



Frame Reader


Здесь настраиваются:
  • Параметры FIFO на входе модуля
  • Насройки передачи данных
  • Количество активных пикселей

Параметры Bits per pixel per color plane и Number of color planes in parallel связаны с драйвером и объясняются ниже. Обратите внимание, что размерность интерфейса с HPS cовпадает с размерностью Master port width.
Frame Reader


Clocked Video Output


Здесь:
  • Настройки синхронизаций ( vga_vs_o, vga_hs_o ), которые описывались выше
  • Способ каким приходят данные ( он такой же как и у Frame Reader )

Clocked Video Output



Qsys Connections


И теперь всё соединяем. Настройки для модуля Frame Reader «цепляем» к h2f master, интерфейс для передачи данных f2h slave. Соединяем Clocked Video Output с Frame Reader avalon_streaming_source -> din. Все тактируется outclk0.
Qsys Connections



И генерируем файлы, нажав Generate HDL ....

Как выше сказано, на плате 16 бит, а из модуля выходит 32 бита, поэтому нужно внимательно назначить пины в qsf-файле, либо отредактировать выход для себя удобным образом в top файле проекта. Нам нужны старшие биты каждого цвета, они более информативны, чем младшие.

Обратите внимание, что это первое и единственное место, где мы редактируем код. Больше это не потребуется.

logic        vga_v_sync;
logic        vga_h_sync;
 
logic [31:0] vga_data;
 
logic [7:0]  vid_r;
logic [7:0]  vid_g;
logic [7:0]  vid_b;

assign  vga_r_o  = vid_r[7:3];
assign  vga_g_o  = vid_g[7:2];
assign  vga_b_o  = vid_b[7:3];
 
assign  vga_hs_o = vga_h_sync;
assign  vga_vs_o = vga_v_sync;
 
assign { vid_r, vid_g, vid_b } = vga_data;


Драйвер и dtb



Нам потребуется драйвер altvipfb.

Вернемся к параметрам Bits per pixel per color plane и Number of color planes in parallel в Frame Reader. В драйвере написано:
if (bits_per_color != 8) {
          dev_err(&fbdev->pdev->dev,
                  "bits-per-color is set to %i.  Curently only 8 is supported.",
                  bits_per_color);
          return -ENODEV;
}

if (!(fbdev->mem_word_width >= 32 && fbdev->mem_word_width % 32 == 0)) {
          dev_err(&fbdev->pdev->dev,
                  "mem-word-width is set to %i.  must be >= 32 and multiple of 32.",
                  fbdev->mem_word_width);
          return -ENODEV;
         }


Число бит на один цвет только 8 и ширина слова должна быть больше или кратна 32. С чем же связано такое ограничение? Смотрим дальше и видим:

/* settings for 32bit pixels */
info->var.red.offset = 16;
info->var.red.length = 8;
info->var.red.msb_right = 0;

info->var.green.offset = 8;
info->var.green.length = 8;
info->var.green.msb_right = 0;
 
info->var.blue.offset = 0;
info->var.blue.length = 8;
info->var.blue.msb_right = 0;


Становится ясно, что драйвер работает в режиме True color, записывая цвет в 32 битное слово ( более удобно выравнивать, чем 24 ), и работает он только в таком режиме.

Чтобы собрать этот драйвер, в конфиге ядра надо внести следующие изменения.

CONFIG_FB_CFB_FILLRECT=m                                       
CONFIG_FB_CFB_COPYAREA=m                                                
CONFIG_FB_CFB_IMAGEBLIT=m
CONFIG_FB_ALTERA_VIP=m


Для того что бы linux узнал, что у нас в FPGA есть фреймбуфер от Альтеры, в dtb надо прописать следующие магические слова:
hps_0_h2f: bridge@0xc0000000 {
    compatible = "altr,bridge-1.0", "simple-bus";
    reg = < 0xc0000000 0x20000000 >;
    #address-cells = < 1 >;
    #size-cells = < 1 >;
    ranges = <0x00000000 0xc0000000 0x4080 >;
    alt_vip_vfr_1: vip2@0x0 {
    compatible = "ALTR,vip-frame-reader-13.0", "ALTR,vip-frame-reader-9.1";
    reg = < 0x4000 0x00000080 >;
    max-width = < 800 >; /* MAX_IMAGE_WIDTH type NUMBER */
    max-height = < 600 >; /* MAX_IMAGE_HEIGHT type NUMBER */
    mem-word-width = < 0x80 >;
    bits-per-color = < 0x8 >;
   };
};


В параметре range — диапазон валидных адресов, с которых драйвер будет читать, а в reg = < 0x4000 0x00000080 > — стартовый адрес и сколько адресов занято alt_vip. mem-word-width это параметр Master port width в Frame Reader.

Подробнее про dtb.

Запуск терминала и иксов



Заходим на прибор и загружаем драйвера:
modprobe altvipfb
modprobe fbcon


Затем проверяем, все ли хорошо с помощью dmesg, и смотрим, есть ли похожая строка:
[   66.424283] altvipfb c0000000.vip2: fb0: altvipfb frame buffer device at 0x2c000000+0x12c000


Ура! Появился fb0:
ls -l /dev/fb0 
crw-rw---T 1 root fb 29, 0 Nov 26 10:13 /dev/fb0


Затем выводим консоль на экран, подключенный к плате:
/sbin/getty 38400 tty1

Ставим icewm и запускаем с помощью startx:

apt-get install icewm icewm-themes
startx


IceWM



Итого: мы получили графический контроллер, с минимальными знаниями HDL языков.
НТЦ Метротек 56,94
Разработка и производство Ethernet устройств etc.
Поделиться публикацией
Похожие публикации
Комментарии 10
  • +1
    Где-то на форуме марсохода я видел реализацию VGA адаптера для ардуины. использовалась самая дешевая MAX II. Там всё было сделано вручную, очень круто. Но найти тему не могу, Если у кого сохранилась — поделитесь пожалуйста.
  • +2
    Ничего себе, линукс! А я как-то все более простыми вещами балуюсь. Делал часы на семисегментниках и ПЛИС. И вот недавно решил семисегментники заменить на VGA.
    • +1
      Жду продолжения цикла статей, расскажете про VGA?
      • +1
        Да, есть такая идея — использовать VGA вместо плат расширения, которые содержат светодиоды, семисегментные, матричные индикаторы. В случае с VGA можно будет произвольно выбирать, количество, размеры, расположение элементов. Но есть небольшой архитектурный вопрос. Как только найдется решение, так можно будет и статью написать. Да и тема в реальной жизни актуальная, например, индикация координат на самодельных станках с ЧПУ или банальная электронная очередь в банке. Мне кажется, что и экономически VGA экран может оказаться дешевле светодиодов.
        • 0
          Решение способно быть дешевле 40$ (без учета стоимости монитора)?
          А то было бы интересно к arduino подключать монтитор так же просто как сейчас текстовый двухстрочник ;)
    • 0
      К сожалению не видел схематику этой платы, в связи с картинкой №3 вопрос- как FPGA часть получает доступ к контроллеру SDRAM? У меня SOCKIT Terasic и FPGA имеет свой чип DDR и я пробовал Linux(непомню дистр.) собранный китайцами.
      • +2
        Там между FPGA и ARM есть мост HPS-to-FPGA там есть несколько компонентов один из них «FPGA-to-HPS SDRAM controller: up to 6 masters (command ports), 4x 64 bit read data ports and 4x 64 bit write data ports»
        Через него и можно получать доступ к DDR проца.
        https://www.altera.com/products/fpga/features/cyv-soc-hps.html
      • +1
        присланный по почте комментарий, который, как говорят, был отклонён, хотя в FAQ по ведению корпоративных блогов утверждает, что это сделать нельзя по историческим причинам:

        > большое спасибо за статью!

        > не хватает правда, видео: как оно (вот это ГУИ) работает — тормозит или нет?
        > какая частота этого СоК? Можете выложить код куда-то архивом? Или всё что делали в Ксис -это не код? А вообще нельзя было без этого СоК обойтись?

        > понятно, что статья рекламная для пиара платок, но до уровня статей от des333 это не дотягивает, на мой взгляд

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое