VHDL с нуля.

VHDL с нуля. Выпуск первый — VHDL в картинках.

  Статья предназначена для желающих освоить VHDL с нуля, но более опытное пользователи могут читать и вносить свои предложения. О языке буду рассказывать безотносительно какого-либо железа, так как особенности синтеза и реализации в железе написанного кода это отдельная тема, здесь же я хотел бы передать концепцию(идею)  проектирования(да именно проектирования, а не программирования) цифровых устройств с использованием языка VHDL. В качестве среды для моделирования буду использовать ModelSim. Так как существует достаточное количество статей по VHDL, например тут, некоторые места не буду подробно рассматривать, а буду ссылаться. По поводу точности определений, для маньяков таких как я, любящих точные определения есть стандарт(тут), но читая его можно сломать мозг и как это не парадоксально, чтобы его читать уже нужно иметь представление о языке, как мне кажется. В данной статье я попытаюсь менее четко формулировать понятия, для того что бы сделать их более понятными, принимаются все предложения по их коррекции.

Итак приступим.

  VHDL(Very high speed integrated circuits Hardware Description Language)- данный язык предназначен для описания проектируемых систем на схемотехническом уровне проектирования и замены классического подхода к схемотехническому проектированию на уровне отдельных элементов. Язык позволяет описывать цифровые ситемы на алгоритмическом уровне. При помощи специального программного обеспечения описание на языке VHDL преобразовывается в схему на уровне простейших элементов цифровой электроники. Описание на языке применятся VHDL как при проектировании
заказных СБИС так и при проектировании цифровых систем на базе специальных устройств ПЛИС(Программируемые логические интегральные схемы).

Основные понятия.

Теперь перейдем к основным понятиям языка(это моя трактовка):

  Интерфейс — описывает наше с вами устройство, как черный ящик с входами и выходами, т.е. главная его задача показать какие входы и выходы есть у нашего устройства для связи с внешним миром.
  Архитектура — описывает поведение нашего устройства или раскрывает его внутреннюю структуру, т.е. в архитектуре описывается алгоритм функционирования нашего устройства.
Стоит обратить внимание, что архитектура может быть описана в общем случае двумя вариантами:
  • поведенческим стилем(описывается алгоритм работы устройства)
  • структурным стилем(описывается структура устройства)
  Поведенческий стиль удобно использовать при описании элементов нанизком уровне иерархии, а структурным на верхних уровнях иерархии, т.е.написать много маленьких устройств поведенческих стилем, а потом описать состоящие из них устройство структурным стилем, в виде связей между ними.
  Операторы языка — операторы в языке бывают последовательные, а бывают параллельные. Параллельные операторы вводятся для того, чтобы отобразить параллельность протекающих в железе процессов. Но любой параллельный оператор можно заменить специальным параллельным оператором процесса с последовательными операторами внутри его, он как раз для этого и предназначен.При помощи параллельных операторов мы описываем как бы элементы схемы которые могут работать одновременно, при моделировании каждому параллельному оператору ставится в соответствие свой процесс. Далее я буду считать каждый параллельный оператор процессом.
  Сигналы — связывают между собой процессы. Они являются внешними по отношению к процессу, т.е. процесс может считывать сигнал и выдавать значение в сигнал. По-этому сигналы могут объявляться только в области деклараций архитектуры. Так же сигналы могут хранить значения, которые необходимо передавать от процесса к процессу.
  Переменные — переменные используются внутри параллельного оператора процесса и требуются для
описании алгоритма работы процесса. Их надо использовать в тех случаях, когда не требуется переносить информацию от процесса к процессу.
  Атрибуты — это значения(характеристики), связанные с какими-либо объектами языка. Например типами, сигналами, переменными и т.д.
  Типы — множества значений с какими то общими характеристиками. Характеризуются типы набором значений, которые могут принимать объекты (сигнал, переменные) данного типа, а так же набором операций которое могут выполнятся с объектами данного типа.
  Библиотеки и пакеты — пакеты представляют собой структуры в которых хранятся описания различных функций, процедур, компонентов, типов, констант и т.д, все пакеты собираются в библиотеки, которые в последствии подключаются к проектам.

Структура проекта.

  Как же выглядит структура проекта. заглянем в замечательную книгу П.Н.Бибило Основы языка VHDL.

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

Типы.

  Всего имеется четыре класса типов: скалярный, составной, файловый и ссылочный(в стандарте за 200 год нашел еще пятый — защищенный тип, может потом рассмотрю). Вот они



  Зелененьким светофором обозначил типы которые синтезируются, т.е. их смело можно использовать при написании программ для программирования ПЛИС. Пример объявления типов посмотри на основе предопределенных типов в пакете STANDART.

Достану несколько оттуда:

type boolean is (false,true); -- перечислимый тип boolean, как видно принимает 2 значения
type bit is ('0', '1'); -- перечислимый тип, представляет собой, как бы эквивалент одного разряда
type integer is range -2147483648 to 2147483647; -- целый тип integer, как видно представлен диапазоном значений
type real is range -1.0E308 to 1.0E308; -- вещественный тип
type time is range -2147483647 to 2147483647 -- физический тип, нужен для задания задержек при моделировании
units 
 fs;
 ps = 1000 fs;
 ns = 1000 ps;
 us = 1000 ns; 
 ms = 1000 us; 
 sec = 1000 ms; 
 min = 60 sec; 
 hr = 60 min; 
end units;
type bit_vector is array (natural range <>) of bit; - массив типа bit, запись natural range <> обозначает, что размер задается при объявлении в диапазоне натуральных значений.

  С остальными типами советую ознакомится из пакета. При синтезе используется пакет std_logic_1164. Он хорошо описан в статье, на которую я дал ссылку в начале. Единственное, не рассмотрено использование безразличного состояния, его применение я рассмотрю позже.
Еще один нюанс, необходимо понимать, что все типы при синтезе преобразовываются в типы std_logic(расширенный эквивалент bit) и std_logic_vector(расширенный эквивалент bit_vector).Т.е. все значения перечислимых типов будут закодированы двоичными комбинациями и целый тип так же будет закодирован двоичными комбинациями.

Способы задания значений.

  В VHDL для задания числовых значений используются литералы. Вот они


Декларации.

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


Операторы используемые в выражениях.


  Я думаю здесь все понятно, объясню только оператор конкатенации(&) — он нужен для объединения нескольких битовых строк в одну.

Операторы языка.

Последовательные операторы.

1. Оператор присваивания значений переменной(:=) — A := B and C;

2. Оператор присваивания значений сигналу (<=) — A <= A and C;

  При присваивании используются следующие задержки

  • inertial – инерционная задержка
  • Передача сигнала будет иметь место, если входной сигнал будет сохранять соответствующий уровень в течении указанного отрезка времени — имитирует время срабатывание устройства
    X <= inertial Y after 3 ns;
  • transport – транспортная задержка
  • Все изменения сигнала будет присваиваться с задержкой — имитирует задержку на линиях
    X <= transport Y after 3 ns;
  • По умолчанию используется инерционная задержка
  • X <= Y after 3 ns;

3. Оператор if

if  условие then
последовательные операторы
{elsif условие then
последовательные операторы}[else
последовательные операторы]
end if;

--Пример №1

      if(A < B) then -- сравниваем A и B
        y <= X0; -- если условие = true
      else
        Y <= X1; -- если условие = false
      end if;

--Пример №2

 if(A < B) then
   Y <= X0;
 elsif(A < C) then --  заменяет связку else if(A < C) then
   Y <= X1;
 else
   Y <= X2;
  end if;


4. Оператор case — удобно использовать при реализации устройств описанных таблицей истинности

case выражение is
when выбор => последовательные операторы
{when  выбор => последовательные операторы}
end case;

  Выбирает одну из альтернатив, выбранная альтернатива определяется значением выражения.
Выражение и выбор должны быть одно типа(дискретного или одномерный массив)
Все возможные выборы должны быть описаны, либо использовать служебное слово others( все другие случаи).
-- Пример - описание элемента ИЛИ заданного таблицей истинности

case X is -- в зависимости от X  выдаем в Y различные значения
when "00" => Y <= '0'; -- когда X = "00" и т.д.
  when "01" => Y <= '1';
    when "10" => Y <= '1';
      when "11" => Y <= '1';
        when others => Y <= '0'; -- если используем std_logic_1164, за счет расширенного алфавита комбинаций будет много больше 4.
          end case;


5. Оператор loop

[метка цикла:] [while условие|for идентификатор in диапазон дискретного типа]
loop
последовательные операторы
end loop; 

  • В случае использования ключевого слова while, сначала вычисляется условие, если результат true, то выполняются последовательные операторы
  • В случае использования ключевого слова for, идентификатор определяет счетчик числа итераций цикла

--Пример №1 - считаем сумму всех цифр от 0 до 9

s := 0;
M1: for i in 0 to 9  -- оператор повторения с for синтезируется
  loop
  s := s + i;
end loop;

--Пример 2 - так же считаем сумму всех цифр от 0 до 9

i := 0;
M2: while(i < 10) -- можно синтезировать при определенных условиях
  loop
  s := s + i;
  i := i + 1;
end loop;


6. Оператор next

next [метка цикла][when условие];

Применяется для перехода к следующей итерации цикла.

--Пример

i := 0;
M2: while(i < 10)
  loop
  s := s + i;
  next M2 when s = 3; -- пропустим следующую строку в случае S = 3, т.е. прибавим 2 к S два раза 
  i := i + 1;
end loop;

7. Оператор exit

exit [метка цикла][when условие];

Применяется для досрочного завершения оператора цикла. Пример такой же как и в предыдущем случае, только в результате будет досрочное завершение цикла

8. Оператор null — можно использовать при синтезе, но не рекомендую, может сильно усложнить схему
Не представляет действий.
case X is 
when "00" => Y <= '0';
  when "01" => Y <= '1';
    when "10" => Y <= '1';
      when "11" => Y <= '1';
        when others => Y <= null; -- во всех остальных случаях ничего не делать
-- предыдущаю строку лучше заменить на
--when others => Y <= '-'; - при всех остальных случаях нам безразлично, по этому пускай синтезатор оптимизирует схему как хочет 
          end case;


9. Оператор assert — можно применять при отладке программ( при трассировке), не синтезируется

assert условие [report выражение][severity выражение];

  Проверяет, является ли условие истинным, и сообщают об ошибке, если условие является ложным
  • severity(степень серьезности) – NOTE, WARNING, ERROR, FAILURE

-- Пример

assert (Y = '1' and X = '1') report  “Incorrect signals“ severity WARNING; 

безусловно выдаваемое сообщение
report “Happy new year“  severity NOTE;
assert false report “Happy new year“;


10. Оператор wait — используется только при моделировании, при синтезе игнорируется(Синтезировать можно только частные случаи)

wait on список чувствительности until условие for тайм-аут


  Приостанавливает процесс до момента, пока не изменится некоторый сигнал в списке чувствительности процесса, в это время будет произведено вычисление условия.
  • Если условие true, выполнение процесса возобновляется
  • тайм – аут устанавливает максимальное время, после которого процесс возобновит свое выполнение
wait on A,B until (C = 0) for 50 ns;

допустимо записывать одно или более условий в операторе ожидания
wait on A,B;
wait until (C = 0);
wait for 50 ns;

может быть больше одного оператора wait внутри оператора процесса

Статья не закончена, буду добавлять сюда примеры и рассказывать дальше о языке. Эту версию выставляю, что бы понять стоит ли дальше продолжать или нет. Жду отзывов и прошу тех кто разбирается помочь сделать картинки читаемыми.

  • +5
  • 07 апреля 2011, 14:07
  • opolo84

Комментарии (32)

RSS свернуть / развернуть
Картинки прикольные. В чём рисовал?
А вообще по поводу полезности хороший вопрос — тоже думал такого плана статью про Verilog написать, но что-то решил, что кому надо найдёт информацию, её вроде не мало.
0
Рисовал во FreeMind + Дописывал в Paint. Информации действительно много, но вся разбросано довольно сильно, чтобы полную картину представить приходиться закопаться в разных источниках.
0
возьму на заметку. шикарные схемы
0
Вообще карты ума интересная штука, помогает все разложить по полочкам.
0
2. Оператор присваивания значений сигналу
(<=) — это параллельный оператор.
0
Если он находится в операторе процесса то последовательный, если вне его то он сам является параллельным оператором присваивания значения сигналу.
0
Все присваивания значений сигналов в процессе происходят после выхода из процесса т.е. если написать a:='1'; b:=a; то b будет равно а, а если a<='1'; b:=a; тогда b будет равно значению а до присваивания.
0
Если честно не очень понял вами приведенный пример, зачем мешать в кашу переменные и сигналы, все операции в процессе надо проводить с переменными, а в его конце выдавать переменную в сигнал. Ну и логично входные сигналы считывать в переменные в начале и менять выходной сигнал в конце.
0
Согласен, запутано получилось
entity dsf is
port ( clk: in bit;
b: out bit;
a: out bit );
end dsf;
architecture s of dsf is
signal s: bit;
begin
process (clk)
variable p: bit := '0';
begin
if (clk'event and clk='1') then
p := '1';
a <= p;
s <= p;
b <= s;
end if;
end process;
end architecture;

На выходе а 1 появиться после первого фронта, а на b — после второго.

Вообще я хотел сказать что ":=" присвоение выполняется сразу, а "<=" после выхода из процесса и одновременно для всех сигналов.
+1
Да это абсолютно верно, но это я поподробней расскажу, когда буду рассматривать оператор процесса.
0
Может как-то подробней раскрыть тему задержек? А то не совсем понятно для чего они там описаны и что собой представляют и для чего используются (только для моделирования или учитываются при синтезе).
0
Задержки нужны только для моделирования, при синтезе они игнорируются.
0
Я имел ввиду, что надо это в статье отразить. А то они получились как-то отдельно от контекста и непонятны из-за этого…
0
Я решил время на продолжение не тратить, кому интересна тема может почитать вот это pep687253.narod.ru/translate/VHDL_reference.pdf, по-мойму здесь очень хорошо все описано, так что надо плату и делать готовые устройства в пику проетам на микроконтроллерах.
0
Хорошая, годная статья! Весьма полезна для начинающих! Здесь, разве что, не написано про настроечные константы aka Generic Map. Тоже полезная штука. Дальше продолжать, безусловно, стоит.
Ну и выскажу свои пожелания и предложения. Хотелось бы в будущих статьях видеть примеры описания элементов цифровых схем: логика, мультиплексоры, дешифраторы, триггеры, счетчики и прочее. Если нужно, могу оказать посильную помощь в подготовке этих примеров.
0
Про настроечные константы я писал в другой статье, по-поводу продолжения не знаю, думаю основы языка можно и самим прочитать все таки, а вот можно всякие универсальные мультиплексоры и дешифраторы и т.п. с использованием настроечных констант наверное было бы самое то, так как их практически везде приходится использовать. Хотя когда картинки рисовал, мне понравилась идея, читать много текста тяжело, а картинку посмотреть проще, попробую где нибудь такие вещи на практике применить :).
0
Маленькая опечатка «в стандарте за 200 год»
0
А я думаю, новички в очередной раз подумают на verilog как на +1 язык программирования. Чем он не является.
Сдается мне что параллельно с введением в язык обязательно объяснять суть синхронной логики и RTL.

Будет понимание RTL: будет автоматически синтезируемый код. Не будет понимание: будет адская смесь логики и шестиколесные велосипеды.
0
Что такое RTL? Это довольно многозначная аббревиатура.
0
Register Transfer Level == суть принципа работы абсолютного большинства цифровых схем.
Который заключается в использовании регистров и комбинаторной логики, тактируемых клоком (синхронная логика).
Фактически если в схеме есть тактовая частота, это как правило синхронная логика.

Из самого verilog или VHDL этот уровень не виден как бы. Но если его не учитывать то получается ерундовый код.
0
А можно пример какого то устройства, написанного ерундовым и нормальным кодом?
0
Отличный пост на эту тему нашелся на хабре под номером 137643. А если вкратце то приведу пример.
Положим, есть функция F() = In + A + B + C + D; При этом In это выход АЦП, ABCD это какие нибудь параметрические константы, например смещения DC и пользовательские параметры, поступающие с разных блоков. То есть, эти слагаемые ортогональны и не могут быть сокращены.

Какова будет реализация в лоб этой функции?
Объявляется модуль F и в нем assign out = in + A + B + C + D;. Это синтезируется в 4 сумматора, расположенных в любом порядке. Длина вычислений будет от 3 до 4.

Теперь напишем то же самое, но с оглядкой на синхронную схему.
reg [N:0] tmp0;
reg [N:0] tmp1;
reg [N:0] tmp2;
reg [N:0] tmp3;
always @ (posedge clk)
begin
tmp0 <= in + A;
tmp1 <= tmp0 + B;
tmp2 <= tmp1 + C;
tmp3 <= tmp2 + D;
end
assign out = tmp3;

Это можно назвать поточным или конвеерным сумматором. Он также синтезируется в 4 сумматора, но разделенных временными регистрами для хранения промежуточных результатов. И эти регистры тактируются клоком clk.

Фатальный недостаток первой очевидной схемы: много комбинаторной логики. Представим что суммирование выполняется за 1 нс. Значит первая схема выдаст верный результат на выходе через 3-4 нс. Вторая схема его вычислит за 4 нс. Но. Первая схема может быть тактирована не больше чем 250 Мгц (вспомним АЦП) (1с/4нс). Вторая схема может тактироваться от 1 ГГц (1с/1нс). Потому что вторая схема за 1 такт делает 1 операцию суммирования, но имеет конвеерную задержку. Разница с скорости работы всего устройства 4 раза в пользу неочевидной схемы.

Хороша или плоха синхронная логика можно рассуждать долго и интересно. Но строительным кирпичиком FPGA является LUF + FF. Комбинаторная логика + регистр, тактируемый от клока. Следовательно строительство больших комбинационных схем приводит с дисбалансу: незадействованные регистры и резкое снижение скорости. Строительство же сильно регистровых схем приводит наоборот к излишнему количеству регистров и большим конвеерным задержкам. На современном этапе проектирования схем выбор баланса скорее искусство подогнать проектируемую схему под реальное железо. Которое в 999 случаях из 1000 будет FPGA.

Резюме: непонимание структуры целевого железа (RTL) порождает дикие комбинаторные схемы, которые могут даже работать, но иметь неудовлетворительные параметры.
0
Разница с скорости работы всего устройства 4 раза в пользу неочевидной схемы.
Так результат мы получаем с такой же задержкой, где выигрыш в 4 раза?
Вообще очень интересно. Было бы здорово почитать статью с результатами синтеза в виде схем реализации и временных диаграмм, на эту тему. Может напишите?
0
Тут выигрыш, в первую очередь, в пропускной способности, она возрастёт в 4 раза. Но конвееризация — искусство тонкое, конвейер из примера выше будет работать так как описал MParygin только при условии одинаковой задержки распространения на каждом из ярусов конвейера. Другими словами, чтобы построить идеальный конвейер, мы должны добиться равенства времени выполнения всех стадий конвейера. А это даже в таком пустяковом примере как F() = In + A + B + C + D очень не простая задача.
0
Главное правило в этом случае: общая скорость системы равна скорости самого медленного звена. Ну и следствие из этого: выявляем из тайминг репорта самый медленный блок и конвееризуем.
А на практике, времязадающий фактор это частота следования сигналов на входе в выч блок (АЦП например). Если ее реализовывать с небольшим (20%) запасом на каждой стадии, то все ок.
Некоторые блоки как ни крути могут быть только конвеерными.
0
Задержка в распространении сигнала одинакова в обоих случаях. Но в первом случае мы получаем неконвеерную архитектуру. Во втором конвеерную. В первом случае АЦП не сможет передать следующий отсчет пока не отработает сумматор. Во втором случае АЦП успеет отдать +3 отсчета в конвеер до тех пор, пока не выходе не появится результат первого отсчета. Вот отсюда разница в пропускной способности архитектуры. 1 к 4. Ну, в жизни встречаются и более драматичные случаи, когда в дизайн рассчитанный на 250МГц вносится сторонний код на VHDL и репорт тайминг показывает уже 20 МГц.

Дальше, процессоры, например, давно перешли на конвеерную архитектуру. Потому что пошаговая архитектура себя исчерпала где то в районе 8080. Хорошая статья на эту тему в wiki Вычислительный конвеер. Процессы идентичны схемотечническим.
0
Ну конвейером то понятно. Но тут просто надо понимать, что именно хочешь получить, а не знать что такое RTL. При этом в коде это очень хорошо видно и понятно, если хочешь просто функцию без памяти используй код без анализа фронтов сигналов, хочешь с памятью используй анализ фронтов сигналов.
0
Признаться, ничего не понял… Фронты, память… Да.
0
Я про то, что указанные вами ошибки могут делать люди, которые только начинают писать программы или вообще ничего не понимают в цифровой электронике. Если на VHDL как то легче сделать такие ошибки, в силу его более высокой абстракции, то на Verilog вы перенаправлено делали такую структуру и в коде хорошо видно, что используются регистры и результат смещается по ступеням конвейера. В VHDL хорошо введено понятие процесса и каждый параллельный оператор это отдельный процесс который выполняется параллельно. Даже в вашем примере на простой логике можно сделать сложение по другому(например складывать по два) и уменьшить количество ступеней логики и ускорить вычисления. Вот как раз такие приемы не очень прозрачны, а с последовательные схемы и комбинационная логика.
0
Видимо так и происходит. Подытожу мою мысль через аналогии:
Если я проектирую скажем Спектрум Z я размышляю в символах корпусов 155 логики.
Если я пишу код на ФП я размышляю в категориях чистых функций.
Если бы я писал для ASIC я бы пользовался примитивами процесса этого ASIC.

Целевая область налагает ограничения на категории мышления. Писать ФП код в 155 логику можно, но микросхемы придется грузить лопатой. Так же неразумно проектировать ASIC на примитивах 155.

Однако! на Verilog/VHDL пишут абстрактный (!) код, полагая в них язык программирования, которыми они не являются и никогда не были. Да, язык позволяет не заморачиваться с примитивами типа + или — и вводит модули для облегчения осознания схемы. Но это не значит что парадигму отменили. А парадигма FPGA железа это синхронная логика, по определению. Промежуточная структура между CPLD и ASIC. Пока что структура FPGA не изменилась, единственный разумный способ проектирования схем — использовать синхрологику.
Отступления от этой концепции: дилетантство. Вот и вся моя мысль.
0
Verilog или VHDL без разницы.
0
Спасибо. Такая хорошая статья. А где-нибудь есть продолжение?
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.