Коротенькое сравнение VHDL и Verilog в помощь начинающим знакомство с ПЛИС

Исторически так сложилось что ПЛИС я начал изучать только на новой работе.
Это были серии ПЛИС фирмы Altera.

Старшие коллеги на перебой рекомендовали как AHDL так и VHDL для программирования этих микросхем.
В итоге я остановился на языке VHDL, поскольку он является языком высокого уровня, в отличии от ADHL.
Хоть и листинг у последнего был куда приятнее.

И я приступил к изучению всех хитростей и ограничений языка VHDL.
В итоге сошелся на мысли что конструкции языка просто ужасны, а ограничения избыточны для проектирования аппаратуры.

Приведу пример листинга из статьи «Делаем таймер или первый проект на ПЛИС».


Исходный код на языке VHDL
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.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

		  if (clk'event and clk = '1') 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) или функция конвертации типов).
Заставьте новичка сделать преобразование внутренней переменной типа integer в тип std_logic_vector и установить это значение на внешний порт!
И что мы получим? Несколько часов мучительных поисков того как же это сделать.
В итоге оказывается, что нам надо:
1. подключить библиотеки:
use ieee.std_logic_1164.ALL;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.ALL;

2. запись integer в порт std_logic_vector напрямую невозможна. Необходимо применять специальные функции преобразования типов! (Всегда возмущало понятие типа в проектах для ПЛИС)
std_logic_vector (to_unsgined(, ))
данная запись преобразует младших бит регистра в вектор std_logic_vector.
И того минимум 4 строки для записи числа в порт.

Так же для меня ужасом было явление записи вектора:
vector(M downto N)
или
vector(M upto N)

слова downto и upto просто выводили меня из равновесия своей избыточностью. По моему мнению они являются лишними.
Я уже молчу о том. что числа разных типов пишутся по разному:
integer пишется как нормально число — 1, 2, 3, 4, 5
а вот с std_logic_vector возникают проблемы.
Запись имеет вид '1' и '0' для единичного вектора, а вот вектора 2х и более разрядов по умолчанию НЕ МОГУТ принимать десятичные значения. Только HEX или BIN.

К счастью на этом мое знакомство с этим языком закончилось и я окунулся в религию языков Verilog и SystemVerilog.

Для меня это было откровение!
Никаких лишних записей. Листинг чистый и понятный. Каждый знак имеет оправданный смысл и место.
К сожалению современные компиляторы для ПЛИС не до конца поддерживают стандарт данных языков, от того не все их возможности можно использовать. (В моей личной практике Quartus II плохо понимал проект с использованием процедур и функций.
Так же он плохо понимал классы (или вообще не понимал? уже не помню).
Сравните сами листинг приведенный выше в записи на SystemVerilog:

Исходный код на языке SystemVerilog
module Div_27Mhz_to_1Hz
(
	 input 			 CLK,
	 output logic 		 CLK_OUT,

	 output logic[7:0] 		 CNT_OUT
);

integer cnt = 0;

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

endmodule



Как видите количество строк заметно уменьшилось. Сам листинг более прост и однотипен (числа везде записываются как числа). Блок проверки фронта сигнала перекочевал к месту объявления триггера процесса.
Так же я специально расширил функционал модуля для демонстрации удобства и простоты преобразования типа данных.
Как вы можете заметить в описании портов модуля я ввел специальный восьмибитный выходной порт CNT_OUT. Тип данных порта logic.
Тут стоит сделать легкое отступление о типах в SystemVerilog.
В SV есть два основных типа данных:
цепи (wire)
регистры (reg)
все остальные типы данных не воспринимаются Quartus в полной мере и потому мной не использовались.
При этом разница между данными типами (по стандарту) в том, что тип цепь не может принимать значения.

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

Стоит отметить что Quartus вообще игнорирует типы данных SV при компиляции. Его компилятор позволяет использовать wire как регистр и наоборот. Потому применение типа данных logic несколько условно. Мы имеем переменную счетчика типа integer (по умолчанию она 32 бита) и внешний порт вывода типа logic и разрядностью 8 бит.
Строка CNT_OUT <= cnt; делает следующее:
каждый такт в порт CNT_OUT записывается младшие 8 разрядов регистра cnt. (стоит отметить, что в данной записи не указан явно диапазон разрядов для записи в порт.
Потому берется 8 младших).

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

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

В целом эти языки можно сравнивать гораздо глубже и дольше. Но я уверен, что в бОльшем количестве всех сравнений SV покажет свою красоту, компактность текста и удобство применения.

В общем и целом SystemVerilog придется по вкусу программистам.
Если же вы закоренелый схемотехник то вам по нраву будет AHDL.
Я не привожу его в сравнении т.к. он совсем другая тема и сравнивать его с приведенными в статье языками, все равно что сравнивать ассемблер и С.
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

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

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

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

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

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


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

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

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


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

          cnt <= cnt + 1;
          if(cnt = 270)
          cnt <= 0;
          К слову, SV только altera поддерживает, при переезде на xilinx будут проблемы. Поэтому есть смысл писать на pure verilog, ибо он «сишнее» и занимает меньше строк для описания одного и того же по сравнению с vhdl. Плюс еще забивать голову новичку фишками SV — себе вредить.
          Хотя у всех своя религия
            +1
            Где reset в вашем коде? Не все ПЛИС умеют устанавливать схемы по умолчанию.
              0
              пример не имеет установки по сбросу, это верно.
              но это лишь пример, а не полноценный, кроссплатформенный проект.
              0
              вашь не будет работать, т.к. на одно событие два неблокирующих (вроде так называются) присвоения (<=) на один регистр.
                0
                нельза инкрементировать значение регистра и сразу же его сравнивать и обнулять.
                если же в вашем варианте заменить присвоение вида <= на =, то проект будет работать.
                однако реаоизации на блокирующих присвоениях плохо синтезируются.
                  0
                  в пректах от SV я использую лишь некоторые конструкции, которые компактнее и приятнее на вид.
                  в целом я не использую его функционал в полной мере.
              +1
              Для меня после знакомства с 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). Один блок — нет.
                0
                1. begin — end в SV уродует код в целом, согласен.
                2. эта проблема решается интерфейсами и более того. если я правильно помню стандарт, то при объявлении модуля внутри тела другого модуля не требуется явное указание подключения для одноименных портов. .clk(clk) в этом случае выглядит как .clk().
                3. не всегда требуется присоединение всех пинов к родителю (если я правильно понимаю о чем идет речь)
                  0
                  2. .clk(), — 7 лишних символов и 1 лишняя строка.
                  3. Конечно, присоединяются только пины, которые не были соединены вручную.
                    +1
                    если вы напишете ".clk()", то вы ничего не подключите. В sv можно подключать одноименные линии слепым присвоением ".clk", т.е. без скобок или вообще написать подключение всех линий как ".*". Пользоваться этим я бы вам не рекомендовал, учитывая что слепое присвоение генерирует wire, которого в коде нет.
                      0
                      благодарю за поясненик.
                      я ниразу не использовал подобную конструкцию, т.к. считаю что она усложняет понимание текста делая его не однотипным по стилю.
                        0
                        Отцов русской демократии спасёт: "`default_nettype none". Какой компилятор генерирует wire для wild-assignment? Такого быть вроде не должно, если объект с нужным именем не найден в scope — должна быть ошибка. Вполне успешно использую, вполне удобно, но безусловно менее читаемо и информативно.
                          +1
                          А вы пробовали? мой софт так делает вроде бы это по стандарту… Софт от MentorGraphics
                            0
                            Множество раз. Но раз Вы спросили — проверил.
                            Пример
                            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


                            Пойдём дружно читать стандарт по этому поводу.
                              0
                              Немного не о том говорил в вашем случае конечно не понятно, что и куда подсоединять.
                              До работы доеду попробую написать пример, где возможно появление wire.
                              0
                              Почитал. Не должно быть такого по стандарту. Более того, если объекты разного размера тоже должна быть ошибка.
                                0
                                Я говорил примерно о таком примере:
                                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 сам по себе не появляется.
                      +1
                      Сравните сами листинг приведенный выше в записи на SystemVerilog:

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

                          SystemVerilog это язык описания (снитеза) и верификации (моделирования) аппаратных средств. И вот верификации в нём уделено гораздо больше внимания, нежли синтезу — синтезируемые конструкции очень небольшое подмножество языка. Правильно смоделированный и верифицированный модуль почти 100% сразу заработает на кристалле.
                            0
                            Ой ну не всегда, то что насинтезирует синтезатор будет работать так же как исходная модель, например очень часто синтезированная модель с задержками может работать как то совсем не так, как ожидалось. Конечно если код писать правильно и знать все подводные камни синхронной и асинхронной логики и везде подстилать соломку, то по идее всё будет работать, но ресурсы кристалла обычно не безграничны.
                              0
                              А задержки вроде несинтезируются. И верификация проходит ведь в два этапа: сначала rtl (пруф оф концепт), потом gate-level (пруф оф синтез). Вот на gate-level должны вылезти ошибки с задержкаи. В целом да, надо чётко понимать где заканчивается модель и начинается синтез. И вообще хорошо знать во что именно синтезируется та или иная конструкция.
                                0
                                На этапе после трассировки, есть возможность получить модель задержек, которую потом можно засунуть в среду моделирования, и вы получите временные диаграммы с задержками на гейтах и линиях, вот тут вылезти может всё, что душе угодно, особенно, если человек в схемотехнике далеко не гуру. Но скорость моделирования с задержками просто никакая 1с модельного времени можно неделю моделить для 20к гейтов.
                                  0
                                  Эмм… Вы сейчас про физику? Про неизбежные задержки распространения сигналов внутри кристала и соблюдение соотношений 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 или выпекать. =)
                                    0
                                    Я конечно не гуру constraints, но бывают случаи когда вроде как фиттер говорит, что мы на такой частоте работать можем, а по факту получается, что не можем.

                                    Вообщем есть такой файл задержек обычно он имеет расширение SDF, после фиттера его можно получить и добавить к прогонам gate-level модели, ну у вас уж очень много времени, SDF файл учитывает физику кристалла и вероятность, если тесты прошли с задержками, что ваш кристалл будет работать сильно повышаются, это справедливо для Actel APA и Actel A3P. В квартусе, если вы делаете gate-level для ModelSim, то в папке с файлами симуляции будет файл *.sdo вот он содержит задержки. Можно моделить с ними это медленно, можно без них это быстрее.
                                      0
                                      у меня обычно было на оборот. проект на 100МГц, фиттер ругается, что максимум можно на 72МГц.
                                      но при проверке «в полевых условиях» выявилось, что все прекрасно работает.
                                        +1
                                        Дело в том, что фиттер (а точнее 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
                              0
                              пожалуй мне стоит сделать оговорку, что 99,9% моих проектов писались в среде Quartus II. Как я понимаю, данная среда безбожно игнорирует требования стандарта SV. Это приводит к тому, что данная среда игнорирует множество конструкций. Они являются не синтезируемыми.
                              В том числе и блоки always_ff, always_comb, always_latch — независимо от применяемого слова синтезированная RTL-модель была одинаковая.

                              По факту в своих проектах из SV я использую малую толику.
                              Например очень удобно описывать состояния автомата через перечисления:
                              enum bit [0:7]
                              {
                                  reset,
                                  idle,
                              
                                  s00,
                                  s01
                              }state, new_state;
                              
                            0
                            про автоматическое выравнивагие:
                            прелесть языка в том, что он не запрещает вручную указывать разрядности ренистров.
                            иногда (в моей практике крайне редко) это мешает (выравнивание разрядности) но чаще это упрощает.
                            особенно если придерживаться хорошего правилаописания аппаратуры: текст описания отдельного модуля не должен превышать 1-2 листов формата А4
                              0
                              что касается названия — оно взято из примера на VHDL. не я его придумал.
                                0
                                Я просто хотел обратить внимание, что счётчик считает с нуля и это распространённая ошибка, что при написании делителя счётчик сравнивают с 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
                                

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

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

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

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

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

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

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

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

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

                                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                  Самое читаемое