Основы разработки с применением ПЛИС. Часть 2.

Первый шаг, который делают при освоении любого нового языка программирования — это написание программы, выводящей «Hello World!». Однако, разработка на ПЛИС — это аппаратная разработка. И Hello World на аппаратном уровне — это будет скорее мигание лампочкой. Но мне бы хотелось начать с теории, и уже после этого перейти к практике. А значит, эта часть будет посвящена теории, Verilog и средствам разработки.

Verilog

Как говорилось ранее, разработка на ПЛИС — это аппаратная разработка. Внутренние схемы ПЛИС, в виде логических элементов, триггеров, регистров и прочих примитивов, объединяются в цифровую схему. Все, что раньше разрабатывалось с помощью дискретных микросхем различной логики, теперь находится внутри одного корпуса. Для того, чтобы задать логику, по прежнему, можно рисовать схемы, однако даже небольшие логические схемы могут работать не так, как задумал автор, потому что он мог не предусмотреть каких-то состояний или комбинаций логических функций. Поэтому, для задания цифровых схем разработаны специальные HDL-языки. Человек в понятной для себя форме определяет работу схемы, а синтезатор формирует прошивку для заливки в ПЛИС. Одним из таких языков является Verilog.

 

Еще одним преимуществом использования HDL языка, является его универсальность. Логика, описанная на Verilog, может быть протестирована с помощью универсальных инструментов и после этого перенесена в ПЛИС любого производителя (Altera, Xilinx, Lattice). Об одном из таких инструментов мы сегодня и поговорим.

 

Icarus Verilog

Icarus Verilog — это небольшой набор, состоящий из компилятора и симулятора Verilog, плюс программа просмотра результата. Комплект очень компактный, всего лишь 10 мегабайт. Есть версия для Linux, но мы будем ставить под Windows. 

http://bleyer.org/icarus/iverilog-20130827_setup.exe — 10 Мб

Скачиваем, устанавливаем.

 

Verilog — разработка и тестирования

HDL-язык Verilog вполне самодостаточен, на нем описываются как модули синтезируемой логики, так и модули для их проверки. Модули проверки не могут быть аппаратно синтезированы и работают только в симуляторе. Они генерируют сигналы во времени, эти сигналы подаются на входы тестируемых модулей, в результате можно понять, правильно ли работают модули.

 

Попробуем создать тестовый модуль, который генерирует тактовый сигнал

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

Создадим файл-модуль с кодом для тестирования модулей — bench.v

module testbench(); 
reg clk; 
initial begin
  $display("start");
  $dumpfile("test.vcd");
  $dumpvars(0,testbench);
  clk <= 0;
  repeat (100) begin
    #10;
    clk <= 1;
    #10;
    clk <= 0;
  end $display("finish");

end
endmodule

В файле bench.v описан тестовый модуль testbench, в нем создан тестовый источник сигнала clk (меандр). Другие модули будут создаваться в отдельных файлах, либо логику можно протестировать сначала в этом модуле, а потом вынести в отдельный модуль. Потом в модуль testbench будут добавляться экземпляры этих модулей, где мы будем подавать на их входы тестовые сигналы и получать из них результаты. Из модулей мы можем строить иерархию, думаю это понятно всем.

Создадим BAT файл, который скомпилирует и просимулирует главный модуль, добавив другие модули из текущей папки — makev.bat

iverilog -o test -I./ -y./ bench.v
vvp test
pause

После запуска этого файла мы увидим на экране текст, заданный в $display (это отладочный вывод), значение же сигналов и регистров схемы будут находиться в файле test.vcd. Кликаем по файлу и выбираем программу для просмотра — GTKWave (в моем случае он установился в папку D:\iverilog\gtkwave\bin\gtkwave.exe). Еще пару кликов и мы увидим наш clk.



Шкала времени зависит от установки timescale в начале файла.

Практически, каждый свой новый модуль я создаю в блокноте и отлаживаю IcarusVerilog. Следующим этапом после такой отладки идет проверка модулей в системе разработки конкретной ПЛИС. В моем случае это Quartus. Хотя в Quartus тоже есть свой симулятор, но я его использую реже. Причина в простоте обновления кода и просмотра результата в IcarusVerilog: сохранил изменения в файле, запустил BAT, нажал кнопку «обновить» в GTKWave — все! В ModelSim для этого требуется чуть больше движений, но он тоже не плох, особенно на данных сложных структур.

Разберемся чуть подробнее и добавим еще что-нибудь

Итак, если все получилось, идем дальше. Теперь нужно понять, что же получилось. Мы создали модуль teshbench. Именно он будет симулироваться Icarus Verilog'ом. Мы запустили BAT файл. Первая строка которого скомпилировала модуль. Вторая строка выполнила модуль. В результате выполнения мы увидели на экране отладочные текстовые сообщения. Еще создался файл vcd, в котором сохранились значения всех «переменных» модуля во времени. Чтобы их посмотреть, мы как раз и запустили GTKWave.

В качестве переменных, которые меняются во времени в тестовых модулях, используются регистры Verilog. Для того, чтобы симулировать работу тактового генератора, мы создаем однобитный регистр clk:

reg clk;

И дальше будем менять его значение.

Блок initial как в синтезируемых блоках, так и в тестовых, используется для того, чтобы задать изначальные значения. Следующие строки используются только для симуляции и отладки:

$display("start");
$dumpfile("test.vcd");
$dumpvars(0,testbench);

А эта строка задает изначальное значение регистра clk, равное нулю:

clk <= 0;

В тестовом модуле, в отличие от синтезируемого, можно указать последовательность изменений значений регистра во времени. То есть сначала наш clk станет равным нолю. С помощью символа #10 мы указываем симулятору, что он должен подождать 10 тиков перед выполнением следующего присвоения. И получается, что мы ждем 10тиков, и ставим clk в единицу, потом ждем еще 10 тиков и ставим в ноль

#10;
clk <= 1;
#10;
clk <= 0; 

А с помощью конструкции repeat, эта последовательность повторяется 100 раз. После этого, никаких действий не происходит и симуляция заканчивается. В файле vcd будет записано 100 тактов clk.

Итак, «виртуальный» клок у нас есть. Но теперь опишем ту часть, которая уже могла бы реально работать в «железе». Например, посмотрим, как можно описать простейшие логические операции И/ИЛИ. Verilog нам дает несколько вариантов того, как мы можем описать простейшую логику. Давайте рассмотрим.

Представим, что у нас будет реальное устройство — ПЛИС, на вход которого подается тактовый сигнал CLK. Внутри ПЛИС у нас пусть будет 2 битный регистр (считай, что двухбитная переменная), с каждым тактом CLK значение переменной будет увеличиваться на единицу. Получается, что пара бит этой переменной будет принимать все 4 возможных значений. Каждый бит будет использоваться в качестве одной из линий для входа логической функции. Опишем регистр на два бита:

reg [1:0] cnt;

Зададим изначальное значение регистра — число в двоичном формате, шириной 2 бита:

initial cnt <= 2'b00;

Зададим две линии (провода/wire), которые будут входами для логических функций. 

wire log_sig_A;
wire log_sig_B;

Присоединим к этим линиям биты нашего регистра

assign log_sig_A = cnt[0];
assign log_sig_B = cnt[1];
А теперь опишем событийный блок, который при каждом нарастающем фронте clk будет увеличивать значение cnt на единицу:
always @(posedge clk) cnt <= cnt + 1'b1;

После этого у нас должен получиться такой код:
`timescale 100ns / 100 ps
module testbench();

reg clk;
reg [1:0] cnt;
initial cnt <= 2'b00;

wire log_sig_A;
wire log_sig_B;

assign log_sig_A = cnt[0];
assign log_sig_B = cnt[1];

initial
begin
    $display("start");

    $dumpfile("test.vcd");
    $dumpvars(0,testbench);

  clk <= 0;
  repeat (100) begin
	#10;
	clk <= 1;
	#10;
	clk <= 0; 
  end

    $display("finish");
end

always @(posedge clk) cnt <= cnt + 1'b1;

endmodule


Перезапускаем makev.bat, нажимаем обновить в GTKWave, видим новые сигналы, добавляем их на панель Waves.

Видим, что с каждым тактом clk у нас увеличивается cnt, и линии log_sig_A и log_sig_B получают каждая свой бит. Теперь мы видим, что линии сигналов A и B меняются и из них можно попробовать комбинировать какие-то логические функции. Для примера создам две функции: одну И, второую ИЛИ:

Первый способ — самый популярный, с помощью параллельных операторов назначения: 

//wire будет содержать результат функции AND
wire method1_AND;
assign method1_AND = log_sig_A && log_sig_B;

//запись можно сократить, и описать wire сразу без assign, назначив ей функцию
wire method1_OR = log_sig_A || log_sig_B;

Проверяем:

Все верно! О логических функция можно почитать по ссылке ниже:

http://www.asic-world.com/verilog/operators1.html

 Однако, Verilog очень развит, и дает нам возможность задавать такую простую логику и через примитивы на вентильном уровне

//через примитив
wire method2_AND; 
// and - примитив, U1 - название экземпляра, первый операнд - результат
//остальные операнды - входные параметры
and U1(method2_AND,log_sig_A ,log_sig_B); 

Однако, стоит заметить что запись через примитивы менее наглядна. 

Как видим, wire — это линия, к которой присваивается результат. И к одной линии можно подключить выход только одной логической функции (что и понятно). Так же, вполне естественно, что линия может быть подключена на несколько разных входов. Однако, есть линии, к которым можно подключить выходы нескольких логических функций, при этом все результаты объединить логическим AND или логическим OR. Для этого можно применить wire соответствующих типов: WAND для объединения AND и WOR для объединения OR. После этого к линии можно присоединять выходы никак не связанных между собой выходов функций. 

//через WAND/WOR
wand method3_AND; 
assign method3_AND = log_sig_A;
assign method3_AND = log_sig_B;
//список assign можно при необходимости продолжить, при этом никак не меняя предыдущие строки

Обычно требуется создавать достаточно сложные условия, которые тяжело самостоятельно привести к примитивным логическим функциям. К тому же, требуется большая работа, чтобы эффективно упрощать такие выражения. Поэтому никто этим и не занимается: условия описываются первым способом с той целью, чтобы они были понятны разработчику. А все упрощения происходят автоматически при синтезе.

С помощью wire и операторов назначения описываются логические функции, выходной результат которых всегда однозначно определен. Такие схемы синтезируются из простых логических элементов И/ИЛИ/НЕ. И результат на выходе комбинационной схемы происходит сразу, после изменения значений на входе (но не мгновенно, т.к. логическим схемам тоже требуется время на переключение).

Для работы с регистрами используются событийные блоки always (см. выше). В нашем случае, менялось значение регистра cnt, причем только по положительному фронту сигнала clk. Список чувствительности описывается в скобках. Если указано posedge, то блок, описанный в always выполняется по положительному фронту. Так же есть возможность «ловить» отрицательный фронт — negedge, либо по любому фронту — в этом случае указывается просто сигнал, без ключевого слова, определяющего фронт.

Интересной особенностью событийных блоков и синтеза в реальное железо является то, что регистры-защелки будут синтезированы только в том случае, если при какой-то комбинации входных сигналов, выходной сигнал не должен менять своего значения. Тут понятно, что без регистра не обойтись. Но если через блок always задать логику, которая будет определять результат при любом значении сигналов из списка чувствительности, то регистр не будет создал, а логика будет описана простыми логическими примитивами.

//событийно
reg method4_AND;
always @(log_sig_A, log_sig_B) begin
	method4_AND <= log_sig_A && log_sig_B;
end

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

Более подробно об этом можно прочитать в книге:

Основы языка проектирования цифровой аппаратуры Verilog. 2014

Примеры реализации логики на Altera

 

Выводы

Сегодня мы освоили инструмент Icarus Verilog и несколько основных приемов описания логики. Теперь у нас есть возможность двигаться дальше: описать логику какого-то реального устройства, проверить правильность этой логики и после этого перейти к записи этой логики в реальное железо — ПЛИС!

0 комментариев
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.