Всем привет!

Иногда начинающие разработчики не очень хорошо представляют, какую литературу надо читать для серьезного изучения того или иного языка.

Разработка под FPGA (ПЛИС) — это не просто какой-то язык. Это очень объемная область, с огромным количеством подводных камней и нюансов.

В этой статье вы найдете:
  • список тем, которые должен освоить начинающий разработчик под FPGA
  • рекомендуемую литературу по каждой из тем
  • набор тестовых вопросов и лабораторных работ
  • классические ошибки новичков (и советы по исправлению)

Добро пожаловать под кат!

Что надо знать и уметь


Цифровая схемотехника


Необходимо:
  • знать базовые цифровые узлы (логические элементы И/ИЛИ/НЕ, шифраторы, мультиплексоры, суммматоры и пр.)

Литература:
  • David Harris, Sarah Harris. «Digital Design and Computer Architecture» — очень большая книга, рассказывающия от азовов до верхов. Есть версия на русском языке. Я просмотрел русскую версию: читается очень быстро и легко.
  • Угрюмов Е.П. «Цифровая схемотехника» — классический советский учебник, со всеми вытекающими последствиями (некоторые темы объяснены слишком сложно, и нельзя сразу понять нужна ли тебе эта информация сейчас или можно её пропустить). Я читал более старое издание, возможно в издании от 2010 года всё изменилось в лучшую сторону, не смотрел.

Тестовые вопросы:
  1. Чем цифровая схемотехника отличается от аналоговой?
  2. Какие есть базовые цифровые узлы? В каких из них выход зависит только от входа?
  3. Что такое мультиплексор? Нарисуйте схему мультиплексора 4 в 1 из примитивных элементов И/ИЛИ/НЕ.
  4. Постройте таблицу истинности для выражения: X = A or ( B and C ) or D.

Синтаксис HDL-языка


Сюда входит:
  • знание синтезируемых конструкций (синтаксиса) HDL-языка
  • знание, как описывать базовые цифровые узлы с помощью HDL-языка
  • понимание во что (со стороны базовых цифровых узлов) превращается тот или иной кусок HDL-кода
  • умение писать на HDL-языке для получения нужного поведения

В качестве HDL-языка я рекомендую сначала изучить самые базовые конструкции Verilog'a, а затем переключится на SystemVerilog.

Литература:
  • Pong P. Chu. «FPGA prototyping by Verilog examples» — расписывается Verilog, начиная с азов. Подробно разбирается синтаксис и есть огромное количество примеров, как простых (счетчики), так и уровнем повыше (UART и VGA).
  • Иосиф Каршенбойм. «Краткий курс HDL» — классический курс на русском языке.
  • www.asic-world.com — сайт с кучей примеров как по Verilog, так и по SystemVerilog.
  • David Harris, Sarah Harris. «Digital Design and Computer Architecture» (см. выше)
  • Stuart Sutherland. «SystemVerilog for Design» — книга п��о различия Verilog и SystemVerilog. Для чтения необходимы базовые знания Verilog'a. Рекомендуется к прочтению для понимания, какие удобства были внесены в новом стандарте.

Тестовые вопросы:
  1. Чем блокирующие присваивание отличается от неблокирующего? Когда стоит применять одно, когда другое?
  2. Есть ли разница между следующими тремя описаниями? Если да, то в чём она проявляется?
    // code 1:
    assign a = b + c;
    
    // code 2:
    always @( b or c )
      begin
        a = b + c;
      end
    
    // code 3:
    always @( * )
      begin
        a = b + c;
      end 
            


Тестовые задания:
1. Нарисуйте схему из базовых цифровых узлов для следующего кода:
Скрытый текст
module test(
  input              clk_i,

  input              a_i,
  input        [2:0] b_i,

  output reg         x_o

);

reg [7:0] cnt = 8'd0;
reg [7:0] cnt2;

wire      c;
reg       d;

always @( posedge clk_i )
  cnt <= cnt + 1'd1;

always @(*)
  begin
    cnt2 = cnt + 1'd1;
  end

assign c = ( cnt < 8'd5 ) && ( a_i == 1'b0 );

always @( posedge clk_i )
  begin
    d   <= c;
    x_o <= c ? ( d ) : ( cnt2[ b_i ] ); 
  end

endmodule


2. Нарисуйте поведение схемы из п.1 (т.е. состояния всех «переменных») при следующих входных воздействиях:


Скрытый текст
Времянки нарисованы с помощью онлайн-редактора WaveDrom.


3. Напишите модуль для управления светофором, который будет зажигать красный, желтый и зеленый фонари во всем известной последовательности: красный, красный и желтый, зеленый, зеленый моргает, желтый, красный. Параметры, задающие время горения фонарей светофора и период мигания зеленого фонаря являются параметрами модуля. Время задается в количестве тактов синхросигнала clk_i.

Интерфейс модуля:
Скрытый текст
module traffic_light(
  // cинхроимпульс
  input  clk_i,

  // асинхронный сброс
  input   rst_i,

  // Если 1, то горит соответствующий цвет, если 0 — то он не горит
  output  red_o,
  output  yellow_o,
  output  green_o
);


Симулирование и верификация HDL-кода


Необходимо:
  • знать несинтезируемые конструкции Verilog'a и SystemVerilog'a
  • уметь написать простой тестбенч, запустить его в симуляторе (например, ModelSim)
  • понимать, как должен быть устроен «идеальный» тестбенч

Литература:
  • testbench.in — огромное количество примеров верификации с использованием Verilog'a и SystemVerilog'a.
  • Chris Spear. «SystemVerilog for Verification» — хорошая, большая книга о верификации с помощью SystemVerilog. Читается легко, можно найти ответы на многие вопросы.

Видеоуроки:

Тестовые вопросы:
  1. Чем function отличается от task?
  2. Представим, что Вы написали максимально простую HDL-модель 5-стадийного RISC-процессора. Как будете её верифицировать? (Вопрос повышенной сложности).
  3. Чем различается queue от mailbox (типы данных в языке SystemVerilog)?
  4. Чем отличается функциональная симуляция от временной? Когда какую необходимо использовать?


FPGA


Необходимо:
  • знать из каких базовых элементов состоит FPGA
  • понимать как происходит workflow разработки под FPGA
  • интуитивно представлять какие операции для FPGA дешевые, а какие дорогие (по частоте и ресурсам)

Я работаю с чипами Altera, поэтому здесь и дальше название семейств и утилит будет от этого вендора. Если вы знаете аналогичную литературу для Xilinx — напишите в личку или в комментариях — я обязательно добавлю её в статью.

Литература:

Видеоуроки:

Тестовые вопросы:
  1. Чем отличается FPGA от ASIC? Из каких блоков состоит (или может состоять) FPGA?
  2. Попробуйте очертить круг задач, для которых хорошо (экономически целесообразно) использовать FPGA, а для каких MCU и CPU?
  3. Какие аппаратные блоки вы знаете? Для чего они используются? (Под аппаратными блоками здесь имеется в виду Hard IP).
  4. В семействе Y используется LUT с тремя входами и одним выходом. Какое минимальное количество LUT'ов надо для вычисления assign eq = ( a == b );, если a и b это 32-битные целые положительные числа? А если у LUT'a будет четыре (пять, шесть) входов?
  5. Необходимо создать одну-портовую память из 16 слов. Каждое слово шириной 100 бит. Сколько блоков M9K (9216 бит) будет занято? Считаем, что делаем проект под Cyclone III. (*)

В вопросах, обозначенных (*) , разумеется, не надо помнить всё наизусть, а можно пользоваться даташитами.

Синхронный дизайн и всё, что связано с таймингами


Необходимо:
  • знать принцип синхронного дизайна
  • знать к каким отрицательным последствиям могут привести те или иные схемы
  • иметь понятие о констрейнах

Литература:

Тестовые вопросы:
  1. Что такое timing constraints (констрейны)? Где они описываются и для чего нужны (для чего они используются)? Что будет, если их не описать?
  2. Что такое clock domain crossing? Как и когда надо его осуществлять?
  3. В чем разница между синхронным и асинхронным сбросом? Что будет если на вход синхронного сброса завести асинхронный сброс?
  4. Что такое latch (защелка, латч)? Какие последствия использования латча? Приведите пример кода, кото��ый создает латч.
  5. Что такое комбинационная петля? Какие последствия использования комбинационной петли?
  6. Что такое метастабильность? Как её достичь? Какие у неё есть плюсы и минусы?
  7. Что такое glitch (глитч)? Нужно ли с этим бороться? И если да, то где и как?
  8. Что такое setup time/hold time у D-триггера?


САПР


Необходимо:
  • уметь создавать проект
  • уметь описывать I/O пины и констрейны (хотя бы для простых ситуаций, без сложных I/O интерфейсов)
  • знать какие есть отчеты о сборке, какая информация содержится в каждом из них
  • уметь пользоваться инструментом для отладки на железе
  • уметь пользоваться инструментом для анализа таймингов (STA)
  • знать какие готовые IP-ядра/модули (FIFO, RAM, FFT, DDR, Ethernet и т.д.) предоставляет вендор и как их можно добавить в проект

Литература:

Видеоуроки:


Тестовые вопросы:
  1. Какие этапы сборки происходят от нажатия на кнопку «Собери проект полностью» до получения готового бинарного файла? Что происходит на каждом из этапов?
  2. Как посмотреть удалось ли САПР'у уложить проект в заданные ограничения (констрейны)?

Лекции и лабораторные


Я два семестра читал курс «Разработка под FPGA» для студентов старших курсов университетов Санкт-Петербурга. В курс входили как лекции, так и набор лабораторных работ. Лекции основывались на литературе, которую была перечислена выше.

План курса:
Скрытый текст
Введение:
  * Что такое ПЛИС? Области применения.
  * Обзор САПР ( Quartus ).

Изучение языка Verilog:
  * Отличительне черты языков описания аппаратуры ( HDL ).
  * Синтезируемые/несинтезируемые конструкции языка. 

  * Изучение синтезируемых конструкций языка:
    * Типы данных и способы их представления.
    * Операции, блокирующие/неблокирующие операции присваивания
    * Управляющие конструкции.
    * Блоки описания узлов комбинационного типа.
    * Блоки описания узлов последовательного типа.
    * Структурное/поведенческое описание проекта.
    * Параметризация.
    * Реализация на Verilog базовых цифровых узлов.

  * Изучение несинтезируемых конструкций языка ( +SystemVerilog ):
    * Применение несинтезируемых конструкций языка. Верификация. Testbench.
      Основные принципы создания testbench.
    * Основные функциональные блоки testbench'ей.  
    * Типы данных. Блоки процедурного типа.
    * Структуры данных для верификации ( массивы, очереди и т.д. ).
    * Функции и tasks.
    * Временная модель симуляции.
    * Использование базовых принципов ООП для верификации.
    * SystemVerilog Assertions.
    * Создание testbench для базовых цифровых узлов.
    * Обзор cуществующих методологий ( библиотек ) верификации.



Названия лекций (в 2015 году):
  1. Введение в FPGA.
  2. Внутреннее устройство FPGA.
  3. Введение в Verilog/SystemVerilog. Примеры описания различных типов логики.
  4. Синхронный дизайн. Создание простых тестбенчей.
  5. Описание FSM, массивов и структур в SystemVerilog. Память: создание с помощью Verilog и MegaWizard.
  6. Как работает DCFIFO. Static Timing Analysis. TimeQuest, constraints.
  7. Верификация: coverage, assertions, SystemVerilog interfaces
  8. Интерфейсы семейства Avalon. IP-Cores. Qsys.
  9. Верификация: SystemVerilog OOP, constrained-random tests.


Слайды к лекциям
Скрытый текст
К сожалению, это именно СЛАЙДЫ, которые мне помогали рассказывать лекции (не вся информация курса содержится на слайдах, некоторые я использовал как опору, а материал давался на доске).

Иногда будут видны картинки, которые никак не связаны с соседними (например, задания для тестов, которые давались на лекциях).

Лабораторные работы:

Классические ошибки


В этой части статьи я расскажу о типичных ошибках, которые допускают начинающие разработчики, и дам советы по их устранению.

Путаница в присваиваниях (блокирующие и неблокирующие)


Симптомы:
  • вы не очень уверено ответили на вопрос о блокирующих и неблокирующих присваиваниях (см. выше)
  • вы за собой замечаете, что в случайном порядке меняете "=" на "<=" (и наоборот), надеясь, что заработает
  • симулятор показывает странные вещи, вы начинаете сомневаться, что понимаете как работает D-триггер
  • результаты симуляции стабильно не совпадают с тем, что происходит на железе (где-то что-то плывет на такт)

Лечение:
  • разобраться в материале по Verilog (см. литературу выше)
  • если вы хотите, чтобы результат симуляции совпадал с тем, что будет синтезировано и воплощено на железе, то запомните простое правило: в блоках, где описывается комбинационная логика (always_comb, always @(*)) вы обязаны использовать только блокирующие присваивания (=). В блоках, которые описывают триггеры (always_ff, always @( posedge clk… )) вы обязаны использовать только неблокирующие присваивания (<=).


Проблема в таймингах


Симптомы:
  • результаты симуляции не совпадают с тем, что происходит на железе
  • железо работает нестабильно: иногда явно видны помехи (например, на VGA)
  • «я добавил сигналтап, и после этого схема перестала корректно работать, я убрал сигналтап и всё хорошо»

Лечение:
  • прописать все необходимые констрейны, (как минимум используемую тактовую частоту (или частоты) в *.sdc файле), подключить этот файл в проект
  • перекомпилировать проект. Зайти в TimeQuest и посмотреть, есть ли отрицательные слаки, и если есть, то дальше разбираться почему это происходи (возможно, достаточно покрутить настройки в Quartus'e, или придется переписывать куски код).

Если вы видите странности в SignalTap, то перепроверьте, синхронны ли те сигналы, которые вы снимаете с частотой «стробирования».

Не соблюдение принципов синхронного дизайна (асинхронщина)


Продолжение первого пункта, но я решил его выделить отдельно.

Симптомы аналогичные с предыдущим пунктом.

Почему-то многие любят делать вот так:
// BAD EXAMPLE
...
  input clk_i,
  ...

logic [7:0] sec_cnt;
logic [7:0] min_cnt;

logic       last_sec_value;

assign last_sec_value = ( sec_cnt == 8'd59 );

always_ff @( posedge clk_i )
  if( last_sec_value )
    sec_cnt <= 'd0;
  else
    sec_cnt <= sec_cnt + 1'd1;

always_ff @( posedge last_sec_value )
  min_cnt <= min_cnt + 1'd1;


Т.е. в качестве входного сигнала на триггере min_cnt используется другой сигнал, чем синхроимпульс clk_i. Он формируется комбинационной логикой (выходом компаратора).

Или вот так:
// BAD EXAMPLE
...
  input clk_a_i,
  input clk_b_i,
...

logic [7:0] cnt_a;
logic [7:0] cnt_b;
logic [7:0] sum;

always_ff @( posedge clk_a_i )
  cnt_a <= cnt_a + 1'd1;

always_ff @( posedge clk_b_i )
  cnt_b <= cnt_b + 1'd1;

always_ff @( posedge clk_b_i )
  sum <= cnt_a + cnt_b;


На вход триггера sum приходит выход комбинационной логики, вход у которой питается от разных тактовых сигналов.

Оба примера ошибочны, никогда так не делайте! Эти примеры — явное нарушение правил синхронного дизайна.

Я догадываюсь, что это всё идет из 2000х, когда чипы были маленькими и разработчики выживали как могли.
Скорее всего на маленьких частотах (типа 1 МГц) это прокатывало, но если вы собираетесь попасть в команду, которая делает серьезные вещи на топовых чипах, то за такие трюки можно легко вылететь со стажировки.

Лечение:
  • пройтись по всему проекту, выписать (на бумажку) какой тактирующий клок (сигнал) используется для каждого триггера.
  • понять как это число можно свести к минимальному количеству (в разумных пределах, разумеется), исправить код согласно правил синхронного дизайна.
  • пройтись по всему проекту и внимательно отследить как происходит clock domain crossing (т.е. переход данных со одной частоту на другую). Исправить, если он происходит некорректно.
  • выполнить все шаги из пункта «Проблема в таймингах».


Постоянная отладка на железе (игнорирование симуляции)


Вы допускаете эту ошибку, если разработка выглядит так:
  • редактирование HDL-файла
  • компиляция полного проекта
  • прошивание бинарника на плату
  • подключение с помощью SignalTap, просмотр нужных сигналов
  • понимание ошибки, переход в п.1

Почему это плохо:
  • с помощью SignalTap вы не можете посмотреть все-все-все сигналы, если у вас большой проект
  • вы отнимаете своё время, или время работодателя, при компиляции полного проекта, т.к. это может занимать значительное время. На маленьких чипах, это занимает 5-10 минут, и можно типа забить и пойти в это время покурить/попить чай/поиграть в кикер, но на больших проектах это вам выйдет боком.
  • нужна рабочая плата под рукой
  • у вас нет уверенности, что изменения в коде рабочие и не сломают что-то другое

Лечение:
  • написать тестбенч для всего проекта, либо для его частей
  • добиться корректной работы в симуляции
  • собрать проект, проверить на плате

Если что-то не заработает на железе, то:
  • пройтись по пyнктам, обозначеным выше (тайминги и асинхронщина)
  • если проблемы в таймингах нет, то пытаться понять при каких входных воздействиях это происходит
  • подать эти воздействия в симуляции, убедиться, что проблема воспроизводится
  • исправить ошибку в RTL-коде, проверить в симуляции и на железе
  • обязательно: сделать вывод, почему предыдущий вариант симуляции не смог отловить эту ошибку


Нет правил разработки (+ код с запашком)


Симптомы:
  • вы много времени тратите на чтение (разбор) кода, который написали вчера.
  • вы часто пишите однотипный код (самые популярные клавиши на вашей клавиатуре это Ctrl+C и Ctrl+V)
  • вы не можете разобраться в том коде, который написал коллега (если вы работаете вместе над одним модулем/IP-ядром), а он — в вашем.

Лечение:
  • ознакомиться с литературой, которая рассказывает как надо писать хороший код, например, Макконнелл. «Совершенный код»
  • разработать и описать правила разработки для команды. Или использовать готовые: NetFPGA, обсуждение на electronix #1, обсуждение на electronix #2, или тот, который использую я.
  • отдайте свой код на ревью более опытным разработчикам. Об этом можно попросить на форуме electronix'a в соответствующем разделе. Разумеется, желательно, что-то более серьезное, чем моргание светодиодов, иначе вас просто не поймут :).

Заключение


Надеюсь, что в этой статье я раскрыл, что надо читать и знать для вхождения в мир разработки под FPGA.

Уверен, что если Вы:
  • сможете легко отвечать на тестовые вопросы выше (без зазубривания, разумеется)
  • решите правильно лабораторные работы
  • избавитесь от «классических ошибок»
  • оформите 1-2 проекта на гитхабе и проверите их на железе. (желательно, сложнее, чем моргание светодиодиков и часики).

то без проблем сможете претендовать на позицию джуниора в серьезной компании.

Разумеется, этот путь нельзя освоить за одни выходные. Может потребоваться месяц и не один, но этот путь надо пройти, если вы хотите перейти из студенченской FPGA-разработки в профессиональную.

Спасибо за внимание!
Как всегда, буду рад вопросам и замечаниям в комментариях или в личной почте.

P.S.
Иногда мне в личке пишут:
Добрый день.
Я студент 3(4, 5) курса такого-то университета.
Мне нравится идея писать под FPGA (параллелизм, можешь делать что хочешь, бла-бла-бла) и нравится Java (делал простенькие приложения под Android). Хочу чем-то из этим заняться более-менее серьезно. Что советуете учить?

Чаще всего я им предлагаю глянуть две ссылочки (FPGA и JAVA) и самостоятельно сделать выводы.