admin / 02.07.2018

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

.

О видах присваиваний в Верилоге

Верилог имеет три вида оператора присваивания: непрерывное, блокирующее и неблокирующее. Если с непрерывным, или постоянным присваиванием все более-менее понятно, то разница между блокирующим и неблокирующим присваиваниями не столь отчетлива и во многих руководствах она остается за кадром. К сожалению, нередко встречаются утверждения о том, что блокирующие присваивания «выполняются последовательно». Некоторые же идут настолько далеко, что дают советы использовать неблокирующие присваивания тем, кто хочет, чтобы их код исполнялся побыстрее. Цель этой статьи — развеять туман и помочь начинающим составить представление о том, что же именно представляют из себя различные виды присваиваний в синтезируемом подмножестве Верилога.

Непрерывное присваивание

То, что в Верилоге именуется «постоянным», или «непрерывным» присваиванием на самом деле всегда развертывается в комбинаторную схему. Непрерывное присваивание может встречаться в операторе assign, либо же прямо в декларации сигнала wire. Левой частью всегда является сигнал, правой — выражение, использующее любые другие сигналы. Значения регистровых переменных тоже являются сигналами.

Примеры:

// регистр, содержащий семплированное значение входа strobe
reg strobe_sampled;

// декларация сигнала strobe_negedge
wire strobe_negedge;
// непрерывное присваивание выражения сигналу strobe_negedge
assign strobe_negedge = ~strobe & strobe_sampled;

// декларация и присваивание совмещенные в одном операторе
wire strobe_posedge = strobe & ~strobe_sampled;

Неблокирующее присваивание

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

Пример

reg reg_A;
reg reg_B;
wire swap_en;

always @(posedge clk) begin
    if (swap_en) begin
        reg_A <= reg_B;
        reg_B <= reg_A;
    end
end

Человеку непосвященному скорее всего покажется, что по фронту сигнала clk, если swap_en равен «1», регистры reg_A и reg_B примут значение, которое reg_B имел до свершения события. В действительности же эта запись соединяет выход регистра reg_A со входом reg_B, а выход reg_B со входом reg_A. Таким образом, если в момент положительного перепада clk сигнал swap_en установлен в «1», в каждый из регистров записывается предварительно установившееся на его входе значение. Для reg_A это значение reg_B, а для reg_B — это значение reg_A. Два регистра обменялись значениями одновременно!

Пример 2

input strobe;
reg strobe_sampled;

reg[7:0] count;

always @(posedge clk) begin
    strobe_sampled <= strobe;
    if (strobe & ~strobe_sampled) begin
        // событие: положительный перепад на входе «strobe»
        count <= count + 1;
    end
end

По фронту clk происходит запись текущего значения strobe в регистр strobe_sampled. Параллельно происходит проверка,
а не единице ли равно текущее значение strobe и не ноль ли при этом значение strobe_sampled.

Пишем «демку» для LESO2 на Verilog

Схема, синтезируемая из условия if использует выход регистра strobe_sampled. То есть, условие внутри if можно понимать как «strobe равно единице и предыдущее значение strobe равно нулю».

При этом не будет лишним повторить, что эта запись на самом деле не так проста, как кажется. Например, условие внутри if — это выход комбинаторной схемы, которая не связана с тактирующим сигналом и поэтому может быть описана извне:

wire strobe_posedge = strobe & ~strobe_sampled;

Но это еще не все. Новичок скорее всего прочитает этот код примерно так: «если обнаружен положительный перепад сигнала strobe, взять содержимое count, увеличить его на 1 и записать обратно в count». В действительности же следует читать это как: «в регистр count записывается значение выражения, которое к моменту обнаружения положительного перепада сигнала strobe имеет установившееся значение count + 1». Вариант записи, иллюстрирующий такое прочтение:

wire strobe_posedge = strobe & ~strobe_sampled;
wire [7:0] count_incr = count + 1;
always @(posedge clk) begin
    strobe_sampled <= strobe;
    if (strobe_posedge)
        count <= count_incr;
end

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

always @(posedge clk) begin
    count <= count + 1;
    if (count == 10) count <= 0;
end

Этот счетчик имеет период счета равный 11.

Выражение count == 10 выполняется на один такт позже после того, как в регистр count было записано значение 10. Один из способов исправить положение — употребить в if то же выражение, что и в правой части присваивания:

if (count + 1 == 10) count <= 0;

Иногда удобно выносить выражения типа count + 1 из блоков always, это позволяет уменьшить вероятность ошибок в случае их многократного использования.

Блокирующее присваивание

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

always @(posedge clk) begin
    x = x + 1;
    y = y + 1;
end

и

always @(posedge clk) begin
    x <= x + 1;
    y <= y + 1;
end

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

В то же время, следующая запись являет собой что-то новое:

always @(posedge clk) begin
    x = x + 1;
    y = x;
end

Здесь x увеличится на 1, а y примет значение x + 1. Чтобы записать это выражение неблокирующими присваиваниями, потребовалась бы такая запись:

always @(posedge clk) begin
    x <= x + 1;
    y <= x + 1;
end

Цепочку блокирующих присваиваний можно рассматривать как одно большое выражение. Еще пример:

y <= 3*((input_value >> 4) + center_offset);

или:

y = input_value >> 4;
y = y + center_offset;
y = 3 * y;

Эти две записи эквивалентны. Но вторую запись нельзя понимать как последовательную цепочку вычислений. Это верно лишь в том смысле, что всё выражение действительно выстраивается в схему, в которой сначала отрезаются 4 младших разряда, результат и второй операнд идут на вход сумматора, а выход сумматора отдается умножителю на три. Так это представляется в электрической схеме и человек для удобства нарисует эту схему слева направо и читать он ее будет последовательно. Но в получившейся схеме все это выражение выполняется непрерывно, так же как и в предыдущей записи с неблокирующим присваиванием. Запись результата в регистр, как и следовало ожидать, происходит по фронту тактового импульса.

Заключительный пример, использующий оба вида присваиваний, заодно напоминающий о неродстве оператора for с одноименными операторами в алгоритмических языках программирования:

// Эквивалентная запись:
// history[3] <= history[2]; history[2] <= history[1]; history[1] <= history[0]; history[0] <= current;
always @(posedge clk) begin
    for (i = 1; i < 4; i = i + 1) history[i] <= history[i-1];

    history[0] <= current;
end

// Эквивалентная запись:
// avg <= (history[3]+history[2]+history[1]+history[0])/4;
always @(posedge clk) begin
    sum = 0;
    for (i = 0; i < 4; i = i + 1) sum = sum + history[i];

    avg = sum/4;
end

Этот пример выдает скользящее среднее с запаздыванием на два такта.

Регистры и провода

Регистровый тип является логической сущностью Верилога и не всегда превращается в физический регистр при синтезе. Иногда регистровый тип нужен для того, чтобы обойти некоторые ограничения синтаксиса языка. Это можно просто помнить, а можно творчески использовать.

Пример. Шинный мультиплексор.

wire [7:0] data_in = cpu_memr ? ram_data :
                                cpu_inport? io_data :
                                interrupt ? 8’hFF : 8’hZZ;

Тернарные выражения удобны для описания подобных конструкций. Однако следует проявлять осторожность, потому что в действительности то, что описано в этом выражении является приоритетным шифратором. Кроме приоритетности, побочным эффектом может являться неравное время установки результата в зависимости от входных значений. «Честный» же мультиплексор можно описать только с помощью оператора case. Но оператор case может быть только внутри always блока, а нужно, чтобы шина data_in имела установившееся значение к очередному фронту тактового сигнала, то есть присваивание должно быть асинхронным.

Выручит конструкция такого вида:

reg [7:0] data_in;

always
    case ({cpu_memr,cpu_inport,interrupt})
    3’b100:    data_in <= ram_data;
    3’b010:    data_in <= io_data;
    3’b001:    data_in <= 8’hFF;
    default:    data_in <= 8’hZZ;
    endcase

Несмотря на то, что переменная data_in формально является регистром, она будет синтезирована как обычная шина типа wire, а присоединена эта шина будет к выходу описанного оператором case мультиплексора. Переменная регистрового типа превращается в регистр только тогда, когда её присваивание происходит по перепаду тактового сигнала. В противном же случае она фактически является эквивалентом переменной типа wire.

Выводы

Мы рассмотрели на несложных примерах три вида присваиваний, их схожесть и отличия. Если после прочтения этой статьи понимания разницы между блокирующим и неблокирующим присваиваниями не появилось, стоит попробовать запустить симуляцию рассмотренных в статье примеров, проанализировать результаты симуляции и, если возможно, результат синтеза схем. В прилагаемом архиве лежит проект для Altera Quartus II, в котором содержатся все использованные выше примеры.

—-—

Адрес этой статьи: http://sensi.org/~svo/verilog/assignments

Просьба указывать ссылку на первоисточник.

Copyright © 2009 Viacheslav Slavinsky  svofski on gmail

Updated: Fri Feb 27 00:01:57 UTC 2009

Железное противостояние: VHDL против Verilog

Introduction to Verilog

Verilog is a type of Hardware Description Language (HDL). Verilog is one of the two languages used by education and business to design FPGAs and ASICs. If you are unfamilliar with how FPGAs and ASICs work you should read this page for an introduction to FPGAs and ASICs. Verilog and VHDL are the two most popular HDLs used. Compared to traditional software languages such as Java or C, Verilog works very differently. Let’s get started by looking at a simple example.

First we will create a Verilog file that describes an And Gate. As a refresher, a simple And Gate has two inputs and one output. The output is equal to 1 only when both of the inputs are equal to 1. Below is a picture of the And Gate that we will be describing with Verilog.

An And Gate

Let’s get to it! One fundamental unit of Verilog is called a wire. For now let’s assume that a wire can only be a 0 or a 1. Here is some basic wire logic:

wire and_temp; assign and_temp = input_1 & input_2;

We are creating a wire called and_temp on the first line of code. On the second line of the code, we are taking the wire that we created and we are assigning the wire. To assign it, we are using the Boolean AND function which in Verilog is the Ampersand (&). If you were to describe the code shown above, you might say, «The signal and_temp gets input_1 AND-ed with input_2.»

Input_1 and Input_2 are inputs to this piece of Verilog Code. Let’s show the complete list of inputs and outputs. This is done in the module definition. Module is a reserved keyword in Verilog which shows the creation of a block of code with defined inputs and outputs.

module example_and_gate ( input_1, input_2, and_result); input input_1; input input_2; output and_result;

This is your basic module. It defines our module called example_and_gate and 3 signals, 2 inputs and 1 output. Let’s put everything together to finish the file. The only thing we are missing is the assignment of the output and_result. One other note, // in Verilog is used for a comment.

/////////////////////////////////////////////////////////////////////////////// // File Downloaded from http://www.nandland.com /////////////////////////////////////////////////////////////////////////////// module example_and_gate ( input_1, input_2, and_result); input input_1; input input_2; output and_result; wire and_temp; assign and_temp = input_1 & input_2; assign and_result = and_temp; endmodule // example_and_gate

Congratulations! You have created your first Verilog file.

Does it seem like you had to write a lot of code just to create a stupid and gate? First of all, and gates aren’t stupid. Secondly, you are correct, HDLs take a lot of code to do relatively simple tasks. You can take some comfort in the fact that Verilog is at least less verbose than VHDL. Get used to the fact that doing something that was very easy in software will take you significantly longer in an HDL such as Verilog or VHDL. But just ask some software guy to try to generate an image to a VGA monitor that displays Conway’s Game of Life and watch their head spin in amazement! By the way, that video is created with an FPGA. You will be able to do that soon enough!

Next we will discuss another fundamental Verilog keyword: ALWAYS

Пишем «демку» для LESO2 на Verilog

.

Особенности языков описания архитектуры Verilog и VHDL

Вводные замечания

Языки VHDL и Verilog (Verilog HDL) относятся, в отличие от языка Argus, к языкам описания аппаратуры. Поэтому их нельзя напрямую сравнивать с Argus — они предназначены не для написания программ для FPGA и др. СБИС, а для проектирования логики самих этих устройств. Эти языки предназначены для моделирования электронных схем на уровнях вентильном, регистровых передач, корпусов микросхем. Поэтому эти языки можно назвать языками сквозного функционально-логического проектирования.

VHDL (Very high speed integrated circuits Hardware Description Language) был разработан в 1983 г. по заказу Пентагона с целью формального описания логических схем для всех этапов разработки электронных систем. Первый стандарт был утверждён в 1987 г., последний из известных — в 2002 г.

Verilog HDL был разработан фирмой Gateway Design Automaton как внутренний язык симуляции. Cadence приобрела Gateway в 1989 г. и открыла Verilog для общественного использования. В 1995 г. был определен стандарт языка — Verilog LRM (Language Reference Manual), IEEE1364-1995, а последний из известных — в 2001 г.

Первоначально VHDL предназначался для моделирования (что и объясняет его большую универсальность), но позднее из него было выделено синтезируемое подмножество. Написание алгоритмической модели на синтезируемом подмножестве гарантирует автоматический синтез по этой модели алгоритмической схемы. Аналогичная поддержка существует и для Verilog.

Основные составляющие языков VHDL и Verilog

Типы данных

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

4.2.2 Знаковые и беззнаковые числа в выражениях.

В Verilog, например, они бывают только цепными и регистровыми (последние могут запоминаться где-то).

Другие элементы VHDL и Verilog

В VHDL синтаксис позволяет описывать модель в разных стилях (структурное, потоковое, поведенческое описания), а также встраивать в описание фрагменты языков программирования высокого уровня (Си, Паскаль). Этим и достигается его большая универсальность и применяемость не только для описания архитектур вычислительных систем. Например, моделирование разных физических систем у него имеет поддержку в виде типов с физическими размерностями.

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

Заключение

Несмотря на похожие названия, Verilog HDL и VHDL — различные языки. Verilog — достаточно простой язык, сходный с языком программирования Си — как по синтаксису, так и по «идеологии». Малое количество служебных слов и простота основных конструкций упрощают изучение и позволяют использовать Verilog в целях обучения. Но в то же время это эффективный и специализированный язык. VHDL обладает большей универсальностью и может быть использован не только для описания моделей цифровых электронных схем, но и для других моделей. Однако из-за своих расширенных возможностей VHDL проигрывает в эффективности и простоте, то есть на описание одной и той же конструкции в Verilog потребуется в 3√4 раза меньше символов (ASCII), чем в VHDL.

Оба языка поддерживаются в качестве стандартов большим количеством программных продуктов, в том числе и open source, в области САПР. Имеются и компиляторы, и симуляторы для обоих языков, в том числе, например, и с первого языка на второй. Именно эти языки используются при проектировании (с помощью современных средств САПР ведущими производителями FPGA) не только самих СБИС, но и готовых модулей (ядер), мегафункций (megafunctions), предназначенных для решения достаточно сложных задач обработки сигналов.


© Лаборатория Параллельных информационных технологий НИВЦ МГУ

Часть 1 Введение в Verilog

Подробности
Автор: EngineerDeveloper®

Verilog, Verilog HDL (англ. Verilog Hardware Description Language) — это язык описания аппаратуры, используемый для описания и моделирования электронных систем. Verilog HDL, не следует путать с VHDL (конкурирующий язык), наиболее часто используется в проектировании, верификации и реализации (например, в виде СБИС) аналоговых, цифровых и смешанных электронных систем на различных уровнях абстракции.

   Разработчики Verilog сделали его синтаксис очень похожим на синтаксис языка C, что упрощает его освоение. Verilog имеет препроцессор, очень похожий на препроцессор языка C, и основные управляющие конструкции «if», «while» также подобны одноимённым конструкциям языка «C».

   Следует отметить одну из самых важных отличительных особенностей программирования логики (Verilog, VHDL и т.д.) освоив которую, сразу же все возвращается на круги свои. Всё дело в том, что в ПЛИС все процессы выполняются одновременно! В то время как привычная и понятная нам процессорная техника работает иначе – последовательно. Уяснив эту важную отличительную особенность можно смело пытаться программировать.

   Все плюсы и минусы СБИС ПЛИС освещаются в других разделах. Тут же хотелось бы продолжить уже конкретнее о языке описания аппаратуры Verilog.

   Автор является приверженцем FPGA Xilinx, поэтому большинство примеров будет именно в пакете ISE 14.4, а времяночки из симулятора ModelSim 6.5b.

   Теоретический материал был найден на просторах интернета, а так же в книгах с полки.

Вводная. ПЛИС в картинках, а также немного о языках VHDL и Verilog.

С литературой и интернет ресурсами по теме Verilog можно ознакомиться в соответствующих разделах сайта.

   Статья ни в коем случае не претендует на полноту описания языка Verilog. И, конечно, она не может заменить множества книг, которые Вам придется прочитать, если Вы всерьез займетесь проектированием цифровых микросхем. Тем не менее, эти уроки послужат практическим навыком начинающим разработчикам.

   Для серьёзного обучения было бы правильно ознакомиться с международным стандартом — IEEE Std 1364-2001(стандарт на Verilog 2001).

FILED UNDER : IT

Submit a Comment

Must be required * marked fields.

:*
:*