Понимание операторов assign в Verilog: непрерывное присваивание сигналов
Сигналы типа wire или подобные wire-типы требуют непрерывного присваивания значения. Например, электрический провод на макетной плате: пока к одному концу подключён аккумулятор +5В, компонент на другом конце получает нужное напряжение.
В Verilog эта концепция реализуется оператором assign, где любой wire или похожий тип может непрерывно управляться значением. Значение может быть константой или выражением из группы сигналов.
Синтаксис assign
Синтаксис начинается с ключевого слова assign, за которым следует имя сигнала (одиночный или конкатенация разных сетей). Сила драйвера (drive strength) и задержка (delay) опциональны и в основном используются для моделирования потока данных, а не для синтеза в реальное железо. Выражение справа вычисляется и присваивается сети или выражению сетей слева.
assign = [drive_strength] [delay] <выражение сигналов или константа>
Значения задержки полезны для задания задержек гейтов и моделирования временного поведения реального железа, так как определяют, когда сеть получит вычисленное значение.
Правила
- Левая часть (LHS) всегда должна быть скалярной/векторной сетью или конкатенацией таких сетей, никогда — скалярным/векторным регистром.
- Правая часть (RHS) может содержать скалярные/векторные регистры и вызовы функций.
- Когда любой операнд на RHS меняет значение, LHS обновляется новым значением.
assignоператоры называются непрерывными присваиваниями и всегда активны.
Пример #1
Сеть out непрерывно управляется выражением сигналов i1 и i2 с логическим И &.
Если провода превратить в порты и синтезировать, получится RTL-схема, показанная ниже.
Оператор непрерывного присваивания можно использовать для представления комбинационных гейтов в Verilog.
Пример #2
Модуль ниже принимает два входа и использует assign для управления выходом z с part-select и множественными конкатенациями бит. Каждый случай рассматривайте как единственный код в модуле, иначе множественные assign на один сигнал сделают выход X.
module xyz (input [3:0] x, input y, output [4:0] z);
wire [1:0] a; wire b;
// Допустим x='hC, y='h1, смотрим значение z
// Case #1: 4 бита x и 1 бит y конкатенируются в 5-битную сеть -> z='b11001 (0x19)
assign z = {x, y};
// Case #2: то же, но присваиваем только 3 бита z[3:1]. Остальные 2 бита z — high-Z -> z='bZ001Z
assign z[3:1] = {x, y};
// Case #3: бит 4 z управляется константой 1. Теперь z='b1001Z (только бит 0 high-Z)
assign z[3:1] = {x, y}; assign z[4] = 1;
// Case #4: бит 3 управляется вместо этого, но два драйвера для бита 3 с одинаковым значением 0 -> нет конфликта, z='bZ001Z
assign z[3:1] = {x, y}; assign z[3] = 0;
// Case #5: бит 3 управляется значением 1, два драйвера с разными значениями (первый даёт 0, второй 1) -> бит становится X, z='bZX01Z
assign z[3:1] = {x, y}; assign z[3] = 1;
// Case #6: Частичный выбор операндов RHS: только 2 бита из x -> z='b00001 (z[4:3] = 0)
assign z = {x[1:0], y};
// Case #7: Явно присваиваем только 3 бита z, остальные не подключены -> z='bZZ001
assign z[2:0] = {x[1:0], y};
// Case #8: Та же переменная многократно: 3{y} = {y,y,y} -> z='b00111
assign z = {3{y}};
// Case #9: LHS тоже конкатенирован: a — 2 бита, b — скаляр. RHS=11001, LHS 3 бита -> первые 3 бита с LSB RHS идут в LHS: a='b00, b='b1
assign {a, b} = {x, y};
// Case #10: Меняем порядок на LHS, RHS тот же -> a='b01, b='b0
assign {b, a} = {x, y};
endmodule
Присваивание переменным reg
Недопустимо управлять или присваивать переменным типа reg через assign. Это потому, что reg способен хранить данные и не требует непрерывного управления. reg сигналы можно управлять только в процедурных блоках initial и always.
Неявное непрерывное присваивание (Implicit Continuous Assignment)
Когда assign используется для присваивания сети — это явное присваивание. Verilog позволяет присваивание при объявлении сети — неявное присваивание.
wire [1:0] a; assign a = x & y; // Явное
wire [1:0] a = x & y; // Неявное
Проектирование комбинационной логики
Рассмотрим цифровую схему из комбинационных гейтов и соответствующий Verilog-код.
Комбинационная логика требует непрерывного управления входами для поддержания выхода, в отличие от последовательных элементов (триггеры), где значение захватывается и хранится по фронту такта. Поэтому assign идеально подходит: выход o обновляется при любом изменении входов справа.
module combo ( input a, b, c, d, output o);
assign o = ~((a & b) | c ^ d);
endmodule
Аппаратная схема
После элаборации и синтеза получаем комбинационную схему, работающую так же, как моделирует assign.
Сигнал o становится 1, когда комбинационное выражение справа истинно. o становится 0, когда RHS ложно. Выход o равен X с 0ns до 10ns, потому что входы X в то же время.
Нажмите для слайд-шоу с примером симуляции!
Verilog
- Освоение оператора switch в C#: синтаксис, примеры и лучшие практики
- Оператор break в C#: как и когда использовать
- Оператор continue в C#: как эффективно пропускать итерации циклов
- Освоение конкатенации в Verilog: практическое руководство
- Понимание назначений в Verilog: типы, синтаксис и лучшие практики
- Блокирующие и неблокирующие назначения в Verilog: ключевые различия
- Мастерство функций в Verilog для эффективного RTL дизайна
- Понимание задач (Tasks) в Verilog: отличия от функций, синтаксис и применение
- Генератор тактового сигнала в Verilog: проектирование и реализация
- Мастерство математических функций Verilog для эффективного аппаратного дизайна