Files
APS/Basic Verilog structures/Modules.md
Andrei Solodovnikov ba237f6e4b Update Modules.md
2025-07-10 11:36:10 +03:00

296 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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.

# Описание модулей в SystemVerilog
Основой цифровых схем в SystemVerilog является модуль. Модуль — это блок SystemVerilog-кода, описывающий цифровую схему какого-то устройства, например пульта телевизора:
<img src="../.pic/Basic%20Verilog%20structures/modules/fig_00.svg" alt="../.pic/Basic%20Verilog%20structures/modules/fig_00.svg" width="300"/>
У пульта есть входные сигналы: кнопки, нажатие на которые сообщает о нашем намерении изменить громкость или переключить канал. Кроме того, есть выходной сигнал ИК-светодиода, по которому пульт отправляет информацию телевизору.
Для создания модуля в языке SystemVerilog используются ключевые слова `module` и `endmodule`, которые определяют начало и конец модуля, обрамляя его. Можно сказать, что эти ключевые слова являются корпусом нашего устройства, отделяют его содержимое от внешнего мира.
Определим наш модуль:
![../.pic/Basic%20Verilog%20structures/modules/fig_01.drawio.svg](../.pic/Basic%20Verilog%20structures/modules/fig_01.drawio.svg)
```Verilog
module
endmodule
```
У всякого модуля должно быть название. Назовём его `box`. В круглых скобках пишутся имена портов, их направление и типы. Если модуль не имеет ни входов, ни выходов, внутри скобок ничего не пишется. После них всегда ставится точка с запятой.
![../.pic/Basic%20Verilog%20structures/modules/fig_02.drawio.svg](../.pic/Basic%20Verilog%20structures/modules/fig_02.drawio.svg)
```Verilog
module box();
endmodule
```
Модуль без входов и выходов (портов) — это просто коробка, которая никак не взаимодействует с внешним миром. Подключим к нему два входных сигнала `a, b` и один выходной `q`. Для объявления портов, необходимо указать направление порта (вход это или выход), и тип используемого сигнала. В рамках данного курса лабораторных работ в качестве типа и входов и выходов будет использоваться тип `logic`, о котором будет рассказано чуть позже.
![../.pic/Basic%20Verilog%20structures/modules/fig_03.drawio.svg](../.pic/Basic%20Verilog%20structures/modules/fig_03.drawio.svg)
```Verilog
module box(
input logic a,
input logic b,
output logic q
);
endmodule
```
Внутри модуля могут быть объявления сигналов, параметров, констант и т.п., о которых другой модуль не узнает. Объявим внутри модуля `box` провод `c`.
![../.pic/Basic%20Verilog%20structures/modules/fig_04.drawio.svg](../.pic/Basic%20Verilog%20structures/modules/fig_04.drawio.svg)
```Verilog
module box(
input logic a,
input logic b,
output logic q
);
logic c;
endmodule
```
Для объявления провода `c` использовалось ключевое слово (тип) `logic`. Этот тип может в конечном итоге привести к созданию как ячеек памяти (регистров), так и проводов, в зависимости от того, как было описано присваивание объекту этого типа (подобно тому как стволовые клетки организма могут дифференцироваться в специализированные клетки в зависимости от ситуации). Поэтому в примере выше говорить о том, что был создан провод не совсем корректно, объект схемы `c` станет проводом, когда будет произведено подключение к этому объекту, соответствующее подключению провода.
Подключим провод `c` ко входу `a`. Для этого используется конструкция `assign c = a;`. Такая конструкция называется **непрерывным присваиванием**. Если очень сильно упростить, то непрерывное присваивание схоже со спайкой двух проводов. После подобного присваивания, провод `c` всегда будет иметь то же значение, что и `a` — как только входной сигнал `a` изменит своё значение, внутренний провод `c` также изменит своё значение (проводу `c` будет **непрерывно присваиваться** значение входа `a`).
![../.pic/Basic%20Verilog%20structures/modules/fig_05.drawio.svg](../.pic/Basic%20Verilog%20structures/modules/fig_05.drawio.svg)
```Verilog
module box(
input logic a,
input logic b,
output logic q
);
logic c;
assign c = a;
endmodule
```
> [!IMPORTANT]
> Обратите внимание, что объявление сигнала типа `logic` нельзя объединять с непрерывным присваиванием этому сигналу. Иными словами, описанный выше сигнал `c` нельзя описать одной строчкой `logic c = a`. Данное выражение не содержит синтаксической ошибки, но оно означает лишь, что в момент создания сигнала `c`, ему будет присвоено значение сигнала `a`. Дальнейшие изменения в значении сигнала `a` никак не отразятся на значении сигнала `c` — именно для этого нужен оператор непрерывного присваивания `assign`.
Стоит, однако, заметить, что аналогия со спайкой проводов имеет свои недостатки: после неё некоторые студенты начинают думать, что расположение "спаиваемых" сигналов относительно знака равно не имеет значения, однако это не так.
В непрерывном присваивании участвует две компоненты: выражение-приемник сигнала и выражение-источник сигнала. Обычно, выражением-приемником является провод (либо группа проводов). Выражение-источник сигнала может быть совершенно различным. В примере, приведенном выше, выражением-источником так же был провод, но вместо него мог использоваться и регистр, и выражение, построенное из цепочки арифметических или логических вентилей.
Важно понять, что при непрерывном присваивании слева от знака равно указывается то, **чему мы будем присваивать**, а справа от знака равно указывается то, **что мы будем присваивать**.
К примеру, мы можем присвоить проводу `с` значение выхода логического вентиля. Пусть нам нужно, чтобы к сигналу `c` был подключен результат операции `a ИЛИ b`.
![../.pic/Basic%20Verilog%20structures/modules/fig_06.drawio.svg](../.pic/Basic%20Verilog%20structures/modules/fig_06.drawio.svg)
Такую схему можно реализовать следующим описанием:
```Verilog
module box(
input logic a,
input logic b,
output logic q
);
logic c;
assign c = a | b;
endmodule
```
Пусть в схеме имеется ещё один логический вентиль - Исключающее ИЛИ. На него подаётся результат операции `a ИЛИ b`, то есть `c`, а также входной сигнал `b`. Результат операции `c Исключающее ИЛИ b` подаётся на выход `q` нашего модуля.
![../.pic/Basic%20Verilog%20structures/modules/fig_07.drawio.svg](../.pic/Basic%20Verilog%20structures/modules/fig_07.drawio.svg)
```Verilog
module box(
input logic a,
input logic b,
output logic q
);
logic c;
assign c = a | b;
assign q = c ^ b;
endmodule
```
Отлично! Мы научились создавать простейшее описание модуля.
Для завершения базового представления о модулях осталось разобраться с таким понятием как **вектор**.
## Векторы
В SystemVerilog **вектором** называют группу проводов или регистров, объединенных общим именем, которая может использоваться как для передачи многоразрядных чисел, так и нескольких сигналов, выполняющих общую задачу.
Синтаксис объявления вектора:
<pre>
<тип> [<старший индекс>:<младший индекс>] <i>имя_вектора</i>
</pre>
Несмотря на то, что может использоваться любой диапазон индексов (даже отрицательный), на практике стараются начинать младший индекс с нуля.
Пример:
<pre>
<b>logic</b> [7:0] <i>sum</i>; // Объявляется 8-битный вектор с именем sum типа logic.
// Старший индекс равен 7, младший — 0.
</pre>
Используя индекс, можно обратиться к отдельным битам вектора. С помощью диапазона индексов можно получить доступ к диапазону соответствующих битов.
|фрагмент кода|описание |
|-------------|-------------------------------------------------------------------------|
|sum[0]; | Обращение к младшему биту вектора sum, объявленного выше |
|sum[7:5]; | Обращение к старшим трём битам 8-битного вектора sum, объявленного выше |
|sum[5+:3]; | Обращение к трём битам, начиная со пятого (т.е. это аналог предыдущего выражения, удобно использовать, когда известен начальный бит и их количество, а конечный нужно считать через них) |
|sum[7-:3]; | Обращение к трём битам, заканчивая седьмым (т.е. это аналог предыдущего выражения, удобно использовать, когда известен конечный бит и их количество, а начальный нужно считать через них) |
_Таблица 1. Способы обращения как к отдельным битам вектора, так и к диапазонам его бит._
Векторы могут быть использованы и при описании портов модуля:
```Verilog
module vector_ex(
input logic [3:0] a, // У данного модуля четырехразрядный вход 'a'
output logic [7:0] b // и восьмиразрядный выход 'b'.
);
assign b[7:4] = a; // К старшим четырем битам выхода b подключен вход a
assign b[3:1] = a[2:0]; // К битам с третьего по первый выхода b подключены
// биты со второго по нулевой входа a
assign b[0] = a[3]; // к младшему биту b подключен старший бит a;
endmodule
```
## Иерархия модулей
Модули могут содержать другие модули. Реализуя модуль "Пульт ДУ" можно использовать такие цифровые схемы как "Передатчик ИК-сигнала" и "Контроллер нажатия клавиш". Обе эти цифровые схемы могут быть независимыми модулями, которые объединяются в модуле верхнего уровня.
Допустим, у нас есть модуль `inv`, который подает на выход инверсию входа, и мы хотим реализовать модуль `top`, который хочет использовать функционал модуля `inv` следующим образом:
![../.pic/Basic%20Verilog%20structures/modules/fig_08.drawio.svg](../.pic/Basic%20Verilog%20structures/modules/fig_08.drawio.svg)
Опишем `inv`:
```Verilog
module inv(
input logic a,
output logic d
);
assign d = ~a;
endmodule
```
Опишем модуль `top`:
```Verilog
module top(
input logic a,
input logic b,
output logic q
);
// создаём вспомогательный провод c
logic c;
// подключение модуля
inv invertor_1( // подключаем модуль inv и
// даём экземпляру этого модуля
// имя invertor_1
.a(a), // вход а модуля inv подключаем ко
//входу a модуля top
.d(c) // выход d модуля inv подключаем к
// проводу с модуля top
);
endmodule
```
Обратите внимание на то, как подключаются сигналы к вложенному модулю: при подключении после `.` пишется имя сигнала подключаемого модуля, затем в скобках пишется имя сигнала подключающего модуля. Для лучшего понимания, посмотрите внимательно на схеме на провод `c` и выход `d` модуля `inv`, а также на SystemVerilog-описание этой схемы.
Мы можем подключить сколько угодно экземпляров одного модуля, поэтому у каждого из экземпляра должно быть своё уникальное имя. Пусть `c` подаётся на логический вентиль И вместе со входом `b`. Результат операции И тоже пойдет на инвертор, а затем на выход `q` модуля top.
![../.pic/Basic%20Verilog%20structures/modules/fig_09.drawio.svg](../.pic/Basic%20Verilog%20structures/modules/fig_09.drawio.svg)
Тогда в нашем описании добавится подключение второго модуля `inv` и провод `c`.
```Verilog
module top(
input logic a,
input logic b,
output logic q
);
// создаём вспомогательный провод c
logic c;
// подключение модуля 1
inv invertor_1( // подключаем модуль inv и даём ему
// имя invertor_1
.a(a), // подключаем вход 'а' модуля inv ко
// входу 'a' модуля top
.d(c) // подключаем выход 'd' модуля inv к
// проводу 'с' модуля top
);
// подключение модуля 2
inv invertor_2( // подключаем модуль inv и даём ему
// имя invertor_2
.a(c & b), // на вход 'а' модуля inv подаём
// результат логической операции
// "с И b"
.d(q) // подключаем выход 'd' модуля inv
// к выходу q модуля top
);
endmodule
```
___
## Итоги главы
1. Ключевым блоком в иерархии цифровой схемы, описанной на языке SystemVerilog является **модуль**. Модули позволяют выносить части сложной цифровой схемы в отдельные блоки, из которых потом и будет составлена итоговая схема, что сильно упрощает разработку.
2. Условно, модуль можно разделить на следующие части:
1. Объявление модуля:
1. Ключевые слова `module` / `endmodule` определяющие границы описания модуля.
2. Название модуля, следующее за ключевым словом `module`. Описанный модуль представляет собой отдельный тип, имя которого совпадает с названием модуля.
3. Указание входов и выходов (портов) модуля, идущих в круглых скобках после названия модуля. Для указания направления порта модуля используются ключевые слова `input` и `output`. После указание направления порта следует указать тип порта (в рамках данного курса типом портов всегда будет `logic`), его разрядность, а затем имя.
2. Функциональное описание модуля:
1. Объявление внутренних сигналов модуля (будь то проводов или регистров) с помощью ключевого слова `logic`.
2. Создание при необходимости объектов других модулей.
3. Описание функциональной связи между различными сигналами и объектами внутри описываемого модуля.
## Проверьте себя
Как, по-вашему, описать нижеприведенную схему на языке описания аппаратуры SystemVerilog?
Обратите внимание, что вход `a` модуля `top` является двухразрядным: нулевой его бит идёт на вход `a` модуля `or`, первый бит идёт на вход `b` модуля `or`.
![../.pic/Basic%20Verilog%20structures/modules/fig_10.drawio.svg](../.pic/Basic%20Verilog%20structures/modules/fig_10.drawio.svg)