mirror of
https://github.com/MPSU/APS.git
synced 2025-09-15 17:20:10 +00:00
263 lines
26 KiB
Markdown
263 lines
26 KiB
Markdown
# Конструкции языка 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. Сигнал цепи постоянно управляется источником сигнала и меняет своё значение каждый раз, когда меняется значение на источнике (цепь не может хранить значение).
|
||
|
||

|
||
|
||
Рассмотрим рисунок выше. Красным цветом выделены сигналы класса "цепь". Сигнал _net\_11_ используется для соединения выхода логического элемента И со входом данных D-триггера _data\_0_. Выход логического элемента И является источником сигнала для цепи _net\_11_. Выход _data\_0_ является источником сигнала _net\_2_.
|
||
|
||
В Verilog множество типов класса цепь, но самым распространенным является тип **wire**(провод). Вы можете представить подобный сигнал, как провод, подключенный к некому источнику. Значение сигнала в любой части провода одинаково и совпадает со значением подключенного к нему источника сигнала. Если провод не подключен ни к одному источнику, он находится в высокоимпедансном состоянии (принимает значение Z).
|
||
|
||
Есть еще один тип, полностью идентичный типу **wire**, который называется **tri**. Эти типы взаимозаменяемы и разделены только для повышения читаемости кода. Тип **tri** используется, когда сигналом управляют несколько источников.
|
||
|
||

|
||
|
||
Пример сигнала, управляемого несколькими источниками.
|
||
|
||
### Класс данных "Переменная"
|
||
|
||
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>), представляющая диапазон чисел [‑2 147 483 648 : 2 147 483 647]|
|
||
|**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> (<port_list> and <port_definitions>);
|
||
// module_items
|
||
<b>endmodule</b>
|
||
</pre>
|
||
|
||

|
||
|
||
### Объявления портов
|
||
|
||
Первое, что указывается после имени модуля — это описание его входов и выходов, или так называемых портов. Каждому порту должно быть дано имя, направление и тип.
|