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

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

Имхо, VHDL гораздо проще и читаемее, чем Verilog. К слову, AHDL вообще из сравнения выпадает, т.к. не поддерживается ничем, кроме средств альтеры.
Потому я его и не сравнивал.
Что касается читаемости листинга. Основным аспектом является дело привычки.
Я же ориентировался на начинающих.
С точки зрения изучения языка с нуля SV гораздо проще.
Мне кажется вы не корректно поступаете сравнивая VHDL и SystemVerilog ( хотя в названии статьи заявлено, что речь пойдет про Verilog).
Если Verilog и VHDL еще можно сравнивать по функциональности и стилистике разработки (об этом немного ниже), то SystemVerilog вещь сильно превосходящая оба традиционных языка (хоть и полностью включающая в себя Verilog и считающаяся по сути просто новым стандартом Verilog). Но да, незадача, ни одна среда проектирования не поддерживает всех его выкрутасов.

Я недавно начал изучать SystemVerilog и даже просто пролистав стандарт уже кажется, что стиль разработки должен сильно поменяться при переходе от Verilog к SystemVerilog.

Но все же, если сравнивать языки по уровню вхождения, то тут двоякая ситуация.
Я, как и вы, обладая более «программистским» складом мышления выбрал сразу же без раздумий Verilog. Тут больше внимания, можно уделить именно алгоритмам работы, информационному взаимодействию что-ли. Но нужно знать и больше всяких замысловатых вещей. Вот тоже блокирующее и неблокирующее присвоение.

VHDL же больше понятен со стороны схемотехники. Да, он избыточен по написанию, но он ближе к железу. То что написано на неё работает так, как написано. VHDL, как говорится, не дает «выстрелить себе в ногу».
UPD
Так же в SV есть удобство в виде автоматического выравнивания разрядности данных.
Т.е. если в проекте используется тип данных integer но по логике работы понятно что значение данного регистра не превышает 255 (в десятичной системе),
то после компиляции данный регистр будет 8 разрядным.
Таким образом налицо очередная вольность допускаемая стандартом SV — он не требует явного выравнивания длин регистров.


Не понятно про какой регистр речь. Если вы про CNT_OUT — он явно объявлен 8 битным, если же вы про cnt, то мне кажется, что это ошибка.
cnt используется, как счетчик, им отмериваются интервалы относительного времени. Если синтезатор сделает cnt 8 битным ваша схема не будет работать, если он как-то пересчитает разрядность — будет работать не правильно.

Обычный Verilog вообще не позволит так написать.

в данном примере cnt будет НЕ 8 битным.
но его разрядность как регистра может быть менее 32 если синтезатор посчитает, что в проекте его максимальное значение меньшей разрядности.
А зачем в листинге на Verilog присутствует везде begin/end?
Часто можно обойтись без них — и удобней читать и код на 50% меньше.
*Обычно удобней и наглядней один always на один reg, и тогда можно избежать begin/end.
Мною приведен пример общего вида.
Каждый блок должен иметь четкое начало и конец. Будь то скобки '{', '}' для С++ или команды begin, end для SV.
Листинг должен быть однотипным, это существенно упрощает сопровождение проектов множеством разработчиков различного уровня подготовки.

И удобнее объединять под один блок always набор процессов, связанных с конкретной задачей.

Если переписать конструкцию
always@(posedge CLK) begin

     if (cnt >= 13500000) begin

         CLK_OUT 	 <= 1;
     end
     else begin
         CLK_OUT 	 <= 0;
     end
     if (cnt == 27000000) begin

             cnt 	 <= 0;
         end
         else begin
             cnt 	 <= cnt + 1;
         end

     CNT_OUT 	 <= cnt;
end

листингом, предлагаемом вами, то получим следующее:
always@(posedge CLK)  CLK_OUT	<= (cnt >= 13500000) ? 1 : 0;
always@(posedge CLK)  cnt	<= (cnt == 27000000) ? 0 : (cnt + 1);
always@(posedge CLK)  CNT_OUT	<= cnt;


Поверьте мне — начинающему разработчику такая запись покажется абракадаброй.
Не всегда более коротко записанный код, является более удобочитаемым.
Не стоит забывать, что большинство проектов для ПЛИС развивается внутри предприятий, что подразумевает сопровождение одного проекта различными разработчиками. По этой причине любое описание для ПЛИС должно иметь листинг понимаемый разработчиком любого уровня.
Вы очень начинающий разработчик, раз так пишите.Можно так:
clk_out <= 0;
if(cnt >= 135)
clk_out <= 1

cnt <= cnt + 1;
if(cnt = 270)
cnt <= 0;
К слову, SV только altera поддерживает, при переезде на xilinx будут проблемы. Поэтому есть смысл писать на pure verilog, ибо он «сишнее» и занимает меньше строк для описания одного и того же по сравнению с vhdl. Плюс еще забивать голову новичку фишками SV — себе вредить.
Хотя у всех своя религия
Где reset в вашем коде? Не все ПЛИС умеют устанавливать схемы по умолчанию.
пример не имеет установки по сбросу, это верно.
но это лишь пример, а не полноценный, кроссплатформенный проект.
вашь не будет работать, т.к. на одно событие два неблокирующих (вроде так называются) присвоения (<=) на один регистр.
нельза инкрементировать значение регистра и сразу же его сравнивать и обнулять.
если же в вашем варианте заменить присвоение вида <= на =, то проект будет работать.
однако реаоизации на блокирующих присвоениях плохо синтезируются.
в пректах от SV я использую лишь некоторые конструкции, которые компактнее и приятнее на вид.
в целом я не использую его функционал в полной мере.
Для меня после знакомства с coffeescript verilog кажется очень сильно избыточным. SystemVerilog лучше, но все-равно не то. Потому после года мучений в работе с verilog я плюнул и написал свой недо-DSL, который максимально убирает дублирование кода. Как доведу код до ума — может напишу статью и выложу код на github.
Фичи для тех кому интересно:
1. Python-like scopes (прощай begin/end)
2. Инстанциирование в ооп-стиле с автоматическим пробросом пинов с одинаковым именем и шириной шины от родителя к ребенку (прощай .clk(clk) и запятые, здравствуй CSON (CoffeeScript Object Notation, аналог JSON))
3. Если в instance есть неприсоединенные пины к родителю — проект не соберется.
4. Автоматическое генерирование компонентов по шаблону. (Надо сумматор на 32 бита, получи sum_32, надо на 16 — получи sum_16, прощай неявное генерирование и параметры в verilog, здравствуй полный контроль над генерированным кодом)
5. JTAG socket server (tcl, доделка примера от xilinx) + client (nodejs). Возможность in-chip отладки проекта в т.ч. покрытие кода регистрами. Дает возможность узнать какие ветви были активированы, а какие нет, также дает возможность просмотреть через ограниченную шину (даже 4 бита, меньше нет смысла) всю собранную информацию включая состояние входов, выходов и внутренних регистров. Бонус. Мы почти не привязываемся к стеку технологий xilinx, заменяем vio на такую же megafunction от Altera, заменяем tcl сервер для общения с JTAG, и у нас снова есть полноценная возможность отлаживать код внутри кристалла. Невостребованный бонус. При наличии двух JTAG кабелей можно отлаживать одновременно два чипа разных производителей, которые присоединены друг к другу.
6. Достаточно большая скорость работы, что позволяет отлавливать примитивные ошибки гораздо быстрее. Не нужно ждать 20 сек, пока ISE соберет все IPcore, проверит все мелочи и только потом скажет, что ты где-то пропустил один пин, или у тебя ошибка в синтаксисе.
7. Мелочные сокращалки синтаксиса для мелких популярных паттернов. (еще на 5-10% меньше букв)
8. Исправление некоторых недочетов парсера verilog за счет генерации функционально идентичного кода. Например два always блока в некоторых случаях приводят к ошибке (pin has multiple drivers). Один блок — нет.
1. begin — end в SV уродует код в целом, согласен.
2. эта проблема решается интерфейсами и более того. если я правильно помню стандарт, то при объявлении модуля внутри тела другого модуля не требуется явное указание подключения для одноименных портов. .clk(clk) в этом случае выглядит как .clk().
3. не всегда требуется присоединение всех пинов к родителю (если я правильно понимаю о чем идет речь)
2. .clk(), — 7 лишних символов и 1 лишняя строка.
3. Конечно, присоединяются только пины, которые не были соединены вручную.
если вы напишете ".clk()", то вы ничего не подключите. В sv можно подключать одноименные линии слепым присвоением ".clk", т.е. без скобок или вообще написать подключение всех линий как ".*". Пользоваться этим я бы вам не рекомендовал, учитывая что слепое присвоение генерирует wire, которого в коде нет.
благодарю за поясненик.
я ниразу не использовал подобную конструкцию, т.к. считаю что она усложняет понимание текста делая его не однотипным по стилю.
Отцов русской демократии спасёт: "`default_nettype none". Какой компилятор генерирует wire для wild-assignment? Такого быть вроде не должно, если объект с нужным именем не найден в scope — должна быть ошибка. Вполне успешно использую, вполне удобно, но безусловно менее читаемо и информативно.
А вы пробовали? мой софт так делает вроде бы это по стандарту… Софт от MentorGraphics
Множество раз. Но раз Вы спросили — проверил.
Пример
module test0
(
  input  logic    a,
  output logic    b
);

  test1
    test1_instance
  (
    .*
  );

endmodule

module test1
(
  input  logic    in_a,
  output logic    out_b
);
  always_comb
    out_b = ~in_a;

endmodule

Вот так ругается компилятор Quartus II

Error (10897): SystemVerilog error at test0.sv(10): can't implicitly connect port "in_a" on instance "test1_instance" of module "test1" - no such object is visible in the present scope
Error (10897): SystemVerilog error at test0.sv(10): can't implicitly connect port "out_b" on instance "test1_instance" of module "test1" - no such object is visible in the present scope


Пойдём дружно читать стандарт по этому поводу.
Немного не о том говорил в вашем случае конечно не понятно, что и куда подсоединять.
До работы доеду попробую написать пример, где возможно появление wire.
Почитал. Не должно быть такого по стандарту. Более того, если объекты разного размера тоже должна быть ошибка.
Я говорил примерно о таком примере:
module test0
(
output logic a,
output logic b
);

test1 test1_instance (.*);
test2 test2_instance (.*);


endmodule

module test1
(
input in_a,
output logic out_b
);
always_comb
out_b = ~in_a;


endmodule

module test2
(
output logic in_a,
input out_b
);

assign in_a = 1;

endmodule

Но он тоже выдаёт ошибку. Так что да вы правы wire сам по себе не появляется.
Сравните сами листинг приведенный выше в записи на SystemVerilog:

Несколько замечаний. Во-первых, приведённый листинг написан на языке Verilog (который, конечно, подмножество SystemVerilog). Для того, чтобы он стал SystemVerilog (и следовал его букве и духу) нужно использовать always_ff@(...).
Во-вторых, глядя на название модуля, можно подумать, что это делитель 27МГц до 1Гц, но на самом деле выходная частота будет 27e6/(27e6+1) и скважность не 50%.
Так же в SV есть удобство в виде автоматического выравнивания разрядности данных.
Т.е. если в проекте используется тип данных integer но по логике работы понятно что значение данного регистра не превышает 255 (в десятичной системе),
то после компиляции данный регистр будет 8 разрядным.
Таким образом налицо очередная вольность допускаемая стандартом SV — он не требует явного выравнивания длин регистров.
Не факт и не стандарт. Это оптимизация: remove registers with lost fan-outs. И автоматическое приведение разрядности сродни автоматическому приведению типов — когда хорошо, а когда и великое зло.
always_ff
это один из вариантов реализации блока события. который говорит компилятору, что данный блок надо стараться синтезировать на триггерной (?) логике.
второ тип always_comb (не уверен, что записал верно) — говорит, что данный блок необходимо синтезировать на комбинационной логике.
always — компилятор сам решает какой логикой реализовать блок.
по опыту (втом числе стороннему) always_ff и slways_comb применяются при описаниее конечных автоматов.
по опыту работы в Quartus — ему безразлично что писать вначеле блока. это влияет только на ошибки компиляции, но не на итоговую синтезированную схему (rtl-модель)
Это говорит о том, что этот блок содержит чисто sequential (триггерную) логику и любое другое должно рассматриваться как ошибка. Меньше возможностей выстрелить себе в ногу, а это уже добро. Конечно, можно писать на SystemVerilog так же как на Verilog (аналогично писать на C++ в стиле C), но только непонятно зачем тогда SV.

SystemVerilog это язык описания (снитеза) и верификации (моделирования) аппаратных средств. И вот верификации в нём уделено гораздо больше внимания, нежли синтезу — синтезируемые конструкции очень небольшое подмножество языка. Правильно смоделированный и верифицированный модуль почти 100% сразу заработает на кристалле.
Ой ну не всегда, то что насинтезирует синтезатор будет работать так же как исходная модель, например очень часто синтезированная модель с задержками может работать как то совсем не так, как ожидалось. Конечно если код писать правильно и знать все подводные камни синхронной и асинхронной логики и везде подстилать соломку, то по идее всё будет работать, но ресурсы кристалла обычно не безграничны.
А задержки вроде несинтезируются. И верификация проходит ведь в два этапа: сначала rtl (пруф оф концепт), потом gate-level (пруф оф синтез). Вот на gate-level должны вылезти ошибки с задержкаи. В целом да, надо чётко понимать где заканчивается модель и начинается синтез. И вообще хорошо знать во что именно синтезируется та или иная конструкция.
На этапе после трассировки, есть возможность получить модель задержек, которую потом можно засунуть в среду моделирования, и вы получите временные диаграммы с задержками на гейтах и линиях, вот тут вылезти может всё, что душе угодно, особенно, если человек в схемотехнике далеко не гуру. Но скорость моделирования с задержками просто никакая 1с модельного времени можно неделю моделить для 20к гейтов.
Эмм… Вы сейчас про физику? Про неизбежные задержки распространения сигналов внутри кристала и соблюдение соотношений tsu/th/tco для триггеров и метастабильность? Если так, то это уже следующий этап синтеза, который решается фиттером/выбором подходящего кристалла/логики/техпроцесса.

Может я чего не понимаю, но мой обычный воркфлоу выглядит так:
1. Моделирование поведения (возможно включает быстрое прототипирование на других языках, для проверки алгоритма и т.п.)
2. Написание собственно модели на языке верификации/синтеза (обычно SystemVerilog)
3. Написание testbench'a (этакий unit-test), прогоны RTL модели.
4a. Написание constraints (описание частот, известных входных выходных задержек) в которые надо втиснутся при синтезе gate-level.
4b. Синтез gate-level модели (фиттер уже на этом этапе выдаёт отчёты о том, втиснулся ли он в constrain'ы и как хорошо у него это получилось для разных температур и кристаллов), прогоны gate-level модели на testbench'ах (угу, если модель небольшая или времени навалом =).
4c. В случае фейлов по втискиванию в констрейны начинаем сначала. Смотрим fail paths, пытаемся изменить алгоритм (смотрим что можно соптимизировать, как средство применяем pipelining, если latency не очень важна).
5. Получаем что-то, что можно зашить в кристалл. Зашиваем и убеждаемся, что ничего не работает =). Начинаем с начала и возможно приходим к выводу, что для решения этой задачи надо либо ПЛИС потолще и побыстрее, либо вообще ASIC или выпекать. =)
Я конечно не гуру constraints, но бывают случаи когда вроде как фиттер говорит, что мы на такой частоте работать можем, а по факту получается, что не можем.

Вообщем есть такой файл задержек обычно он имеет расширение SDF, после фиттера его можно получить и добавить к прогонам gate-level модели, ну у вас уж очень много времени, SDF файл учитывает физику кристалла и вероятность, если тесты прошли с задержками, что ваш кристалл будет работать сильно повышаются, это справедливо для Actel APA и Actel A3P. В квартусе, если вы делаете gate-level для ModelSim, то в папке с файлами симуляции будет файл *.sdo вот он содержит задержки. Можно моделить с ними это медленно, можно без них это быстрее.
у меня обычно было на оборот. проект на 100МГц, фиттер ругается, что максимум можно на 72МГц.
но при проверке «в полевых условиях» выявилось, что все прекрасно работает.
Дело в том, что фиттер (а точнее time analyzer) Quartus'a (в разных версиях правда по разному) проверят на worst case. То есть берётся самый статистически медленный кристалл при высокой температуре. В текущих версиях он по умолчанию проверят три граничных условия (corner case): slowest chip +100 C, slowest chip -40 C, fastest chip -40 C (температуры для индустриальных чипов). Я за свою практику ни разу не видел граничных чипов (slowest или fastest), по натурным измерениям все около средних значений (впрочем так и должно быть, статистика вещь упрямая). Поэтому мы получаем, что в среднем граничная частота будет выше, чем насчитал фиттер/анализер для худшего случая. Но нужно учитывать метастабильность, то есть модель будет работать большую часть времени, но изредка будут сбои.

Кстати, по поводу «как хорошо писать на Verilog/SystemVerilog» рекомендую прочесть бумагу Verilog GOTCHAs, описаны типичные ловушки и неоднозначности при использовании этих языков и рекомендации по их избежанию.
www.lcdm-eng.com/papers/snug06_Verilog%20Gotchas%20Part1.pdf
www.lcdm-eng.com/papers/snug07_Verilog%20Gotchas%20Part2.pdf
пожалуй мне стоит сделать оговорку, что 99,9% моих проектов писались в среде Quartus II. Как я понимаю, данная среда безбожно игнорирует требования стандарта SV. Это приводит к тому, что данная среда игнорирует множество конструкций. Они являются не синтезируемыми.
В том числе и блоки always_ff, always_comb, always_latch — независимо от применяемого слова синтезированная RTL-модель была одинаковая.

По факту в своих проектах из SV я использую малую толику.
Например очень удобно описывать состояния автомата через перечисления:
enum bit [0:7]
{
    reset,
    idle,

    s00,
    s01
}state, new_state;
про автоматическое выравнивагие:
прелесть языка в том, что он не запрещает вручную указывать разрядности ренистров.
иногда (в моей практике крайне редко) это мешает (выравнивание разрядности) но чаще это упрощает.
особенно если придерживаться хорошего правилаописания аппаратуры: текст описания отдельного модуля не должен превышать 1-2 листов формата А4
что касается названия — оно взято из примера на VHDL. не я его придумал.
Я просто хотел обратить внимание, что счётчик считает с нуля и это распространённая ошибка, что при написании делителя счётчик сравнивают с N, а не с (N — 1). Ведь от 0 до N число состояний (N + 1).
На SV я бы реализовал делитель вот так:
module div_by_N
#(
  parameter N = 1000
)
(
  input  logic    areset,
  input  logic    clk,
  input  logic    ena,

  output logic    divided_clk
);
  
  localparam Nm2 = N / 2;
  localparam CW  = $clog2(Nm2);
  
  typedef logic [CW-1:0] counter_t;
  
  counter_t   counter;
  
  logic   nextedge;
  
  always_comb
    nextedge = (counter == (Nm2 - 1));
  
  always_ff@(posedge areset or posedge clk)
    if (areset)
      counter <= '0;
    else
      if (ena)
        if (nextedge)
          counter <= '0;
        else
          counter <= counter + 1'b1;

  always_ff@(posedge areset or posedge clk)
    if (areset)
      divided_clk <= '0;
    else
      if (ena)
        if (nextedge)
          divided_clk <= ~divided_clk;

endmodule

Я хочу добавить следующее ИМХО:

VHDL — похож на Pascal;
Verilog — похоже на C (если не обращать внимания на begin и end);
SystemVerilog — похож на C++, в нём появилось очень много всего, что помогает писать синтезируемый код:
— пользовательские типы данных;
— объединения и структуры;
— тип данных logic;
— более адекватное объявление портов;
— always_ff, always_comb, always_latch;
и писать программы верификации:
— удобный генератор случайных чисел;
— появились классы;
— появились интерфейсы, которые из ООП;
— появились ассерты;
Вообщем и целом SV язык для верификации, мало софта может из него синтезировать, мало софта его понимает, но это не повод от него отказываться.

P.S. Это я перечислил, то чем приходилось пользоваться, а так возможности SV сильно больше, чем у Verilog
полностью согласен.
и крайне жаль, что системы синтеза не способны реализовать всю мощь его стандарта.
потому я делал (в коментариях) заметку, что из SV только часть описания модуля: тип logic, описание портов модуля.
для красоты always_ff — для тактирования переключения состояний автомата, always-comb — для отработки состояний автомата.
пробовал работать с задачами (task) и функциями — квартус пректы синтезировал, но они работали специфически — не всегда правильно.
это как запись вида
c = a/b; — квартус синтезирует однотактный делитель, которы иногда делит не верно.
(знаю, что так делать нельзя, и однотактное деление по сути зло)
c = a/b; — квартус синтезирует однотактный делитель, которы иногда делит не верно.
(знаю, что так делать нельзя, и однотактное деление по сути зло)

Вы меня пугаете! Пример неправильного делителя насинтезированного квартусом в студию. И однатактное (чисто комбинационное) деление не зло, а один из способов решения задачи.
к сожалению безвозвратно утерял пример.

помню лишь саму суть:
считал что-то похожее на средне-арифметическое, но в знаменателе была не константа.
числа 32 битные.
проверял по signal-tap (вроде так пишется), в реальных условиях.
модуль работал интересно — при прочих равных мог отработать как надо, а мог и не как надо.
проверка данных на входе, выходе и внутри модуля показала, что на этапе деления произвольного (вычисляемого модулем числа) на произвольное (вычисляемое модулем число) результат деления не всегда был верным.

строка имел вид такой же, как я описывал выше (с другими именами регистров).

мне нужно было найти отношение максимума в массиве к его окрестности.
максимум вычислялся как среднее арифметическое центральных ячеек, а окрестность — как крайние точки массива.

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

справедливости ради замечу, что при делении на константу, такой делитель всегда дает верный результат деления.
Возможно, не в однотактном делителе дело.
У меня была похожая ситуация. Я сделал по глупости однотактный 64-битный сумматор.
Если не указать в constrains ограничение на частоту clock, то САПР может сгенерировать прошивку с рабочей частотой ниже, чем опорная частота генератора частоты на плате, и тогда будет работать многое неправильно.
Справедливости ради замечу, что не проверял (на тот момент) сообщения анализатора частотных характеристик синтезируемой схемы. Более того, я применял упрощенный анализатор. Оказалось проще перейти к иному принципу достижения той же цели — перешел от деления к умножению и все заработало.
Между сторонниками VHDL и Verilog идет холивар и конца ему не видно. Исторически сложилось, что в европе более распространен VHDL, а в штатах — Verilog. В России — приблизительно поровну. В вузах более распространен VHDL, а в коммерческих организациях — Verilog.
Мое мнение, что профессионал должен знать оба языка. Я сам писал и на Verilog_е и на VHDL_е. В чистом верилоге не хватает многих возможностей VHDL, например описания шин. Но в SystemVerilog_е все это имеется. Сам я предпочитаю SystemVerilog, но если требуется, без проблем перехожу на VHDL. Хотя, признаюсь, меня порой выбешивает его многословность, например операторы-паразиты entity, is, use, portmap (((
Задача профессионала — выдавать качественный продукт на любом языке, который требуется заказчику. Используемый язык — дело десятое. Гораздо важнее выбранная платформа, используемые алгоритмы, правильное разбиение на модули, документирование кода и тому подобные «мелочи».
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории