Описание модулей в SystemVerilog
Основой цифровых схем в SystemVerilog является модуль. Модуль — это блок SystemVerilog-кода, описывающий цифровую схему какого-то устройства, например пульта телевизора:
У пульта есть входные сигналы: кнопки, нажатие на которые сообщает о нашем намерении изменить громкость или переключить канал. Кроме того, есть выходной сигнал ИК-светодиода, по которому пульт отправляет информацию телевизору.
Для создания модуля в языке SystemVerilog используются ключевые слова module и endmodule, которые определяют начало и конец модуля, обрамляя его. Можно сказать, что эти ключевые слова являются корпусом нашего устройства, отделяют его содержимое от внешнего мира.
Определим наш модуль:
module
endmodule
У всякого модуля должно быть название. Назовём его box. В круглых скобках пишутся имена портов, их направление и типы. Если модуль не имеет ни входов, ни выходов, внутри скобок ничего не пишется. После них всегда ставится точка с запятой.
module box();
endmodule
Модуль без входов и выходов (портов) — это просто коробка, которая никак не взаимодействует с внешним миром. Подключим к нему два входных сигнала a, b и один выходной q. Для объявления портов, необходимо указать направление порта (вход это или выход), и тип используемого сигнала. В рамках данного курса лабораторных работ в качестве типа и входов и выходов будет использоваться тип logic, о котором будет рассказано чуть позже.
module box(
input logic a,
input logic b,
output logic q
);
endmodule
Внутри модуля могут быть объявления сигналов, параметров, констант и т.п., о которых другой модуль не узнает. Объявим внутри модуля box провод c.
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).
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.
Такую схему можно реализовать следующим описанием:
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 нашего модуля.
module box(
input logic a,
input logic b,
output logic q
);
logic c;
assign c = a | b;
assign q = c ^ b;
endmodule
Отлично! Мы научились создавать простейшее описание модуля.
Для завершения базового представления о модулях осталось разобраться с таким понятием как вектор.
Векторы
В SystemVerilog вектором называют группу проводов или регистров, объединенных общим именем, которая может использоваться как для передачи многоразрядных чисел, так и нескольких сигналов, выполняющих общую задачу.
Синтаксис объявления вектора:
<тип> [<старший индекс>:<младший индекс>] имя_вектора
Несмотря на то, что может использоваться любой диапазон индексов (даже отрицательный), на практике стараются начинать младший индекс с нуля.
Пример:
logic [7:0] sum; // Объявляется 8-битный вектор с именем sum типа logic.
// Старший индекс равен 7, младший — 0.
Используя индекс, можно обратиться к отдельным битам вектора. С помощью диапазона индексов можно получить доступ к диапазону соответствующих битов.
| фрагмент кода | описание |
|---|---|
| sum[0]; | Обращение к младшему биту вектора sum, объявленного выше |
| sum[7:5]; | Обращение к старшим трём битам 8-битного вектора sum, объявленного выше |
| sum[5+:3]; | Обращение к трём битам, начиная со пятого (т.е. это аналог предыдущего выражения, удобно использовать, когда известен начальный бит и их количество, а конечный нужно считать через них) |
| sum[7-:3]; | Обращение к трём битам, заканчивая седьмым (т.е. это аналог предыдущего выражения, удобно использовать, когда известен конечный бит и их количество, а начальный нужно считать через них) |
Таблица 1. Способы обращения как к отдельным битам вектора, так и к диапазонам его бит.
Векторы могут быть использованы и при описании портов модуля:
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 следующим образом:
Опишем inv:
module inv(
input logic a,
output logic d
);
assign d = ~a;
endmodule
Опишем модуль top:
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.
Тогда в нашем описании добавится подключение второго модуля inv и провод c.
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
Итоги главы
- Ключевым блоком в иерархии цифровой схемы, описанной на языке SystemVerilog является модуль. Модули позволяют выносить части сложной цифровой схемы в отдельные блоки, из которых потом и будет составлена итоговая схема, что сильно упрощает разработку.
- Условно, модуль можно разделить на следующие части:
- Объявление модуля:
- Ключевые слова
module/endmoduleопределяющие границы описания модуля. - Название модуля, следующее за ключевым словом
module. Описанный модуль представляет собой отдельный тип, имя которого совпадает с названием модуля. - Указание входов и выходов (портов) модуля, идущих в круглых скобках после названия модуля. Для указания направления порта модуля используются ключевые слова
inputиoutput. После указание направления порта следует указать тип порта (в рамках данного курса типом портов всегда будетlogic), его разрядность, а затем имя.
- Ключевые слова
- Функциональное описание модуля:
- Объявление внутренних сигналов модуля (будь то проводов или регистров) с помощью ключевого слова
logic. - Создание при необходимости объектов других модулей.
- Описание функциональной связи между различными сигналами и объектами внутри описываемого модуля.
- Объявление внутренних сигналов модуля (будь то проводов или регистров) с помощью ключевого слова
- Объявление модуля:
Проверьте себя
Как, по-вашему, описать нижеприведенную схему на языке описания аппаратуры SystemVerilog?
Обратите внимание, что вход a модуля top является двухразрядным: нулевой его бит идёт на вход a модуля or, первый бит идёт на вход b модуля or.