Как стать автором
Обновить

Комментарии 45

Остался самый нудный этап, хотя и полностью графический — назначить сигналам конкретные пины.

Данный этап можно также сделать в удобном для Вас текстовом редакторе, если правильно описать .qsf файл. Да и в целом можно вообще не запускать графическое представление Quartus. Например, как здесь github.com/Dmitriy0111/wrapper_for_8bitworkshop через makefile.
1 Нет такой платы за 200, начинаются с 300 без индикаторов и кнопок. (MAX ii EPM240)
2 чем Вас таймер от stm32f103 не устраивал, за 100Р?
1. Ну вот так мне повезло… К тому же сами чипы очень дешевы — рублей по 80 стоят, а то вдруг придется что-то на них делать…
2. Так тем и не устроила, что не FPGA! На ней же не освоишь программирование на VHDL…
К тому же сами чипы очень дешевы

У них всего 100 циклов программирования, если я не путаю.

На ней же не освоишь программирование на VHDL

Ну можно поспорить :)
Ну да, 100 — но это же целых 100! У меня пока всего один истрачен…
У них всего 100 циклов программирования, если я не путаю.

Путаете. Вернее не путаете, но путаете. То есть что-то путает производитель. Мдя… В общем, всё запутано так, что лучше почитать вот здесь. Если кратно — 10'000 циклов она точно выдержит)
так я и не понял — все же в доке пропустили три нолика? или что? и на сколько циклов перезаписи ориентироваться, кто-нибудь подскажет?
Это где ж такие платки по 200р можно прикупить? Мне кажется, что вам кажется, что они столько стоят)

Модуль генерации корректно описан? В процессе tact по переднему фронту при старте тикнет счётчик cntX и вроде бы больше тикать никогда не будет.
Автор похоже закупался тогда, когда доллар был по 6 рублей.
Сейчас аналогичная плата «EPM240 Training Board Altera MAX II CPLD» стоит в районе 1100 руб.
За эти деньги можно прикупить «Xilinx XC6SLX16 Spartan 6 FPGA development board 32Mb SDRAM» ~$18- $19
Вполне серьезная плата
Вот, кстати, если думать о будущем, хотелось бы перейти на что-то серьезное — но так, чтобы это стоило потом применять в проектах…
Однако выбор довольно большой — что же делать?
Тут надо понимать, что ПЛИС — это просто матрица соединений и всё. Да, во «взрослых» ПЛИСах есть всякие штуки типа PLL, или DSS. Но там нет ЦАП и АЦП, энергонезависимой памяти, готовых интерфейсов типа UART/SPI/I2C (на самом деле через IP Core всё добывается, но ядра денег стоят) и прочего. Если вы захотите реагировать на уровень чего-то, то вам придётся ставить микросхему АЦП и реализовать интерфейс с ней. Если понадобиться формировать уровень — то же самое с ЦАП. Хранить калибровочные параметры или какие-то данные? Ставьте свою ПРОМ. Получается куча мороки вместо того, чтобы сразу использовать какой-нибудь микроконтроллер. ПЛИС хорош для быстрой обработки сигналов, но, как практика показывает, их сфера применения «в быту» очень узка. Иногда, бывает нужна надежность. Чтобы контроллер не завис, не сбойнул и не сформировал какую-нибудь бяку из-за «поехавшей» в памяти программы. В этом плане ПЛИС неплох, т.к. по своей сути представляет цифровую схему из набора регистров — тут логике сложно сломаться.

Я, уже не помню для чего, как-то использовал Digilent 410-328. Это плата в форм-факторе DIP-48 на основе Xilinx Artix-7. Плата уже имеет на борту конфигурационную память, распаянный программатор и немного SDRAM. Помню, что туда без проблем влезли все интерфейсные модули (два протокола UART + SPI) и места ещё осталось достаточно. 10 тыс. лутов, конечно, гораздо лучше, чем 240 ячеек в MAX II, но и цена платы сильно побольше будет)
Мы тут как-то по работе недавно покупали Zynq-модули у Trenz, на выходе получается ARM-процессор + ПЛИС довольно большой ёмкости (это в одном кристалле), + SPI Flash + пара модулей на плате, выводы и отдельный Xilinx-совместимый программатор на CPLD от Lattice, и всё это чуть меньше $100.
С этой серией в живую я напрямую не сталкивался. Философия этой платформы вроде как раз в том, чтобы в ПЛИСе реализовать то, что в МК не сделать (какую-нибудь специфичный высокоскоростной интерфейс, например, или БПФ), а всё остальное — отдать обычным программистам, благо на ARM ядро хоть Debian ставь. По логике это должно увеличивать сроки разработки.
Ну там никто не мешает и как просто ПЛИС без процессора использовать на самом деле.

Но вообще — позволяет выполнить и интегрировать realtime-периферию гораздо ближе к ядру процессора, чем при отдельных кристаллах, не ограничиваясь при этом softcore-процессором внутри самой ПЛИС.
Забавно, что именно в max II как раз таки есть энергонезависимая память, в виде юзерского кусочка флеш.
А еще для неё не нужно внешней конфигурационной памяти)
А вот, кстати, как осуществляется защита IP в fpga с внешней конфигурационной памятью? В MAX II это просто…
Во флешке лежит bitstream же, который получен с помощью САПР. Можно взять эту прошивку и залить в другой такой же ПЛИС — работать будет. Вытащить прошивку можно, но это надо чтобы вот прям очень нужно было реверсить железо для неё. Да и сам разработчик заранее может озаботиться шифрованием обмена памяти с ПЛИСом. Ну а как ведётся сам по себе учёт IP — это уже вопросы ценовой политики. IP стоят ооочень дорого)
Да и сам разработчик заранее может озаботиться шифрованием обмена памяти с ПЛИСом

Протокол загрузки конфигурации в ПЛИС строго регламентирован и не допускает никаких изменений со стороны разработчика. Так что да — посадив сниффер на линии загрузки ПЛИС можно легко и безболезненно получить «прошивку» ПЛИС.
Есть ПЛИС, поддерживающие шифрование конфигурации, но они более дорогие и для них нужны более дорогие загрузочные флэшки, которые так же поддерживают шифрование.
Да, именно так. Мало того, частичку ПЛИСа ещё нужно и постоянным питанием обеспечивать чтобы не утратился ключ в энергозависимой памяти. Ставятся батарейка или аккумулятор. Но тут нюанс в том, что нужно заранее продумать схемотехнику замены этого аккумулятора — ставят большие кондёры или ионистор, чтобы при техобслуживании через N-лет можно было быстренько заменить источник питания. Конечно, так заморачиваются только когда реально интеллектуальная собственность в прошивке очень дорогая.
частичку ПЛИСа ещё нужно и постоянным питанием обеспечивать чтобы не утратился ключ в энергозависимой памяти

Этого я не знал :) Думал, что ключ прошивается в какую-нибудь OTP-область :)
Не обязательно, бывает однократно программируемый энергонезависимый ключ.
Я сам лично этот вопрос не изучал — коллеги по работе в каком-то проекте заморачивались, знаю как у них реализовывалось.

Однократно программируемый ключ можно же вытащить? Область наверняка заранее определена: электронный микроскоп + знание топологии ПЛИСа…
Вероятно можно, у нас тут специалист даже был BarsMonster но это переводит задачу на совсем другой уровень…
Иногда, бывает нужна надежность. Чтобы контроллер не завис, не сбойнул и не сформировал какую-нибудь бяку из-за «поехавшей» в памяти программы. В этом плане ПЛИС неплох, т.к. по своей сути представляет цифровую схему из набора регистров

А эта цифровая схема из набора регистров в случае fpga чем конфигурируется, как вы думаете?

Конфигурируется-то она из ПРОМки, но если будет косяк в данных, то процедура инициализации не завершится (CRC не сойдётся) и ПЛИС не загрузится.
Ненене, я не про то, из чего она конфигурируется, а про то, где лежат параметры конфигурации в загруженной fpga.
Ааа, вы про конфигурирование макроячеек и матрицы соединений? В принципе там такие же транзисторы как и везде)) Технологическая разница в изготовлении может как-то сказываться? Ну… это вопрос интересный. Надо бы на досуге поизучать.
Да, я про макроячейки, но не про технологическую разницу, а про SRAM-based FPGA (то есть примерно все коммерческие СБИС, исключая CPLD-мелочь и космические антифузы). Там самые натуральные ячейки памяти, которые собственно и конфигурируют макроячейки. И если в ячейку памяти что-то этакое попадет… Уж явно в этой ситуации у ПЛИС нет никакого преимущества перед такой же ячейкой памяти в микроконтроллере.
> хотя и пишу все на С, но люблю все же Ada

Смахнул слезу — есть же еще люди!

Я извиняюсь, не пишу на vhdl, но зная архитектуру ПЛИС предположу, что у вас должны наблюдаться гличи на сигнале генератора, так как выходной сингал не простробирован регистром…
Единственный раз, когда пришлось работать с vhdl, намучился с переменными (когда частота подросла) и все перевел на сигналы, сигналы это физически регистр.
А в целом отлично, что есть доступные для изучения комплекты, спасибо за статью

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

Во-вторых, считывать кнопки по фронту не совсем хорошая идея из-за дребезга, особенно в случае дешёвых китайских кнопок. Обычно просто сэмплят с малой частотой, 50-100 Гц.
Я тоже хотел сначала устранить дребезг и добавить автоизменение (ну для этого надо просто процесс с частотой 2-5 герц) — но тогда не входит по размеру, а как оптимизировать не придумал, к сожалению.
А по-моему, очень даже неплохое и удобочитаемое форматирование. Сам бы я делал чуть по-другому, но вариант автора мне нравится и более чем имеет право на жизнь, ИМХО.
Спасибо за заметку! Смахнул скупую слезу, вспомнив, как в универе писал на VHDL железки для отладочной платы Spartan-3E.

А посоветуйте, пожалуйста, какую отладочную плату FPGA / CPLD взять за не очень дорого, скажем, до $70, чтобы восстановить навык, ну и делать простые штуки, подобные описанной в статье? Лучше, наверное, FPGA? Есть идея подключить быстрый АЦП и попытаться сделать простой осциллограф.
Можно на e-bay взять плату с Virtex4. XC4VLX100 вам не только для осциллографа хватит, поверьте) Но готовьтесь к тому, что нужно будет купить не особо дешёвый JTAG программатор. Либо же взять какую-нибудь отладочную плату на Spartan 6, выше название где-то приводили. Я в своё время маялся вот такой ерундой на марсоходе)
Добро пожаловать в мир HDL! Несколько придирок, на которые хотелось бы обратить внимание. :)

1. Строго говоря, код на HDL не является «программой». На это же напрямую намекает само название hardware description language — «язык описания аппаратуры». HDL скорее ближе к схемотехнике, чем к программированию. Хотя конечно ПЛИС дают некоторое впечатление программирования, т.к. результат можно увидеть буквально через пару минут, а не через три месяца как в случае со СБИС.
2. Первый процесс даст вам деление на 6, а не на 5 (отсчёты с 0 до 5, 0-1-2-3-4-5).
3. Никогда раньше не сталкивался с
shared variable
, в любом случае даже просто переменные использовать в VHDL лучше по минимуму и для промежуточных значений в процессах; основу, а тем более 100% выходов синхронных элементов, должны составлять сигналы
signal
. Если возникают сложности с использованием значений сигналов внутри процессов — следует подробнее ознакомиться к параллельными и последовательными блоками в VHDL (в Verilog логика похожа) и присвоением значений сигналам и переменным.
4. С т.з. пользователя управление с требованием зажатия нескольких кнопок выглядит не очень приятным. Я бы предложил, например, отвести одну кнопку под выбор режима (редактирование одного или другого параметра), две под инкремент/декремент и одну под включение/выключение как есть.
5. Если уж мы выполняем проверку «длина импульса должна быть меньше периода» при изменении импульса, то стоило бы проверять и обратный случай при изменении периода.
6. В целом получается, что duty/period и dig/di — это живущие своей независимой жизнью одинаковые величины, отличающиеся лишь в представлении. В общем случае более логично было бы выполнять двоично-десятичное преобразование от исходных значений. Но в данном примере подозреваю, что стоял вопрос жёсткого ограничения по площади ПЛИС.
7.
if cnt > period then

elsif cnt = duty then

Здесь опять «ошибка +1» — счётчик работает на диапазоне от 0 до периода, что приводит к заданию числом period реального значения в period+1 тактов.
8.
 variable cntX : integer range 0 to 1000 := 0;
 variable cnt : integer range 0 to 1000 := 0;

Тут опять максимальное значение 1000, что даст нам 1001 отсчётов, а еще — возможно данное описание корректно работает с обработкой переполнения счётчика (переход из 1000 в 0), но описано несколько страшновато. В самом первом процессе счётчик более корректен — там данная проверка описания переполнения выполняется явно:
	t := t + 1;
  	if t = 5 then
	  t := 0;
	  tact <= not tact;
	end if;

… и тут я понял, что пункт №2 как раз правильный, а у меня нет, всё дело в переменных, где сначала выполняется t := t + 1, получаем 5, а потом сразу меняем на 0. Но такое использование переменных может привести к избыточной логике. При использовании сигналов, новое значение устанавливается в сигнале только по выходу из процесса, т.о. если процесс сихронный, то результат t <= t + 1 мы «увидим» в этом процессе только на следующем такте (так же это работает и в реальности для триггеров). Совсем хорошо было бы так:

signal t: integer;
process(clk)
begin
  if rising_edge(clk) then
    if t = 4 then
      t <= 0;
    else
      t <= t + 1;
    end if;
  end if;
end process;

Возвращаясь к счётчикам — для cntX переход через 0 не проверяется, а для cnt, как уже выше было сказано, посчитается как +1.
Спасибо за конструктивную критику! Буду думать… Правда, не совсем понимаю, почему нельзя использовать shared variable — они же есть в языке, их использование оговорено, вроде я по уставу их использовал. Ну а с кнопками — как уж вышло, я уже выше отвечал, что хотелось бы большего, но LUTы не позволяют…
Variable, используемые как де-факто регистры, — это один из верных способов выстрелить себе в ногу в VHDL. Их поведение сложнее контролировать в асинхронных (описывающих логику, а не триггер) процессах, в целом есть довольно высокий шанс получить на выходе т.н. защёлки (latch), которых не должно быть в проекте, если только не ставишь их туда вручную с конкретной целью.

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

Это не значит, что переменные вообще использовать не надо. В процедурах и функциях — там можно использовать их «как обычно». Но надо чётко понимать к чему это приводит на схемотехническом уровне в реальной схеме.

Например надо подсчитывать чётность передаваемого байта в потоке данных. Мы ограничены во времени — на всё отводится один такт (потоковая передача). Можно сделать это так:

process(CLK)
begin
if (CLK'event) and (CLK = '1') then

  Do <= Di;
  PAR <= Di(7) xor Di(6) xor Di(5) xor Di(4) xor Di(3) xor Di(2) xor Di(1) xor Di(0);

end process;


А можно сделать так:

process(CLK)
  variable parity : std_logic := '0';
begin
if (CLK'event) and (CLK = '1') then

  Do <= Di;

  parity := '0';
  for i in 0 to 7 loop parity := parity xor Di(i); end loop;
  PAR <= parity;

end process;


Схемотехнически результат этих двух примеров кода будет один и тот же — логика XOR соответствующих бит между выходами регистра Di и входом регистра PAR. Верхняя запись смотрится лаконичнее и красивее, чем нижняя, но это только для байта. А если на входе будет 64-разрядное слово? Уже целесообразнее нижняя форма записи чисто из-за экономии времени и нежелания делать тупую работу, расписывая все 64 бита)

В целях обучения я бы на вашем месте не использовал переменные в синтезируемом коде. Кроме того, я бы сделал полностью синхронную схему, работающую по единому тактовому сигналу без асинхронных элементов и межтактовых переходов. К сожалению, сложно объяснить всю опасность «ездящей» времянки из-за асинхронных вещей — это хорошо доходит до людей только после этак недельки интенсивной отладки большого проекта, когда то работает, то нет) Опять же надо понимать, что и асинхронные схемы вполне можно использовать, но разработчики со стажем прибегают к этому обычно по веским причинам и когда деваться некуда.
Те, кто приходят из Си мыслят в цикле последовательном исполнении команд и переключиться сразу на параллельное мышление нет так просто как кажется на первый взгляд.

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