Понимание блоков always в Verilog: синтаксис и списки чувствительности
always блок — один из процедурных блоков в Verilog. Операторы внутри always выполняются последовательно.
Синтаксис
always @ (event) [statement]
always @ (event) begin [multiple statements] end
always блок выполняется при определённом событии. Событие задаётся списком чувствительности.
Что такое список чувствительности?
Список чувствительности — выражение, определяющее, когда должен выполняться always блок. Указывается после оператора @ в скобках ( ). Может содержать один или группу сигналов, изменение которых запускает блок.
В коде ниже все операторы внутри always выполняются при каждом изменении значений сигналов a или b.
// Выполнять always блок при изменении "a" или "b"
always @ (a or b) begin [statements] end
Для чего нужен always блок?
always блок может реализовывать комбинационные или последовательные элементы. Последовательный элемент (триггер) активируется при подаче такта и сброса. Комбинационный блок — при изменении одного из входов. Эти аппаратные блоки работают параллельно, независимо друг от друга. Связи между ними определяют поток данных. Для моделирования этого поведения always блок делается непрерывным процессом, который срабатывает и выполняет действие, когда сигнал в списке чувствительности становится активным.
В примере ниже все операторы внутри always выполняются на каждом положительном фронте сигнала clk.
// Выполнять always блок на положительном фронте "clk"
always @ (posedge clk) begin [statements] end
Что если нет списка чувствительности?
always блок повторяется непрерывно на протяжении всей симуляции. Список чувствительности привносит чувство времени: когда любой сигнал в списке меняется, блок срабатывает. Если внутри always нет операторов управления временем, симуляция зависнет из-за нулевой задержки бесконечного цикла!
Пример
Пример ниже — always блок, пытающийся инвертировать значение clk. Оператор выполняется каждые 0 единиц времени. Поэтому он выполняется вечно из-за отсутствия задержки.
// always блок стартует в момент 0
// Но когда он должен повторяться?
// Нет управления временем — останется и повторяется в 0 времени. Цикл — симуляция зависнет!
always clk = ~clk;
Даже если список чувствительности пуст, должна быть какая-то задержка. Время симуляции продвигается оператором задержки внутри always как показано ниже. Теперь инверсия такта происходит каждые 10 единиц времени.
always #10 clk = ~clk;
Примечание: Явные задержки не синтезируются в логические гейты!
Поэтому реальный Verilog-код дизайна всегда требует список чувствительности.
Пример проектирования последовательного элемента
Код ниже определяет модуль tff, принимающий вход данных, такт и активный-низкий сброс. Выход инвертируется, когда d равен 1 на положительном фронте такта. always блок срабатывает либо на положительном фронте clk, либо на отрицательном фронте rstn.
Что происходит на положительном фронте такта?
- Первый
ifпроверяет значение активного-низкого сбросаrstn. - Если
rstnноль — выходqсбрасывается в 0. - Если
rstnединица — сброс не применён, следует стандартное поведение. - Если предыдущий шаг ложен:
- Проверяется значение
d, если 1 — инвертируетсяq. - Если
d= 0 — сохраняется значениеq.
module tff (input d, clk, rstn, output reg q);
always @ (posedge clk or negedge rstn) begin
if (!rstn) q <= 0;
else if (d) q <= ~q;
else q <= q;
end
endmodule
Что происходит на отрицательном фронте сброса?
- Первый
ifпроверяетrstn. На отрицательном фронте его значение 0. - Если
rstn= 0 — сброс применён, выход сбрасывается в 0. - Случай
rstn= 1 не рассматривается, так как текущее событие — отрицательный фронтrstn.
Пример проектирования комбинационного элемента
always блок также можно использовать для комбинационных блоков. Например, цифровая схема из трёх гейтов, выдающая выход на сигнале o.
Код ниже — module с четырьмя входами и одним выходом o. always блок срабатывает при изменении любого сигнала в списке чувствительности. Выходной сигнал объявлен как reg в списке портов, потому что используется в процедурном блоке. Все сигналы в процедурном блоке должны быть reg.
module combo ( input a, b, c, d, output reg o);
always @ (a or b or c or d) begin
o <= ~((a & b) | (c^d));
end
endmodule
Сигнал o становится 1, когда выражение справа истинно, 0 — когда ложно.
Нажмите для слайд-шоу с примером симуляции!
Verilog
- Освоение Verilog: полное руководство по проектированию цифровых схем
- Освоение конкатенации в Verilog: практическое руководство
- Основы Verilog: краткий обзор
- Понимание назначений в Verilog: типы, синтаксис и лучшие практики
- Блокирующие и неблокирующие назначения в Verilog: ключевые различия
- Мастерство функций в Verilog для эффективного RTL дизайна
- Понимание задач (Tasks) в Verilog: отличия от функций, синтаксис и применение
- Генератор тактового сигнала в Verilog: проектирование и реализация
- Мастерство математических функций Verilog для эффективного аппаратного дизайна
- Мастерство $timeformat в Verilog: точность и контроль отображения времени