Files
APS/Basic Verilog structures/Verilog syntax.md

263 lines
26 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Конструкции языка Verilog
Verilog чувствителен к регистру. Это значит что "ЗаписЬ" и "запись" Verilog будет воспринимать как два разных слова. Все ключевые слова Verilog записываются строчными буквами.
Каждое утверждение оканчивается точкой с запятой (`;`). Таким образом, символ переноса строки не прерывает выражение и может быть использован для повышения читаемости кода.
В Verilog Си-подобные комментарии. Вы можете закомментировать одну строку или окончание строки с помощью двух косых черт (`//`). Кроме того, вы можете использовать многострочные комментарии — всё что находится между парами символов `/*` и `*/` считается комментарием.
Любое имя, определяемое пользователем (имя переменной/модуля и т.п.) должно начинаться с латинской буквы, а не с числа. Определяемые пользователем имена не должны совпадать с ключевыми словами Verilog.
В тексте будет много определений синтаксиса языка Verilog. При появлении новых конструкций будет использована следующая нотация:
|| |
|-------------|-------------|
| **жирным** | будут выделены ключевые слова Verilog, такие слова необходимо использовать в точности как они написаны |
|_курсивом_ | будут выделены имена, определяемые пользователем |
|<> |            в скобках будут указаны требуемые характеристики, такие как тип, направление input/output размерность и т.п.|
По прочтению этой главы вы узнаете:
* Типы данных, представленных в языке Verilog;
* Как описывать базовую конструкцию модуля Verilog.
## Типы данных
В Verilog, каждому сигналу, константе, переменной или функции должен быть присвоен _тип данных_. Какие-то типы являются синтезируемыми, а какие-то используются только для моделирования абстрактного поведения.
Verilog поддерживает четыре базовых состояния, которые может принять сигнал: 0, 1, X и Z. Ниже представлено описание каждого из этих состояний:
|Значение|Описание|
|--------|--------|
|0| Логический ноль / ЛОЖЬ|
|1| Логическая единица / ИСТИНА|
|X| Неопределенное (неизвестное) или непроинициализированное состояние|
|Z| [Высокоимпедансное состояние](https://ru.wikipedia.org/wiki/%D0%92%D1%8B%D1%81%D0%BE%D0%BA%D0%BE%D0%B8%D0%BC%D0%BF%D0%B5%D0%B4%D0%B0%D0%BD%D1%81%D0%BD%D0%BE%D0%B5_%D1%81%D0%BE%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D0%B5)|
<!-- В Verilog к этим значениям привязывается так называемая _сила сигнала_. Силы используются для определения значения сигнала, управляемого несколькими источниками. Сигнал принимает значение источника с большей силой сигнала. Если у источников силы сигнала равны, значение сигнала неопределено в случае, если источники подают разные сигналы, и равно сигналу источников, если они подают одно и тоже значение.
В университетских курсах по Verilog вы не будете использовать силы сигнала, о них написано выше лишь для полноты картины. -->
### Класс данных "Цепь"
Как говорилось выше, каждому сигналу в Verilog должен быть назначен тип данных. Класс данных под названием "цепь" моделирует соединение (электрическую цепь) между компонентами и может принимать значения 0, 1, X, Z. Сигнал цепи постоянно управляется источником сигнала и меняет своё значение каждый раз, когда меняется значение на источнике (цепь не может хранить значение).
![../.pic/Basic%20Verilog%20structures/verilog%20syntax/nets_variables.png](../.pic/Basic%20Verilog%20structures/verilog%20syntax/nets_variables.png)
Рассмотрим рисунок выше. Красным цветом выделены сигналы класса "цепь". Сигнал _net\_11_ используется для соединения выхода логического элемента И со входом данных D-триггера _data\_0_. Выход логического элемента И является источником сигнала для цепи _net\_11_. Выход _data\_0_ является источником сигнала _net\_2_.
В Verilog множество типов класса цепь, но самым распространенным является тип **wire**(провод). Вы можете представить подобный сигнал, как провод, подключенный к некому источнику. Значение сигнала в любой части провода одинаково и совпадает со значением подключенного к нему источника сигнала. Если провод не подключен ни к одному источнику, он находится в высокоимпедансном состоянии (принимает значение Z).
Есть еще один тип, полностью идентичный типу **wire**, который называется **tri**. Эти типы взаимозаменяемы и разделены только для повышения читаемости кода. Тип **tri** используется, когда сигналом управляют несколько источников.
![../.pic/Basic%20Verilog%20structures/verilog%20syntax/](../.pic/Basic%20Verilog%20structures/verilog%20syntax/tri_state.png)
Пример сигнала, управляемого несколькими источниками.
### Класс данных "Переменная"
Verilog также поддерживает типы данных, которые моделируют ячейки памяти. Сигналы класса "переменная" могут принимать значения 0, 1, X, Z<!-- , но с ними не связана сила сигнала -->. Типы класса "переменная" могут хранить присвоенное им значение до очередного присваивания. Ниже представлено описание переменных типов:
|Тип |Описание|
|------------|--------|
|**reg** | Переменная, моделирующая базовую ячейку памяти. Может принимать значения 0, 1, X, Z|
|**integer** | 32-битная переменная, представленная в [дополнительном коде](<https://ru.wikipedia.org/wiki/%D0%94%D0%BE%D0%BF%D0%BE%D0%BB%D0%BD%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BE%D0%B4>), представляющая диапазон чисел [2147483648:2147483647]|
|**real** | 64-битная переменная, представляющая числа с плавающей точкой в диапазоне [2.2×10<sup>308</sup>:2.2×10<sup>308</sup>]|
|**time** | Беззнаковая 64-битная переменная, принимающая значения в диапазоне [0:9.2×10<sup>18</sup>] |
|**realtime**| Синоним типа **real**. Используется для повышения читаемости|
### Векторы
В Verilog вектором называют одномерный массив элементов. Все типы класса "цепь", а также тип **reg** могут использоваться для формирования вектора. Синтаксис объявления вектора представлен ниже:
<pre>
<тип> [<старший индекс>:<младший индекс>] <i>имя_вектора</i>
</pre>
Несмотря на то, что может использоваться любой диапазон индексов (даже отрицательный), на практике стараются начинать младший индекс с нуля.
Пример:
<pre>
<b>wire</b> [7:0] <i>sum</i>; // Объявляется 8-битный вектор с именем sum типа wire.
// Старший индекс равен 7, младший — 0.
// Векторы типа wire обычно называют "шиной"(bus).
<b>reg</b> [15:0] <i>Q</i>; // В данной строке объявлен 16-битный вектор типа reg.
</pre>
Используя индекс, можно обратиться к отдельным битам вектора. С помощью диапазона индексов, можно получить доступ к диапазону соответствующих битов.
|фрагмент кода|описание|
|-------------|--------|
|sum[0]; | Обращение к младшему биту вектора sum, объявленного выше|
|Q[15:8]; | Обращение к старшим восьми битам 16-битного вектора Q, объявленного выше|
</pre>
Обратите внимание, что нельзя объявить вектор типа **integer**, **time**, **real**, **realtime**. Однако, типы **integer** и **time** являются векторами сами по себе. Иными словами, вы не можете объявить следующий вектор:
<pre>
<b>integer</b> [7:0] dunno_what_size_of_this_object;
</pre>
но можете обращаться к отдельным битам объявленного объекта как если бы он был вектором:
|фрагмент кода|описание|
|-------------|--------|
|<b>integer</b> int_object;<br>int_object[31];|<br>Обращение к старшему биту вектора|
Таким образом, вы можете считать **integer** синонимом `reg signed [31:0]` (о значении **signed** будет чуть позже), а **time** синонимом `reg [63:0]`
Поскольку **real** и **realtime** представляют числа с плавающей точкой, они не считаются векторами, и вы не можете обратиться к их отдельным битам.
### Множества (Arrays)
Множество (array) — это многомерный массив элементов. Множество можно назвать вектором векторов. Векторы внутри множества (элементы множества) обладают одинаковой размерностью. Для объявления множества, необходимо указать тип и размерность элементов множества, а также имя и размерность самого множества. При указании размерности множества, обычно сперва указывают начальный индекс, а после конечный.
Синтаксис объявления множества:
<pre>
<тип элемента> [<старший индекс элемента> : <младший индекс элемента>] <i>
имя_множества</i> [начальный индекс множества : конечный индекс множества]
</pre>
Пример:
<pre>
<b>reg</b> [7:0] <i>mem</i> [0:4095]; // Объявляется множество 8-битных векторов типа
// reg, размером 4096.
// Множество, элементами которого являются вектора
// типа reg (например то, что объявлено здесь)
// называют памятью.
// Элементы памяти называют "словами".
<b>integer</b> <i>A</i> [1:100]; // Объявляется множество со 100 элементами типа
// integer.
</pre>
К элементу множества можно обратиться, используя его индекс. Кроме того, если элементом множества является вектор, можно так же обратиться к отдельному биту этого вектора, добавив и его индекс.
Примеры:
|фрагмент кода|описание|
|-------------|--------|
|mem[2]; | Обращение к третьему элементу множества mem (третьему, поскольку нумерация начинается с нуля).<br> Подобное обращение вернет 8-битный вектор типа reg.|
|mem[2][7]; | Обращение к старшему биту третьего элемента множества mem.|
|A[2]; | Обращение ко второму элементу множества A (в этом случае, нумерация начинается с единицы, поскольку так было указано при объявлении множества A)|
### Целочисленные константы
В Verilog есть два способа выразить целое число. Первый способ — это обычная запись десятичного числа, формируемая последовательностью цифр от 0 до 9, с опциональным указанием знака перед ней. Второй способ — это запись целого числа с указанием основания системы исчисления. Данная форма записи состоит из трех частей:
1. опциональной константы размера
2. апострофа (`'`), за которым указывается формат основания системы исчисления
3. набора цифр, представляющих значение числа
<pre>
<размер>'[s]<основание><значение>
</pre>
Между каждой из частей может стоять пробел, но не внутри любой из них.
Первая часть (константа размера) указывает точное число бит целочисленной константы. Для указания размера используется ненулевое положительное десятичное число. Например, для указания двух шестнадцатеричных цифр потребуется 8 бит, поскольку каждая шестнадцатеричная цифра занимает 4 бита.
Вторая часть, формат основания системы исчисления состоит из апострофа и нечувствительной к регистру букве, обозначающей основание системы исчисления, перед которой допустимо указать символ s(или S) для указания того, что число знаковое.
|Синтаксис|Описание |
|---------|-------------------------------|
|**'b** | Беззнаковое двоичное |
|**'o** | Беззнаковое восьмеричное |
|**'d** | Беззнаковое десятичное |
|**'h** | Беззнаковое шестнадцатеричное |
|**'sb** | Знаковое двоичное |
|**'so** | Знаковое восьмеричное |
|**'sd** | Знаковое десятичное |
|**'sh** | Знаковое шестнадцатеричное |
Третья часть содержит беззнаковое число, записанное цифрами, разрешенными для данной системы исчисления. Шестнадцатеричные цифры a-f не чувствительны к регистру. Кроме того, в качестве цифры могут использоваться символы `x`, `z` и `?`(аналогичен `z`) для обозначения неопределенного/высокоимпендансного состояния соответствующих им бит.
Если размер беззнакового числа в третьей части меньше размера, указанного в первой части, это число дополняется слева нулями за исключением случаев, когда в старшем бите числа записано `x` или `z`, в этом случае, число дополняется слева `x` или `z` соответственно.
Если размер беззнакового числа в третьей части больше размера, указанного в первой части, это число обрезается слева.
Константы, для которых не указан размер будут иметь размер минимум 32 бита. Отрицательные числа представляются в дополнительном коде.
При записи числа в любом его месте кроме начала может использоваться символ нижнего подчеркивания (`_`). Этот символ будет игнорироваться и используется как разделитель для повышения читаемости.
Пример 1 — Беззнаковые константы:
|фрагмент кода|описание|
|-------------|--------|
|659 | знаковое десятичное число|
|'h 837FF | беззнаковое шестнадцатеричное число|
|'o7460 | беззнаковое восьмеричное число|
|4af | запрещенная запись (шестнадцатеричная запись возможна лишь с 'h)|
<br>
Пример 2 — Знаковые константы:
|фрагмент кода|описание|
|-------------|--------|
|4'b1001 | 4-битное двоичное беззнаковое число|
|5 'D 3 | 5-битное десятичное беззнаковое число|
|3'b01x | 3-битное беззнаковое двоичное число, у которого неизвестен младший бит|
|12'hx | 12-битное неизвестное число|
|16'hz | 16-битное высокоимпеднансное число|
<br>
Пример 3 — Использование знака с константами:
|фрагмент кода|описание|
|-------------|--------|
|8 'd -6 | Запрещено, константа в третьей части записывается в виде беззнакового числа|
|-8 'd 6 | Здесь записано -6 в дополнительном коде (1111_1010), хранящееся в 8-ми битах.|
|4 'shf | Здесь записано 4-битное число с двоичной записью: '1111', которое интерпретируется как '-1' в десятичной записи. <br> Это эквивалент записи -4'h 1|
|-4 'sd15 | Это эквивалент записи -(-4'd 1), или двоичного '0001'|
|16'sd? | То же, что и 16'hz|
<br>
Пример 4 — Выравнивание размера:
|фрагмент кода|описание|
|-------------|--------|
|8 'h F0AA |Константа превышает указаный размер и будет обрезана до AA|
|8'sb1001 |Константа меньше указанного размера и дополняется слева нулями до "00001001"|
|13'h1z1 |Константа меньше указанного размера и дополняется слева Z до "zzzzzzzzz0001"|
<br>
Пример 5 — Использование нижнего подчеркивания:
|фрагмент кода |
|-----------------------|
|27_195_000 |
|16'b0011_0101_0001_1111|
|32 'h 12ab_f001 |
### Присваивания между различными типами
Verilog является так называемым языком со слабой типизацией, что означает, что он допускает присваивания между различными типами. Причиной тому является то, что Verilog относится к любому типу как к группе битов. Присваивая значения одного типа другому, Verilog автоматически обрезает или дополняет необходимым для присваивания количеством старших бит. Ниже приведен пример того, как Verilog справляется с присваиваниями между различными типами (предположим, что мы создали переменную `ABC_TB`, которая объявлена как `reg [2:0]`)
<pre>
ABC_TB = 2'b00; // ABC_TB будет присвоено 3'b000. Автоматически добавлен
// старший бит
ABC_TB = 5; // ABC_TB будет присвоено 3'b101. Целочисленное число
// размером минимум 32 бита обрезано до 3-х.
ABC_TB = 8; // ABC_TB будет присвоено 3'b000. Целочисленное число
// со значащими битами "1000" будет брезано до 3-х.
</pre>
### Проверь себя
Чаще всего используемыми типами в Verilog являются wire и reg. В чем их фундаментальное различие?
1. У них нет различий, поскольку они оба могут принимать значения 0, 1, X, Z.
2. wire является типом класса "цепь" — это значит, что значением этого сигнала постоянно управляет источник. reg является типом класса "переменная" — это значит, что он будет хранить своё значение после присваивания.
3. wire может принимать лишь значения 0 и 1, в то время как reg может принимать значения 0, 1, X, Z.
4. Они не могут подаваться на входы друг друга.
## Создание модуля в Verilog
Все устройства, описываемые языком Verilog воплощаются в виде модуля (**module**). Модули могут включать в себя создание других модулей с целью создания иерархических проектов. Начало и конец описания подобного устройства обозначаются ключевыми словами **module** и **endmodule** соответственно. На практике считается хорошим тоном размещать каждый модуль в отдельном файле с расширением `.v`.
Синтаксис объявления модуля:
<pre>
<b>module</b> <i>module_name</i> (&lt;port_list&gt; and &lt;port_definitions&gt;);
// module_items
<b>endmodule</b>
</pre>
![../.pic/Basic%20Verilog%20structures/verilog%20syntax/file_structure.png](../.pic/Basic%20Verilog%20structures/verilog%20syntax/file_structure.png)
### Объявления портов
Первое, что указывается после имени модуля — это описание его входов и выходов, или так называемых портов. Каждому порту должно быть дано имя, направление и тип.