Ты ждал знак? Вот он!
Много лет я не решался начать программировать ПЛИС, потому что это сложно, дорого и больно (как мне казалось). Но хорошо, когда есть друзья, которые помогают сделать первый шаг. И теперь я не понимаю одного - ПОЧЕМУ Я ЖДАЛ ТАК ДОЛГО?
Сейчас я помогу сделать первый шаг и тебе!
Хочешь наконец вздохнуть свободно? Идем дальше!
В тот вечер я понял: ВОТ ОНО! Вот та платформа, в которой я быстро и легко могу превращать свои мысли в реально работающее железо!
В семействе Altera, за разумные деньги мы можем купить платы с CPLD MAX II на 240, 570 и 1270 элементов, либо более старшие микросхемы FPGA, которые Cyclone 1, 2, 3, 4 с количеством до 10000 и более ячеек. Как же выбрать?
Даже на базе 240 ячеек, проект Марсоход делает просто огромное количество проектов . Настоятельно рекомендую ознакомиться, чтобы иметь примерное представление о сложности проектов, которые можно уместить в 240 ячеек. С другой стороны, существуют проекты , которые полностью программируются под аппаратную копию определенного ПК, включая процессор и всю логику вокруг него (NES , Speccy , Orion , ЮТ-88 , etc). Для этого уже требуется пять, десять и более тысяч ячеек. Плюс эти платы содержат дополнительные внешние устройства.
Поэтому я бы посоветовать взять что-то среднее между 240 и 10000 ячейками, с предпочтением в сторону увеличения в зависимости от доступных средств. На отладочной плате лишние ячейки это не страшно, а если их не хватит - уже ничего не поделаешь. Потом, когда устройство отлажено, станет ясно, сколько надо ячеек, купить под нужное количество, без лишнего «обвеса», дешевле и оставить в готовом устройстве.
То, чем действительно отличаются MAX от Cyclone"ов, кроме количества ячеек, это:
1) У серии MAX внутри нет PLL. На каждой отладочной плате есть генератор, как правило на 50 МГц. Основной массе проектов этого будет достаточно. Все синхронизации будут происходить путем деления 50 МГц на какое-нибудь значение. Либо, можно взять внешний генератор и подать на отдельный вход ПЛИС. А что, если потребуется частота выше 50 МГц? Мне не удалось с ходу найти генераторы выше 50 МГц. Но тут как раз на помощь и приходит PLL, который встроен в Циклоны. На нем можно умножить частоту, например, до 100 МГц.
2) В серии Cyclone встроены аппаратные блоки умножения. Их количество зависит от конкретной модели - тут как раз можно «всетаки заглянуть в инструкции», чтобы узнать сколько. Если предполагаете делать какой-то ЦОС, то они пригодятся: сэкономят ячейки, увеличат скорость. С другой стороны, если нет умножителей, их можно синтезировать, но у маленькой ПЛИС на это может не хватить ресурсов.
Во всем остальном у меня критерий «влезло/не влезло». Отладка на заведомо бОльшей, чем нужно плате, с последующей заливкой в минимально необходимую для этого.
Программатор
Я считаю, что у меня нет времени, чтобы паять программаторы на рассыпухе.
300 рублей. Я свой брал на ебее , выглядит так:
Отладочная плата
Выбор широкий, в зависимости от количества денег.
Начальный уровень
350 - 550 рублей. Это платы на MAX II ( или ячеек). Могут подойти для начального ознакомления и дальнейшего пристройства в конечные устройства. На плате есть генератор, пара кнопок, пара светодиодов, остальные 80 выводов на свое усмотрение.
Блок питания
Обязательно должен быть, но не всегда идет в комплекте. Потребуется БП на 5 вольт и ток 2А.
Средний уровень
от 900 до 1500 рублей. Это платы Cyclone 1, 2, 3, 4 отличающиеся в основном количеством ячеек.
Маркируются примерно так:
EP2
C5
T144 - Cyclone 2 примерно 5к ячеек
EP4
CE6
E22C8N - Cyclone 4 примерно 6к ячеек
EP2
C8
Q208C8N - Cyclone 2 примерно 8к ячеек
Можно заметить, что Cyclone 3 может иметь больше ячеек, чем Cyclone 4.
Вот несколько вариантов:
835 рублей.
ALTERA FPGA CycloneII EP2C5T144 Minimum System Board for Learn good
880 рублей
Altera CycloneII EP2C5T144 FPGA Mini Development Learn Core Board E081
1265 рублей
EP2C8 EP2C8Q208C8N ALTERA Cyclone II FPGA Evaluation Development Core Board
Платы с расширенными возможностями . Это платы, на которых установлены дополнительные модули (UTP, USB, AUDIO), разъемы (SD, VGA), кнопки, переключатели, светодиоды, семисегментные индикаторы и т.д. Либо может идти базовая плата, а к ней могут прилагаться платы расширения отдельно.
У меня трудится такой комплект - плата + плата расширения:
Altrea EP4CE10E22 FPGA CORE Board+ Device Board USB/Sound/Ethernet/SD Card/VGA
2760 рублей
Вот основная плата. На ней есть 2 светодиода, 2 кнопки, 4 переключателя, семисегментный интикатор и микросхема оперативной памяти.
Плата расширения. На ней распаяны SD, VGA, а так же контроллеры USB(High Speed USB2.0 Chip: CY7C68013A), AUDIO(Sound Card up to 96kHz/32bit ADC/DAC: WM8731S), UTP(100M Ethernet interface: DM9000A):
Эти платы просто вставляются одна в другую, но у меня она пока лежит в ящике. Для своих поделок у меня макетка, с которой я соединяюсь шлейфом, который идет в комплекте. Еще в комплекте идет блок питания на 5 вольт.
Начну свою первую статью с того, что сообщу: в предмете статьи я сам новичок, но выбрал именно такую тему. Объясню почему. Читаю хабр уже достаточно долго и мне всегда были интересны топики тех, кто сам в настоящий момент изучает то, о чем повествует. Такие статьи всегда понятны, всегда находят свою аудиторию и всегда предают читающему интерес и энтузиазм автора, который у новичков в любой области обычно зашкаливает!
ПЛИС (Программируемая логическая интегральная схема) – один из видов электронных устройств, применяемый для реализации различных логических устройств самой разной сложности, от двоичных счетчиков, несложных логических схем (замены стандартных интегральных схем - рассыпухе) до специализированных процессоров и нейрочипов.
С назначением разобрались, вопрос – как? Внутри ПЛИС находятся некие базовые элементы, которые соединяются на основе конфигурационной записи. Возможные базовые элементы, вид и место хранения конфигурационной записи зависят от вида ПЛИС и от конкретной модели. В современных ПЛИС выделяют два вида: CPLD и FPGA, уделим им по абзацу.
CPLD (complex programmable logic device - сложные программируемые логические устройства) - ПЛИС, базовыми элементами которой являются макроячейки и простые логические вентили (И(-НЕ)/ИЛИ(-НЕ)). Обычно содержит меньше базовых элементов, чем FPGA, но является более быстродействующей. Также обычно содержит энергонезависимую конфигурационную память прямо на кристалле, но имеет ограниченное число циклов конфигурирования.
FPGA (field-programmable gate array - Программируемая пользователем вентильная матрица) – ПЛИС, которые обычно имеют целый букет видов базовых блоков, это и настраиваемые логические элементы (таблицами истинности) и блоки сложения-умножения (Digital signal processing - DSP) и PLL (Phase-Locked Loop) для деления и умножения частоты и некоторые другие в зависимости от модели. Обычно имеют энергозависимую внутреннюю память и функционал для загрузки конфигурации с внешней энергонезависимой памяти.
Определения этих видов ПЛИС и разницы между ними можно встретить самые различные, как в книгах, так и в интернете. Поэтому не стоит зацикливаться на этой разнице, производители сами классифицируют выпускаемые ими ПЛИС. Если вы не запомнили аббревиатур или не поняли значений некоторых слов – не страшно, главное чтобы появилось общее понятие о ПЛИС, надеюсь я этого добился. А теперь к практике!
Начнем с обсуждения инструментов. Я буду пользоваться стартовым набором разработчика Altera Cyclone II FPGA Starter Board, это готовая плата, на которой установлена FPGA серии Cyclone II – EP2C20F484C7N, а также различная периферия и интерфейсы. В этой статье мы используем светодиоды и семисегментные индикаторы. Мы не будем использовать никаких специальных блоков данной серии FPGA, поэтому при желании вы можете использовать почти любую другую ПЛИС (FPGA и CPLD).
Сильно заинтересовавшиеся могут купить себе один из наборов разработчика или самим собрать устройство, что является достаточно сложной для новичка, но вполне выполнимой задачей. Схемы программаторов и схемы подключения самих ПЛИС легко гуглятся, к тому же, кто ранее занимался любительской прошивкой AVR, может обнаружить у себя подходящий программатор Altera Byte Blaster. В общем, схемную реализацию я предоставляю тебе хабраюзер (В конце статьи схема моей Starter Board). Кто хочет попробовать без денежных затрат и увидеть результат работы – можно использовать встроенный в Quartus II симулятор (в этой статье работа с ним не описана).
Из ПО мы будем использовать Quartus II, free версию которого вы сможете найти на сайте производителя (Altera), как в Windows, так и в Linux вариантах.
И вот мы подошли к практике вплотную! Запускаем наш САПР Quartus II и создаем проект. Первыми шагами визарда указываем имя проекта и его место дислокации, затем пропустим шаг добавления файлов (еще успеем). Закончим создание проекта на этапе выбора устройства, если делаем на железяке – точно знаем имя ПЛИС, его и выбираем. Если просто делаем проект для ПЛИС выберем что-нибудь помощней, например третий циклон. Я же выбираю FPGA которая установлена в моем стартер ките.
Жмем Finish – проект создан. Структура проекта в Quartus – иерархическая, нам необходимо выбрать верхушку иерархии (Top-Level Entity). Мы можем использовать для проектирования схемные файлы и файлы с описанием логики на одном из HDL (Hardware description language – язык описания аппаратуры). Мое мнение – наглядней всего в качестве верхушки иерархии использовать схемный файл с основными блоками логики, а сами блоки реализовывать на HDL. Мнения могут быть разными, пока выбирал, прочитал немало холиварных тем, но остановился пока на такой модели, вы можете сделать свой выбор. В качестве HDL языка проекта я выбрал VHDL ((Very high speed integrated circuits) Hardware Description Language), вы можете использовать любой другой, например Verilog или AHDL, все зависит от ваших предпочтений.
Создаем наш первый файл проекта (File – New..) выбираем Block Diagram/Schematic File. Теперь давайте нарисуем простейшую схему, добавим один Input, один Output и соединим их (в реальной ПЛИС эта схема будет передавать сигнал с одной ножки на другую). Для этого Double Click на пустом месте схемы и в открывшемся диалоге Symbol выбираем необходимый элемент.
Соединяем просто так, линией. Дадим пинам имена (Double Click по элементам), input назову CLOCK_27, а output назову LEDR. Имена выбраны не случайно – CLOCK_27 я затем ассоциирую с входом генератора 27Mhz, а LEDR с нулевым красным светодиодом. Сохраняем файл, оставив галочку добавления в проект. Теперь установим полученный файл вершиной иерархии проекта. Для этого окне Project Navigator, во вкладке Files, в контекстном меню нашего файла выбираем Set as Top-Level Entity. Hello, world готов. Компилируем проект (Processing – Start Compilation), если видим Info: Quartus II Full Compilation was successful. – радуемся и считаем, что первый этап пройден.
Теперь разберемся с нашими целями. Наше устройство будет при включении питания начинать отсчет минут и часов. Значит нам понадобится четыре семисегментных индикатора «ЧЧ: ММ». Для отсчета времени нам нужен более-менее точный сигнал 1Hz. Его мы получим путем деления частоты 27Mhz, затем мы будем отсчитывать его на 60 (секунды), потом еще раз на 60 (минуты), а потом на 24 (часы). С последних двух блоков двоичное число минут и часов будет поступать на декодер Bin -> BCD (binary-coded decimal) -> 7seg. Вот, в общем, и все устройство. Сразу оговорюсь, что схема будет асинхронная (Минуты заводятся от секунд, а часы от минут), для простоты и наглядности.
Итак, создадим наш первый блок - блок деления частоты. Создадим новый файл, как мы уже умеем, только тип файла будет VHDL File. Вставим в него код:
Library ieee;
use ieee.std_logic_1164.all;
Entity Div_27Mhz_to_1Hz is
port(clk:in std_logic; clk_out:out std_logic);
end Div_27Mhz_to_1Hz;
Architecture div_behavior of Div_27Mhz_to_1Hz is
begin
process(clk)
variable cnt: integer range 0 to 27000000;
begin
then
If(cnt >= 13500000)
then
clk_out <= "1";
else
clk_out <= "0";
end if;
If(cnt = 27000000)
then
cnt:= 0;
else
cnt:= cnt + 1;
end if;
End if;
end process;
end div_behavior;
Пропущу служебные директивы (можно посмотреть в справочнике, ссылки в конце), уделю внимание только логике работы. Вначале мы объявляем сущность, т.е. сам блок. Указываем его входы и выходы, их типы и имена. Тип std_logic в простонародье значит бит. Далее мы описываем внутреннюю архитектуру этого блока. Архитектура состоит из параллельных процессов. Каждый процесс имеет свой список чувствительности, например единственный процесс в примере выше чувствителен к изменениям на входе clk. Для процесса можно объявить переменные, в нашем примере это переменная типа integer range 0 to 27000000. Далее в теле процесса задается элементарная логика: пока не прошла половина периода - пихаем в выход логический ноль, как половина прошла – пихаем единицу, при этом не забываем считать и обнулять счетчик по достижению 27000000. На идеальный код не претендую – пока учусь, буду рад поправкам:)
Сохраняем файл с кодом и создаем символ (File – Create/Update – Create Symbol Files For Current Files), это необходимо для того чтобы вставить данный блок в нашу главную схему. Найти свои символы можно в папке Project в диалоге вставки символа. Теперь пробежимся по остальным блокам менее подробно.
Library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
Entity cnt_0_to_59 is
port(clk:in std_logic; c59:out std_logic; vector:out std_logic_vector(5 downto 0));
end cnt_0_to_59;
Architecture cnt_behavior of cnt_0_to_59 is
begin
process(clk)
variable cnt: integer range 0 to 59;
begin
if(clk"event and clk = "1")
then
if(cnt = 59)
then
cnt:= 0;
c59 <= "1";
vector <= CONV_STD_LOGIC_VECTOR(cnt, 6);
else
cnt:= cnt + 1;
c59 <= "0";
vector <= CONV_STD_LOGIC_VECTOR(cnt, 6);
end if;
end if;
end process;
end cnt_behavior;
Это блок счета от нуля до 59, который мы используем для счета минут и секунд. Из новинок тут тип выхода std_logic_vector(5 downto 0), который определяет группу битов (битовый вектор), а также функция CONV_STD_LOGIC_VECTOR(cnt, 6), которая преобразует переменную в битовый вектор указанной длины.
Library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
For CONV_STD_LOGIC_VECTOR:
use ieee.std_logic_arith.all;
Entity cnt_0_to_23 is
port(clk:in std_logic; vector:out std_logic_vector(4 downto 0));
end cnt_0_to_23;
Architecture cnt_behavior of cnt_0_to_23 is
begin
process(clk)
variable cnt: integer range 0 to 23;
begin
if(clk"event and clk = "1")
then
if(cnt = 23)
then
cnt:= 0;
vector <= CONV_STD_LOGIC_VECTOR(cnt, 5);
else
cnt:= cnt + 1;
vector <= CONV_STD_LOGIC_VECTOR(cnt, 5);
end if;
end if;
end process;
end cnt_behavior;
Выше счетчик часов. Ничего нового.
Library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
For CONV_STD_LOGIC_VECTOR:
use ieee.std_logic_arith.all;
Entity bin2bcd_5bit is
port(bin:in std_logic_vector(4 downto 0);
bcd1:out std_logic_vector(3 downto 0);
bcd10:out std_logic_vector(3 downto 0)
);
End bin2bcd_5bit;
Architecture converter_behavior of bin2bcd_5bit is
begin
process(bin)
variable i: integer range 0 to 23;
variable i1: integer range 0 to 9;
begin
i:= conv_integer(bin);
i1:= i / 10;
bcd10 <= CONV_STD_LOGIC_VECTOR(i1, 4);
i1:= i rem 10;
bcd1 <= CONV_STD_LOGIC_VECTOR(i1, 4);
end process;
end converter_behavior;
Преобразователь Binary в BCD, по сути, просто разбивает одно бинарное число на два, каждое из которых представляет разряд десятичного числа. Из новинок – оператор rem, остаток от деления. Аналогично написан и преобразователь для шести бит, его приводить не буду.
Library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
Entity BCD_to_7seg is
port(
BCD:in std_logic_vector(3 downto 0);
seg:out std_logic_vector(6 downto 0)
);
End BCD_to_7seg;
Architecture conv_behavior of BCD_to_7seg is
begin
process(BCD)
begin
if BCD = "0000" then seg <= "0000001";--0
elsif BCD = "0001" then seg <= "1001111";--1
elsif BCD = "0010" then seg <= "0010010";--2
elsif BCD = "0011" then seg <= "0000110";--3
elsif BCD = "0100" then seg <= "1001100";--4
elsif BCD = "0101" then seg <= "0100100";--5
elsif BCD = "0110" then seg <= "0100000";--6
elsif BCD = "0111" then seg <= "0001111";--7
elsif BCD = "1000" then seg <= "0000000";--8
elsif BCD = "1001" then seg <= "0000100";--9
else seg <= "1001001";--err
end if;
end process;
end conv_behavior;
Преобразователь одного разряда в семисегментный код, реализованный простой таблицей истинности. Сам семисегментный код представляет собой битовое представление горящих сегментов на индикаторе в порядке abcdefg, в моем случае еще и инверсное.
Недавно я все-таки сделал свой первый шаг к ПЛИС и . Мое фанатическое увлечение ПЛИС и идея о том, что ПЛИС является лучшей платформой для создания любых устройств приобрела религиозный характер. Моя секта ПЛИСоводов проповедует полный отказ от микроконтроллеров, а особо экстремистская ветвь проповедует отказ не только от софт процессоров , но и вообще от последовательных вычислений!
Как всегда, постижению истин помогло решение реальных задач. В сегодняшней проповеди я хотел бы рассказать об испытаниях, которые выпадают на долю молодого ПЛИСовода. Преодолевая испытания мы постигаем истину. Но остаются вопросы, на которые я не нашел ответов. Поэтому я бы очень хотел, чтобы братья-хабровчане - ПЛИСоводы с опытом, поучаствовали в обсуждении, протянули руку помощи своим младшим собратьям.
Эта статья для новичков. В ней я опишу типичные проблемы, вопросы, заблуждения, ошибки, которые могут появиться в самом начале обучения (потому что они появились у меня). Однако, контекст статьи ограничен тем, что разработка ведется на ПЛИС от Altera в среде Quartus на языке Verilog .
Но в своих разработках я пришел к тому, что наличие реальной ПЛИС мне требуется уже на финальном этапе разработки, когда нужно протестировать проект «в железе». Речь о том, что бóльшую часть времени я провожу в отладке своего кода с помощью симуляторов.
Поэтому мой совет: отсутствие ПЛИС - это не повод бездействовать. Пишите и отлаживайте модули для ПЛИС в симуляторах!
Попробовать прямо сейчас
Для начала работы в среде Windows, достаточно скачать по ссылке http://bleyer.org/icarus/ файл установки iverilog-20130827_setup.exe (development snapshot)
Установка трудностей не вызывает. Теперь немного забежим вперед: создадим папку для проекта и в ней пару файлов с пока что не понятным содержимым:
Файл-модуль с кодом для тестирования модулей - bench.v
`timescale 1ns / 100 ps 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
BAT Файл, который скомпилирует и просимулирует главный модуль, добавив другие модули из текущей папки - makev.bat
iverilog -o test -I./ -y./ bench.v vvp test pause
После симуляции наступает пора запуска Quartus. Но загружать прошивку в ПЛИС пока еще рано. Нужно убедиться, что божественная вычислительная машина правильно поняла, какую схему мы хотим получить, изложив свои мысли в виде Verilog"а.
Пытливый котенок, начинает искать взаимосвязь между своими действиями и результатом («голубиное суеверием»).
//задаю 8 битный регистр
reg val;
//инициирую его значением
initial val <= 8"d0240;
//wire к которому подключим выход из модуля
wire out_data;
//неведомый модуль, называется bbox
//экземпляр этого модуля называется bb_01
//будем считать, что в модуле есть входной порт in_data и выходной out_data
//во входной порт подаем значение с регистра val, а выход подключаем к wire - out_data
bbox bb_01(.in_data(val), .out_data(out_data));
Казалось бы в чем подвох? В императивных ЯП мы часто задаем переменные в качестве констант и потом ни разу их не меняем и все работает. Что же мы видим в железе?
Во-первых, мы не видим регистра. Во-вторых, на вход модуля подано 8"hFF вместо наших 8"d0240! И этого уже достаточно для того, чтобы схема заработала не так, как мы планировали. То, что регистра нет - это нормально. В Verilog можно описывать логику разными способами, в то же время, синтезатор всегда оптимизирует аппаратную реализацию. Даже если написать блок always и в нем работать с регистрами, но при этом выходное значение всегда будет определяться входными, то применение регистра тут окажется лишним и синтезатор его не поставит. И наоборот, если при каких то значениях входных данных выходное значение не меняется, то тут никак не обойтись без регистра-защелки и синтезатор его создаст. (Книга 1 стр. 88-89). Что из этого следует? Если мы начнем менять значение регистра, например, в зависимости от нажатии кнопок, то геристр уже будет создан и все будет работать так, как нужно. Если же окажется, что кнопки ничего не меняют, то синтезатор его опять же выкинет и опять все поломается. Что же делать с константой? Нужно подать ее напрямую на вход модуля:
Bbox bb_01(.in_data(8"d0240), .out_data(out_data));
Теперь на входе модуля мы имеем правильное значение:
Остается загадкой, почему при сокращении регистра, его значение в initial не подставляется на вход модуля.
Bbox bb_01(.in_data(8"d0240), .out_data(int_data));
other_bbox bb_02(.in_data(int_data), .out_data(out_data));
Предупреждение
Warning (10236): Verilog HDL Implicit Net warning at test.v(15): created implicit net for "int_data"
Результат:
Как видим, один бит подключен, а остальные 7 бит получаются не подключены (NC). Чтобы такой проблемы не было - нужно создать wire самостоятельно. Не зря компилятор IcarusVerilog выдает не warning, а ошибку, если wire не задан заранее.
Wire int_data; bbox bb_01(.in_data(8"d0240), .out_data(int_data)); other_bbox bb_02(.in_data(int_data), .out_data(out_data));
Компьютер не будет лазать по модулям, смотреть, какая разрядность портов. К тому же, разрядность может оказаться разной, а на вход модуля или с выхода берутся не все биты, а какие-то определенные.
То становится ясно, каким образом разрабатываются устройства на ПЛИС: вся задача делится на аппаратные блоки, а данные между ними движутся по конвеерам, синхронно защелкиваясь в регистрах по тактовому сигналу. Таким образом, зная общую тактовую частоту, синтезатор рассчитывает максимальную частоту работы всех комбинаторных схем, определяет, укладывается ли их скорость к период такта и делает вывод - будет или не будет работать схема в ПЛИС. Все это происходит на этапе синтеза. Если схемы укладываются в параметры, то можно прошивать ПЛИС.
Таким образом, для разработчиков устройств на базе ПЛИС созданы все необходимые методологии, и если их придерживаться, то проблем не будет.
Но что же делать, если вы не согласны с мнением синтезатора о том, что нужно выкидывать неизменяемые регистры, сокращать логические схемы? Как быть, если хотите делать схемы с асинхронной логикой? Нужна тонкая настройка? А может быть вы сами хотите собрать схему на низкоуровневых компонентах ПЛИС? Легко! Спасибо разработчикам Altera за такую возможность и подробную документацию!
Как это сделать? Можно попробовать графический редактор схем. Вы, возможно, слышали о том, что Quartus позволяет рисовать схемы? Можно самому выбрать стандартные блоки и соединить их. Но это не решение! Даже нарисованная схема будет оптимизирована синтезатором, если на это будет возможность.
В итоге мы приходим к старой истине: если ничего не помогает - прочитайте инструкцию . А именно «Altera Handbook» часть под названием «Quartus II Synthesis Options» .
Начнем с того, что описывая архитектуру на Verilog определенным образом, можно получить определенный результат. Вот примеры кода для получения синхронного и асинхронного RS триггера:
//модуль синхронного RS триггера
module rs(clk, r, s, q);
input wire clk, r,s;
output reg q;
always @(posedge clk) begin
if (r) begin
q <= 0;
end else if (s) begin
q <= 1;
end
end
endmodule
В этом случае получится синхронный триггер.
Если не брать во внимание тактовый сигнал и переключаться в зависимости от любых изменений r и s, то в результате получится элемент с асинхронной установной значений - защелка (latch).
//пример модуль асинхронного RS триггера module ModuleTester(clk, r, s, q); input wire clk, r,s; output reg q; always @(r or s) begin if (r) begin q <= 0; end else if (s) begin q <= 1; end end endmodule
Module ModuleTester(clk, r, s, q); input wire clk, r,s; output reg q; DLATCH lt(.q(q), .clrn(~r), .prn(~s)); endmodule
В итоге, весь «обвес» на входе защелки, который синтезатор посчитал нужным, исчезнет и мы получим именно то, что хотели:
Список существующих примитивов можно посмотреть на сайте Altera.
А теперь небольшой пример про асинхронность и сокращение. Задумал я, к примеру, сделать генератор по тому же принципу, как это было принято делать раньше, но только на ПЛИС:
Но для увеличения периода я возьму 4 элемента, но только один из них будет с инверсией:
Module ModuleTester(q); output wire q; wire a,b,c,d; assign a = b; assign b = c; assign c = d; assign d = ~a; assign q = a; endmodule
Но получается сокращение (1 элемент, вместо четырех). Что логично. Но мы то задумывали линию задержки.
Но если поставить синтезатору условие, что линии a,b,c,d не сокращать, то получится то, что мы задумали. Для подсказки синтезатору применяются директивы . Один из способов указания - это текст в комментарии:
Module ModuleTester(q);
output wire q;
wire a,b,c,d /* synthesis keep */;
// ^^^--- это директива для синтезатора
assign a = b;
assign b = c;
assign c = d;
assign d = ~a;
assign q = a;
endmodule
А вот и результат - цепочка из четырех элементов:
И это далеко не все! Оставлю на радость самостоятельного изучения: работу с case и директиву для реализации его в качестве RAM/ROM или логической схемой; работу со встроенными блоками памяти (RAM/ROM); выбор реализации умножения - аппаратным умножителем или логической схемой.
По мере развития цифровых микросхем возникло противоречие между возможной степенью интеграции и номенклатурой выпускаемых микросхем. Экономически оправдано было выпускать микросхемы средней интеграции, таких как , . Более сложные схемы приходилось создавать из этих узлов. Разместить более сложную схему на полупроводниковом кристалле не было проблем, но это было оправдано либо очень большой серийностью аппаратуры, либо ценой аппаратуры (военная, авиационная или космическая). Заказные микросхемы не могли удовлетворить возникшую потребность в миниатюризации аппаратуры. Решение могло быть только одним — предоставить разработчикам аппаратуры возможность изменять внутреннюю структуру микросхемы (программировать).
История развития программируемых логических интегральных схем (ПЛИС) начинается с появления программируемых постоянных запоминающих устройств. Первое время программируемые ПЗУ использовались исключительно для хранения данных, однако вскоре их стали применять для реализации цифровых комбинационных устройств с произвольной таблицей истинности. В качестве недостатка подобного решения следует отметить экспоненциальный рост сложности устройства в зависимости от количества входов. Добавление одного дополнительного входа цифрового устройства приводит к удвоению требуемого количества ячеек памяти ПЗУ. Это не позволяет реализовать многовходовые .
Для реализации цифровых комбинационных устройств с большим числом входов были разработаны программируемые логические матрицы (ПЛМ). В иностранной литературе они получили название — Programmable Logic Arrays (PLA). Именно программируемые логические матрицы можно считать первыми программируемыми логическими интегральными схемами (Programmable Logic Devices — PLDs). ПЛМ получили широкое распространение в качестве первых универсальных микросхем большой интеграции.
В настоящее время программируемые логические интегральные схемы развиваются по нескольким направлениям, поэтому возникла необходимость как то различать эти микросхемы. Классификация программируемых логических интегральных схем (ПЛИС) приведена на рисунке 1.
Поле, засеянное Массивами Программируемых Калиток
Начинаем курс лекций о том, что такое программируемая логика и как начать ее использовать на благо себя и ближнего своего…
ПЛИС
- так ЭТО называется по-русски. П
рограммируемая Л
огическая И
нтегральная С
хема.
По-буржуйски это звучит несколько иначе - FPGA
- F
ield of P
rogrammable G
ate A
rrays.
Дословно это переводится как "Поле, засеянное массивами программируемых калиток."
Если быть чуть серьезнее, можно назвать это "Матрица программируемых вентилей"
Что такое вентиль?
Совершенно верно, это в подвале такая хреновина на водопроводной трубе, которую если завинтишь,
то весь дом останется без воды. Короче говоря, это то, что может закрываться и открываться.
Так вот, пусть для вас это будет откровением, но каждая микросхема состоит из вентилей. Правда, они не совсем похожи на то, что у вас и у меня в подвале. Но делают они то же самое - открываются и закрываются.
Вентиль - это элементарная составляющая любой цифровой микросхемы.
Даже самый навороченный супермегапроцессор состоит из вентилей. Их в нем много, очень много, просто целое море. Естественно, что в каждой микросхеме вентили соединены определенным образом. Собственно, от того, как они соединены, и зависит функциональность микросхемы.
В обычных микросхемах схема соединения вентилей задается при изготовлении,
и в последствии ее изменить уже нельзя.
ПЛИС позволяет нам задавать эту схему самим, и изменять ее как нам заблагорассудится уже при
"жизни" микросхемы. Достаточно лишь нарисовать схему на компютере и при помощи программатора прошить ее в микросхему ПЛИС.
Это в-общем.
На самом деле, все гораздо сложнее:) Смотрим на рисунок
Это есть одна макроячейка - Macrocell по-ихнему.
Каждая микросхема ПЛИС состоит из таких вот макроце… макроячеек.
Как видно из схемы макроячейки, она состоит из блока Look-Up Table (LUT
) - "Просмотровой таблицы",
а также, триггера
с синхронными и асинхронными входами и некоторой логики по входам триггера.
Программируется, собственно, только LUT. Как видите, у нее 4 входа и один выход.
Этот выход может быть подан как непосредственно на выход макроячейки (Q0), так на синхронный вход данных триггера (D).
LUT - это ни что иное как ПЗУ на 16 1-битных ячеек. При подаче на входы LUT (D0…D3) некой комбинации цифровых сигналов, она воспринимает их как адрес и выдает на выход содержимое ячейки по этому адресу.
Вообще, любая схема из логических элементов, у которой 4 входа (или меньше) и один выход, может быть описана таблицей истинности в 16 строк. Например, возьмем вот такую несложную схему, и напишем для нее таблицу истинности:
Таблица истинности описывает логический уровень выхода схемы (Q) для всех возможных комбинаций сигналов на входах (D0…D3). Точно так же в LUT каждой комбинации входных сигналов (читай - каждому адресу ПЗУ) сопоставлен свой выходной сигнал. То есть, в LUT прошивается таблица истинности той схемы, которую мы хотим видеть на его месте. Вот так все просто!
Если схема содержит более 4 входов или более 1 выхода - используются LUT нескольких макроячеек.
clear="all">
Но самое главное - это триггер. Ведь, как вы знаете (а может, еще не знаете), все регистры, счетчики и многие другие элементы цифровой техники состоят именно из триггеров. Так вот, количество макроячеек в микросхеме ПЛИС определяет количество триггеров. И наоборот:) Так что, выбирая микросхему для какого-либо проекта, надо предварительньно прикинуть, сколько триггеров будет в схеме, и брать с запасом…
На рисунке чуть ниже изображена структурная схема микросхем серии EPM7000 фирмы Altera Эта картинка дернута из даташита https://www.altera.com/literature/ds/m7000.pdf Она относится к микросхемам EPM7032, EPM7064, EPM7096. Дык вот, последние две цифры в названии обозначают именно количество макроячеек в микросхеме.
Как видно из схемы, макроячейки объединяются в "блоки логических массивов" (LAB - Logic Array Block).
Эти блоки соединяются между собой через "программируемый массив внутренних соединений" -
programmable interconnect array (PIA).
Кроме того, LAB"ы подключаются к выводам микросхемы через блоки управления ввода/вывода (I/O Control Block).
При прошивке, в каждый из блоков "зашивается" своя информация:
- В Макроячейках программируются LUT"ы,
- в PIA зашивается информация о внутренних межблочных соединениях,
- в блоки управления ввода/вывода (I/O Control Block) зашивается информация о подключениях к ногам микросхемы.
По-серьезному, "прошивка" ПЛИС называется "загрузка конфигурации".
А как вы думаете, чем мы будем загружать конфигурацию???
Ну конечно же, старым добрым Байт Бластером! :) Байт бластер - это штука универсальная:)
О том, как его сделать, подробнейшим образом я рассказывал вот