1i7 (1i7) wrote,

Лабораторная работа2: знакомство с платой ПЛИС (FPGA) - основы комбинаторной логики (1)

Введение: Для введения я сначала приготовил небольшой документ, который появился по результатам переписки с Юрием Панчулом и Сергеем Вакуленко, следуя которому я хотел рассказать про индустрию инструментов автоматизации проектирования электронных приборов (EDA), цикл разработки и производства микропроцессора и про текущее состояние на рынке инженеров цифрового дизайна. Но через 3 минуты после начала стало понятно, что информация о том, что для разработки процессоров используются платы ПЛИС и язык Verilog, не очень хорошо впитывается в людей, которые платы ПЛИС и язык Verilog никогда перед этим раньше не видели, поэтому рассказ по содержимому документа я решил оставить до более подходящего случая (например в качестве подведения итогов по области ПЛИС после лабы с процессором) и сразу приступить к практической части - знакомству с платами ПЛИС и языком Verilog.

1. Запуск среды разработки и создание проекта для платы Digilent Basys 2

Запустить среду разработки специально подготовленным скриптом (см установка программного обеспечения):


> /opt/Xilinx/bin/start_ise.sh

или 2мя командами в терминале:


> . /opt/Xilinx/13.4/ISE_DS/settings32.sh
> ise &

На появившемся экране нажать кнопку "New Project...", в появившемся диалоге "New Project Wizard" ввести имя проекта "basic_boolean" (место положения проекта можно сменить по желанию), нажать Next:




На 2м экране "Specify device and project properties." указать следующие настройки для платы ПЛИС Digilent Basys 2:

Product Category: All
Family: Spartain3E
Device: XC3S100E
Package: CP132
Speed: -5

Проверить, что поле Simulator имеет значение ISim, Preffered Language = Verilog (оба значения должны быть уже выбраны по умолчанию) и нажать Next



Экран со сводкой по новому проекту - просто Finish




2. Создать файл с исходным кодом на Verilog - пустой модуль

Далее создаем первый файл с исходниками на верилоге: меню Project > New Source..., в открывшемся диалоге "New Source Wizard":
- выбираем в списке слева тип файла исходного кода (Source Type) = Verilog Module
- Вводим справа в поле File name: basic_boolean

кнопка Next



Второй экран ("Specify ports for module") определения портов ввода и вывода для модуля - можно ничего не менять - позже определим их вручную - Next



Сводка информации по новому файлу - Finish



Получили открытый в редакторе новый файл с исходниками модуля на языке Verilog - basic_boolean.v. Всю шапку из зеленых комментариев и директивы "`timescale 1ns / 1ps" можно сразу удалить - оставить только главное определение (пока пустого) модуля:


module basic_boolean(
    );

endmodule





3. Структурная единица дизайна на Verilog - модуль как черный ящик со входами и выходами

Далее небольшое разъяснения о том, что из себя представляет модуль HDL (Verilog). Модуль Verilog - это основная структурная единица построения цифровой системы - по сути полный аналог физического модуля цифровой логики (logic gate) из 1й лабораторной работы (см упражнение 7 - модуль 4011 - 4NAND) - черный ящик с ножками - на какие-то ножки (input) подается входной сигнал - параметры модуля, с других ножек (output) считывается выходной сигнал - результат работы модуля.



тот же модуль в виде таблицы истинности

Определение любого модуля начинается с перечисления его входов (input) и выходов (output) в зависимости о того, что от этого модуля требуется.

В модуле basic_boolean будет проверена работа базовых булевых операторов NOT, AND, OR, NAND, поэтому у него будет два входа (2 операнда - a и b) и несколько выходов - по одному на каждый оператор (для NOT пусть будет 2) - возьмем:



- два входа (input): a и b
- пять выходов (output): not_a, not_b, a_and_b, a_or_b, a_nand_b.

Визуально с точки зрения синтаксиса в коде это выглядит почти как определение параметров для функции или переменных - роль переменной input/output + имя переменной ("физически" и по механизму работы это конечно же никакие не функции, а скорее классы или объекты, но не суть) - добавляем код:

module basic_boolean(input a, input b,
    output not_a, output not_b, output a_and_b,
    output a_or_b, output a_nand_b
    );

endmodule




Замечание: модульный подход при определении структуры программы в Verilog должен оказаться очень хорошо понятен тем, кто знаком с принципами объекто-ориентированного программирования (ООП) - т.е. всем Java и С++-программистам, для которых предназначен этот курс. По сути модуль - это идеальный объект - у него есть публичный внешний интерфейс в виде входов и выходов и есть закрытое инкапсулированное внутреннее состояние в виде регистров (до них дойдем будущих лабах) - нет разве только полиморфизма и наследования, но это уже абстракции из другой области. А идеальный этот объект потому, что в данном случае инкапсуляция и внешний интерфейс по сути реализованы на физическом уровне, а не абстрактными механизмами среды выполнения программы.




4. Базовые булевы операторы

В теле модуля входами и выходами можно оперировать примерно как переменными внутри привычной программы - переменным-выходам можно присваивать значения при помощи оператора assign - при этом значением может быть резульатат действия стандартных булевых операторов (или их комбинаций) над переменными-входами (или другими внутренними переменными или числовыми константами).

Каждый вход и выход в данном случае несет ровно 1 бит информации (1=TRUE, 0=FALSE). Значения 2х входных битиков конвертирует в значения 5ти выходных битиков - добавляем еще несколько строк кода:

basic_boolean.v
module basic_boolean(input a, input b,
    output not_a, output not_b, output a_and_b,
    output a_or_b, output a_nand_b
    );

assign not_a = ~a; // NOT
assign not_b = ~b; // NOT
assign a_and_b = a & b; // AND
assign a_or_b = a | b; // OR
assign a_nand_b = ~(a & b); // NAND

endmodule





Все, модуль готов - за 3 минуты при помощи нескольких строк текста было реализовано то, на что на первой лабораторной работе с макетными платами ушло два с половиной часа. Теперь осталось только этот код каким-то образом где-то запустить - для этого подойдет живая плата ПЛИС (FPGA) или программный симулятор. Начать конечно приятнее с живой ПЛИС, чтобы сразу увидеть результат работы в действии.


4. Синтез (synthesis) для ПЛИС (FPGA)




Перед синтезом для платы ПЛИС нужно сначала понять, что любая плата ПЛИС опять же представляет из себя черный ящик - внутри него находится прошивка, синтезированная из программы на Verilog, а снаружи опять же торчат разные устройства ввода (рычажки, кнопки, порт для клавиатуры и т.п) и вывода (отдельные светодиоды, светодиодный дисплей, порт для VGA-дисплея и т.п.), которые с одной стороны доступны человеку, работающему с платой (понажимать кнопки, посмотреть на светодиоды), а с другой - доступны для манипуляций внутри программы (считать значение вкл/выкл с определенного рачажка, подать значение вкл/выкл в нужный светодиод).

Соответственно для того, чтобы программа на Verilog получила возможность начать общение с устройствами ввода/вывода конкретной платы, нужен некий механизм, который ей в этом поможет. Для этого в среде разработки Xilinx ISE WebPack используется механизм специальных файлов с расширением ucf.

4.1. Подключение ucf-файла с описанием структуры платы ПЛИС

Все устройства ввода-вывода на плате ПЛИС пронумерованы уникальными идентификаторами. На Dililent Basys 2 эти идентификаторы можно посмотреть прямо на самой плате или же узнать из документации, доступной на сайте продукта.




см белые подписи рядом с каждым устройством




Устройства ввода рычажки SW и ввода-вывода PIO (о них ниже)

Для проверки работы модуля basic_boolean выберем два рычажка (каждый из которых может находиться в 2х состояниях - LOW/HIGH=0/1=FALSE/TRUE) - для входных параметров a и b и 5 светодиодов (которые тоже могут находиться в 2х состояниях - ВЫКЛ/ВКЛ=LOW/HIGH=0/1=FALSE/TRUE) - для выходных значений not_a, not_b, a_and_b, a_or_b, a_nand_b.



Рычаги-переключатели на плате Basys 2 называются SW - пронумерованы от 0ля до 7ми справа налево, для каждого рычага указан уникальный код - например для 1го рычага SW[0] указан код "P11", для 2го рычага SW[1] указан код "L3" и т.п.

Светодиоды на плате Basys 2 называются LD (расположены в ряд прямо над рядом рычагов) - пронумерованы от 0ля до 7ми справа налево, для каждого светодиода также указаны коды LD[0] - "R45", LD[1] - "R46" и т.п.

Все эти имена и коды устройств перечислены в файле basys2.ucf, который можно скачать или опять на сайте платы Digilent Basys 2 или взять в моем репозитории с исходными кодами лабораторной работы на github.

Для начала файл basys2.ucf нужно подключить к проекту - выбрать меню Project > Add Copy of Source... и найти в файловой системе только скачанный файл basys2.ucf:



Файл должен появиться в дереве проекта, после этого его нужно открыть в редакторе и оставить только те строки, которые соответствуют используюемым в проекте устройствам ввода/вывода - строки для остальных устройств нужно или удалить или закомментировать (символ '#' в начале строки), т.к. иначе система выдаст ошибку при синтезе прошивки:

basys2.ucf
# Pin assignment for LEDs
NET "ld<4>" LOC = "n5" ;
NET "ld<3>" LOC = "p6" ;
NET "ld<2>" LOC = "p7" ;
NET "ld<1>" LOC = "m11" ;
NET "ld<0>" LOC = "m5" ;

# Pin assignment for slide switches
NET "sw<1>" LOC = "l3";
NET "sw<0>" LOC = "p11";



4.2. Создание модуля верхнего уровня для манипуляции реальными устройствами ввода/вывода платы ПЛИС

Как уже было сказано выше, основная структурная единица дизайна на Verilog - это модуль с набором входов и выходов. В рамках дизайна можно "размещать" модули на одном уровне друг рядом с другом и подключать выходы одного модуля к входам другого. А еще можно поместить несколько модулей внутрь другого модуля более высокого уровня - тогда входы внешнего модуля будут подключены к входам внутренних модулей, а выходы внутренних модулей будут подключены к выходам внешнего модуля. Таким образом дизайн на Verilog может расти как вширь, так и вглубь на любое количество уровней.



Очевидно, что при таком вложенном размещении модулей должен получиться один главный модуль самого верхнего уровня (top module), который будет содержать все остальные модули дизайна вложенные один в другой.

При синтезе для ПЛИС (FPGA) в качестве такого модуля верхнего уровня (top module) выбирается модуль, входами и выходами которого являются реальные устройства ввода/вывода, доступные на плате ПЛИС (в нашем случае рычажки и светодиодики), а внутри этого модуля уже можно размещать другие логические модули дизайна (в нашем случае один модуль - basic_boolean) и направлять сигналы с реальных рычажков sw[0] и sw[1] на виртуальные входы модуля basic_boolean a и b.



Любое устройство ввода/вывода на плате (в том числе каждый отдельный рычаг sw и каждый отдельный светодиод ld) всегда может принимать/отдавать два возможных значения - традиционно HIGH=TRUE=1 или LOW=FALSE=0.

Создаем новый модуль Verilog как было показано выше (Project > New Source... > Verilog Module) basic_boolean_basys2, определяем входы и выходы с теми именами, которые указаны в файле basys2.ucf и перенаправляем их в новый "экземпляр" модуля basic_boolean (типа как создание объекта-экземпляра класса - при желании их можно определить сразу несколько) в теле модуля:

basic_boolean_basys2.v
module basic_boolean_basys2(
    input [0:1] sw,
    output [0:4] ld);

basic_boolean impl(.a(sw[0]), .b(sw[1]),
    .not_a(ld[0]), .not_b(ld[1]),
    .a_and_b(ld[2]), .a_or_b(ld[3]),
    .a_nand_b(ld[4]));

endmodule

параметы sw и ld можно указывать прямо в виде "массивов" (каждый элемент - один "проводок" - 1 бит информации 1/0).

basic_boolean - имя модуля
impl - имя конкретного экземпляра внутри текущего модуля (может быть любым)
в скобках - ассоциирование параметров текущего модуля с параметрами внутреннего модуля



Все готово - теперь значения рычагов sw[0] и sw[1] (0=FALSE или 1=TRUE) подаются в качестве значений входных параметров модуля basic_boolean a и b, а светодиоды ld[0]...ld[4] будут показывать значения результатов работы модуля basic_boolean not_a, not_b, a_and_b, a_or_b, a_nand_b (0=FALSE=выкл или 1=TRUE=вкл).


4.3. Синтез прошивки

Можно синтезировать прошивку для ПЛИС - в списке действий под деревом проекта выбрать элемент Generate Programming File, кликнуть два раза и подождать пока рядом с элементом не появится зеленый кружочек с белой галочкой внутри - значит bit-файл (basic_boolean_basys2.bit) с прошивкой готов.



Нужно убедиться, что файл basic_boolean_basys2.bit появился в каталоге проекта ("/home/user/hdl/basic_boolean").

Запустить терминал, войти в каталог /home/user/hdl/basic_boolean


> cd /home/user/hdl/basic_boolean

проверить, что файл basic_boolean_basys2.bit на месте:


> ls *.bit
basic_boolean_basys2.bit


4.4. и программирование платы ПЛИС

при помощи инструментов Digilent Adept 2 (которые установили и проверили при подготовке к лабораторной работе).

Подключить плату ПЛИС через кабель USB, включить рычаг POWER в положение ON, убедиться, что плата определилась системой:


> djtgcfg enum
Found 1 device(s)

Device: Basys2
    Product Name:   Digilent Basys2-100
    User Name:      Basys2
    Serial Number:  210155389410

Загрузить прошивку на плату:


> djtgcfg prog -d Basys2 -i 1 --file ./basic_boolean_basys2.bit
Erasing PROM. Do not touch your board. This may take a few minutes...
Erase succeeded.
Programming device. Do not touch your board. This may take a few minutes...
Programming succeeded.



Включить и выключить рычаг POWER и убедиться, что дизайн работает как нужно - подвигать рычаги sw[0] и sw[1] в разных комбинациях положений вкл/выкл и убедиться, что светодиоды ld[0]...ld[4] включаются/выключаются в соответствии с таблицей истинности модуля.

sw[0], sw[1] справа налево
ld[0]...ld[4] справа налево



a=sw[0]=FALSE, b=sw[1]=FALSE
ld[0]=not_a=TRUE
ld[1]=not_b=TRUE
ld[2]=a_and_b=FALSE
ld[3]=a_or_b=FALSE
ld[4]=a_nand_b=TRUE



a=sw[0]=TRUE, b=sw[1]=FALSE
ld[0]=not_a=FALSE
ld[1]=not_b=TRUE
ld[2]=a_and_b=FALSE
ld[3]=a_or_b=TRUE
ld[4]=a_nand_b=TRUE




a=sw[0]=FALSE, b=sw[1]=TRUE
ld[0]=not_a=TRUE
ld[1]=not_b=FALSE
ld[2]=a_and_b=FALSE
ld[3]=a_or_b=TRUE
ld[4]=a_nand_b=TRUE



a=sw[0]=TRUE, b=sw[1]=TRUE
ld[0]=not_a=FALSE
ld[1]=not_b=FALSE
ld[2]=a_and_b=TRUE
ld[3]=a_or_b=TRUE
ld[4]=a_nand_b=FALSE

Небольшое видео с процессом синтеза и программирования платы ПЛИС, чтобы тем, у кого платы ПЛИС под рукой пока нет, было видно как все происходит действительно просто, но немного долго:



5. Знакомство с симуляцией (simulation)

Перед тем, как запускать дизайн на ПЛИС или если под рукой нет платы ПЛИС, работоспособность модулей Verilog хорошо бы проверить сразу на компьютере - специально для этой задачи созданы симуляторы.

В самом простом случае симулятор позволяет программно задать значения (1=TRUE=HIGH/0=FALSE=LOW) на входы модуля и показывает на специальном графике, какие при этом значения (опять же 1=TRUE=HIGH/0=FALSE=LOW) генерируются на выходах модуля.

Для того, чтобы самостоятельно манипулировать тестовыми значениями, подаваемыми на вход тестируемого модуля, можно создать еще один специальный модуль верхнего уровня basic_boolean_test, внутри которого будет находиться тестируемый модуль basic_boolean. Все точно также, как в случае с модулем верхнего уровня basic_boolean_basys2, только вместо живых устройств ввода/вывода к входам и выходам внутреннего модуля будут подключены внутренние переменные тестового модуля basic_boolean_test.

Project > New Source... > Verilog Module > basic_boolean_test

basic_boolean_test.v
module basic_boolean_test();

reg a, b;
wire not_a, a_and_b, a_or_b, a_nand_b;

basic_boolean impl(a, b,
    not_a, a_and_b, a_or_b, a_nand_b);

always begin
    a = 0; b = 0; #1;
    a = 1; b = 0; #1;
    a = 0; b = 1; #1;
    a = 1; b = 1; #1;
end

endmodule

Новые конструкции:
- wire - позволяет определять внутренние переменные модуля - почти аналогично input/output, только их не видно снаружи
- reg - определить регистр - пока без пояснений (подробно будет разобрано в следующей лабораторной работе) - можно считать, что тоже, что wire
- always begin/end - будем считать, что бесконечный цикл
- #1 (#N) - задать паузу между "выполнением" двух инструкций (N - длительность паузы) - может потребоваться для удобства анализа логики кода на графике симулятора - работает только в режиме симуляции - при синтезе для реальной ПЛИС инструкция игнорируется

В Xilinx ISE WebPack встроен симулятор ISim. Чтобы его запустить, нужно перейти в режим симуляции (см. переключатель "Simulation" над списком модулей проекта), выбрать в списке модулей тестовый basic_boolean_test и в списке действий два раза кликнуть на ISim Simulator > Simulate Behavioral Model



Через несколько секунд должно открыться окно с графиком симуляции. Несколько раз нажать на кнопку "Zoom Out" на панели инструментов до тех пор, пока не появятся ломаные волны, полосу прокрутки внизу графика лучше отмотать до упора влево.

В столбце Name перечислены имена внутренних переменных модуля basic_boolean_test, в столбце Value перечислены их текущие значения для выбранной позиции на графике - текущая позиция обозначена вертикальной желтой полосой, ее положение можно менять кликая мышкой в нужном месте графика.

Глядя на график можно убедиться, что значения переменных a и b меняются со временем именно так, как это было определено в модуле basic_boolean_test с учетом расставленных пауз, а значения остальных переменных типа not_a,..,a_nand_b меняются в соответствии с таблицей истинности модуля basic_boolean.




продолжение см Лабораторная работа2 (2)
Tags: verilog, ПО, плис, цифровая электроника для программистов
  • Post a new comment

    Error

    default userpic

    Your IP address will be recorded 

  • 4 comments