VERILOG
Язык VERILOG был разработан в 1985 г. одной из корпораций США и позднее утвержден институтом электроинженеров США (IEEE) — стандарт IEEE 1364 в 1994 г. [2]. В приложении 2 дано краткое описание его подмножества. В отличие от VHDL, который строго типизирован и синтаксически напоминает языки ADA и PASCAL, VERILOG базируется на С, имеет меньше встроенных возможностей саморасширения, но зато более прост в реализации (более простой и быстрый компилятор), имеет более развитый интерфейс с языком Си и лаконичен, что позволяет уменьшить объем описаний схем примерно в полтора раза по сравнению с VHDL. В настоящее время принят вариант нового стандарта IEEE 1364—2001 (VERILOG-2001), который, в частности, включил в себя ряд стилистических средств, сближающих его с VHDL [15] (см. приложение 2). Тем не менее остается впечатление елки, на которую все время добавляют новые украшения, не имея возможности убрать ненужные старые. Ниже VERILOG-2000 означает то же, что и VERILOG-2001.
Идентификаторы,ключевые слова, системные имена
Идентификаторы, ключевые слова и системные имена строятся по одним и тем же правилам, поэтому они и объединены в данный параграф. Общие правила следующие:
• Идентификаторы не должны совпадать с ключевыми словами;
• Идентификаторы не могут начинаться с символов ($) или (‘- ап остроф);
• Идентификатор не может начинаться с цифры;
• Длина идентификатора по стандарту не ограничена, но может ограничиваться в конкретном компиляторе;
• Ключевые слова всегда пишутся строчными буквами;
• Системные имена начинаются с символа ($);
• Директивы компилятора начинаются с символа (‘ - апостроф);
• Компилятор различает строчные и прописные буквы.
X, z, Z и. щ
Идентифищпоры
Идентификаторы используются в языке verilog в качестве символических имен для обозначения переменных, констант, модулей, функций, задач и т.д. и могут
использоваться для обозначения этих объектов в любом месте описания в любом месте описания.
Идентификатор представляет собой последовательность из букв, цифр, символов из букв, цифр, символов
доллара ($) и символов подчеркивания (_) с учётом изложенных выше правил.
Числа
Числа или иначе константы могут определяться в десятичном, шестнадцатеричном, восьмеричном или двоичном форматах. В языке Verilog предусмотрены две формы для записи чисел. Первая форма представляет собой простое десятичное число как последовательность цифр от 0 до 9 и опционально может предварятся символами плюса или минуса. Вторая форма представления чисел имеет следующий формат:
<size><baseformat<number>
Поле size содержит десятичные цифр и указывает разрядность константы в битах, это поле опционально и может опускаться в случаях если разрядность константы заранее определена (Допустим производится присвоение константного значения временной, разрядность которой задана при ее объявлении). Если разрядность не определена, то разрядность принимается по умолчанию равной 32 (32 в большинстве систем синтеза, в некоторых разрядность по умолчанию может отличаться). Второе поле base format содержит букву, определяющую формат представления числа (десятичный, двоичный...). Эта буква предваряется символом одиночной кавычки ('). Формат представления определяется следующими буквами: d - десятичный; h - шестнадцатиричный; о - восьмеричный и b - двоичный. Допускается вводить эти буквы как прописными, так и строчными. Последнее поле number содержит цифры допустимые для выбранного формата: от 0 до 9 для десятичного формата; от 0 до 7 для восьмеричного; 0 и 1 для двоичного; от 0 до 9 и буквы а, Ь, с, d, е, f для шестнадцатеричного. Буквенные обозначения могут вводится как строчными, так и прописными буквами.
Числа имеющие знаковое представление предваряются опциональными символами плюса и минуса перед полем size, Эти символы перед полем number недопустимы. Запись -8’h5A эквивалентна записи –(8’h5A), а запись 8’h-5A вызовет синтаксическую ошибку.
Помимо указанных цифр в поле number могут присутствовать буквы x, X, z, Z и
символ (?) (В поле size они недопустимы). Буквы х и X обозначают неизвестное
(неопределенное) состоянии, т.е. состояние соответствующих битов неизвестно. Буквы
z и Z обозначают состояние высокого импеданса - z-состояние или отсутствие
источника (драйвера). Символ? эквивалентен символу z и предназначен для лучшей
читабельности кода в случае когда, это состояние безразлично (don't-care). Существует
Возможность сокращать запись числа, например: 8’b1 будет эквивалентно записи 8’b11111111, но запись 8’b01 будет эквивалентна записи 8’b00000001. В силу того, что такие сокращенные записи могут мешать читабельности кода, употреблять их без
особой надобности не следует.
Для улучшения читабельности допускается ещё два приёма: допускается вставлять пробелы и символы табуляции между полями записи, например 8’hB6 можно записать как 8 ’h B6. Вставлять пробелы внутри поля number не допускается, но можно вставлять символ подчеркивания (_), для разбиения числа на некоторые группы. Примеры 83465 можно записать как 83_465; 8’b01100111 можно записать как 8’b0110_0111. Символ подчёркивания в поле size или перед полем number недопустим.
Введение в программирование на языке Verilog
Из вышесказанного следует что Verilog - язык описания цифровых схем. Познакомимся с базовыми типами источников сигнала используемыми в языке.
Пожалуй, было бы не плохо начать наше обсуждение с понятия сигнал (signal). Сигналы – это электрические импульсы, которые передаются по проводам (wire) между логическими элементами схемы. Провода переносят информацию не производя над ней никаких вычислений. В цифровой схеме сигналы важны для передачи двоичных данных.
Базовый тип источника сигнала в языке Verilog – это провод, wire. Таким образом, если у вас есть арифметическое или логическое выражение, вы можете ассоциировать результат выражения с именованным проводом и позже использовать его в других выражениях. Вот пример декларации однобитного провода в “программе” Verilog:
wire a;
Вы можете ему назначить другой сигнал, скажем сигнал “ b ”, вот так:
wire b;
assign a = b;
Или вы можете определить сигнал и сделать назначение ему одновременно в одном выражении:
wire a = b;
У вас могут быть провода передающие несколько бит:
wire [3:0] c; //это четыре провода
Провода передающие несколько бит информации называются “шина”, иногда “вектор”. Назначания к ним делаются так же:
wire [3:0] d;
assign c = d; //“подключение” одной шины к другой
Количество проводов в шине определяется любыми двумя целыми числами разделенными двоеточием внутри квадратных скобок.
wire [11:4] e; //восьмибитная шина
wire [0:255] f; //256-ти битная шина
Из шины можно выбрать некоторые нужные биты и назначить другому проводу:
wire g;
assign g = f[2]; //назначить сигналу “g” второй бит шины “f”
Кроме того, выбираемый из шины бит может определяться переменной:
wire [7:0] h;
wire i = f[h]; //назначить сигналу “i” бит номер “h” из шины “f”
Вы можете выбрать из сигнальной шины некоторый диапазон бит и назначить другой шине с тем же количеством бит:
wire [3:0] j = e[7:4];
Так же, в большинстве диалектов Verilog, вы можете определить массивы сигнальных шин:
wire [7:0] k [0:19]; //массив из двадцати 8-ми битных шин
Еще существует другой тип источника сигнала называемый регистр: reg. Его используют при поведенческом (behavioral) описании схемы. Если регистру постоянно присваивается значение комбинаторной (логической) функции, то он ведет себя точно как провод (wire). Если же регистру присваивается значение в синхронной логике, например по фронту сигнала тактовой частоты, то ему, в конечном счете, будет соответствовать физический D-триггер или группа D-триггеров. D-триггер – это логический элемент способный запоминать один бит информации. В англоязычных статьях D-триггер называют flipflop.
Регистры описываются так же как и провода:
reg [3:0] m;
reg [0:100] n;
Они могут использоваться так же, как и провода в правой части выражений, как операнды:
wire [1:0] p = m[2:1];
Вы можете определить массив регистров, которые обычно называют “память” (R AM):
reg [7:0] q [0:15]; //память из 16 слов, каждое по 8 бит
Еще один тип источника сигнала – это integer. Он похож на регистр reg, но всегда является 32х битным знаковым типом данных. Например, объявим:
integer loop_count;
Verilog позволяет группировать логику в блоки. Каждый блок логики называется “модулем” (module). Модули имеют входы и выходы, которые ведут себя как сигналы wire.
При описании модуля сперва перечисляют его порты (входы и выходы):
module my_module_name (port_a, port_b, w, y, z);
А затем описывают направление сигналов:
input port_a;
output [6:0] port_b;
input [0:4] w;
inout y; //двунаправленный сигнал, обычно используется
//только для внешних контактов микросхем
Позже мы увидим, что выход модуля может быть сразу декларирован как регистр reg, а не как провод wire:
output [3:0] z;
reg [3:0] z;
Еще проще можно сразу в описании модуля указать тип и направление сигналов:
module my_module
(
input wire port_a,
output wire [6:0]port_b,
input wire [0:4]w,
inout wire y,
output reg [3:0]z
);
Теперь можно использовать входные сигналы, как провода w ire:
wire r = w[1];
Теперь можно делать постоянные назначения выходам, как функции от входов:
assign port_b = h[6:0];
В конце описания логики каждого модуля пишем слово endmodule.
module my_module_name (input wire a, input wire b, output wire c);
assign c = a & b;
endmodule
Последний тип источника сигнала, о котором мы поговорим на этом уроке – это постоянные сигналы или просто числа:
wire [12:0] s = 12; //32-х битное десятичное число, которое будет “обрезано” до 13 бит
wire [12:0] z = 13’d12; //13-ти битное десятичное число
wire [3:0] t = 4'b0101; //4-х битное двоичное число
wire [3:0] q = 8'hA5; //8-ми битное шестнадцатеричное число A5
wire [63:0] u = 64'hdeadbeefcafebabe; //64-х битное шестнадцатеричное число
Если точно не определить размер числа, то оно принимается по умолчанию 32-х разрядным. Это может быть проблемой при присвоении сигналам с большей разрядностью.
Числа – это числа. Они могут использоваться во всяких арифметических и логических выражениях. Например, можно прибавить 1 к вектору “ aa ”:
wire [3:0] aa;
wire [3:0] bb;
assign bb = aa + 1;
Синтаксис
Синтаксис операций Verilog похож на синтаксис операций языка С. Символы операций, их названия, а также применимость к вещественным переменным приведены в таблице 4.
Таблица 4
Символ | Описание операции | Применимость к real |
{} | Конкатенация | Не допустимо |
+ - * / | Арифметические | Допустимо |
% | Модуль | Не допустимо |
> >= < <= | Отношения | Допустимо |
! | Логическое отрицание | Допустимо |
&& | Логическое И | Допустимо |
|| | Логическое ИЛИ | Допустимо |
== | Логическое равенство | Допустимо |
!= | Логическое неравенство | Допустимо |
=== | Идентичность | Не допустимо |
!=== | Неидентичность | Не допустимо |
~ | Побитовая инверсия | Не допустимо |
& | Побитовое И | Не допустимо |
| | Побитовое ИЛИ | Не допустимо |
^ | Побитовое исключающее ИЛИ | Не допустимо |
{^~ ~^ | Побитовая эквивалентность | Не допустимо |
& | Редукционное И | Не допустимо |
~& | Редукционное НЕ-И | Не допустимо |
| | Редукционное ИЛИ | Не допустимо |
~| | Редукционное НЕ-ИЛИ | Не допустимо |
^ | Редукционное исключающее ИЛИ | Не допустимо |
~^ ^~ | Редукционное НЕ исключающее ИЛИ | Не допустимо |
<< | Сдвиг влево | Не допустимо |
>> | Сдвиг вправо | Не допустимо |
<<< | Циклический сдвиг влево | Не допустимо |
>>> | Циклический сдвиг вправо | Не допустимо |
?: | Условный оператор | Допустимо |
Теперь расположим операции Verilog в порядке убывания приоритета:
- + -! ~ (унарные) Наивысший приоритет
- * / %
- + - (бинарные)
- << >> <<< >>>
- < <= > >=
- ==!= ===!===
- & ~&
- | ~|
- &&
- ||
- ?: Низший приоритет
Операции с равным приоритетом выполняются слева направо, за исключением?: который выполняется справа налево.
Из полученных выше примеров попробуем описать на языке Verilog шифратор с 8 входами и 3-мя выходами.
СДНФ:
Y1=X1vX3vX5vX7
Y2=X2vX3vX6vX7
Y3=X4vX5vX6vX7
Зная СДНФ шифратора мы можем описать её на Verilog.
1-й способ:
module SHIFRATOR (input X1, input X2, input X3, input X4, input X5,input X6, input X7, output Y1, output Y2, output Y3);
assign Y1=X1|X3|X5|X7;
assign Y2=X2|X3|X6|X7;
assign Y3=X4|X5|X6|X7;
endmodule
2-й способ:
module SHIFRATOR (X1,X2,X3,X4,X5,X6,X7,Y1,Y2,Y3);
input X1,X2,X3,X4,X5,X6,X7;
output Y1,Y2,Y3;
assign Y1=X1|X3|X5|X7;
assign Y2=X2|X3|X6|X7;
assign Y3=X4|X5|X6|X7;
endmodule
Аналогичная ситуация будет и с четырехканальным мультиплексором, СДНФ которого:
1-й способ:
module MULTIPLEXOR (A0, A1, D0, D1, D2, D3, Y);
input A0, A1, D0, D1, D2, D3;
output Y;
assign Y=(D0&!A1&!A0)|(D1&A0)|(D2&A1&!A0)|(D3&A1&A0);
endmodule
2-й способ (описание мультиплексора через wire):
module MULTIPLEXOR (input A0, input A1, input D0, input D1, input D2, input D3,
output Y);
wire S1, S2, S3, S4, S5, S6;
NOT my_1_not (.OUT(S1),.IN1(A0));
NOT my_2_not (.OUT(S2),.IN1(A1));
AND3 my_1_and (.OUT(S3),.IN1(S1),.IN2(S2),.IN3(D0));
AND2 my_2_and (.OUT(S4),.IN1(A0),.IN2(D1));
AND3 my_3_and (.OUT(S5),.IN1(S1),.IN2(A1),.IN3(D2));
AND3 my_4_and (.OUT(S6),.IN1(A0),.IN2(A1),.IN3(D3));
OR4 my_or (.OUT(Y),.IN1(S3),.IN2(S4),.IN3(S5),.IN4(S6));
endmodule