Menu

Значения X и Z в Verilog: unknown и high-impedance сигналы

У сигналов Verilog четыре возможных значения, а не два. Что на самом деле значат x (unknown) и z (high-impedance) в симуляции, и как их отлаживать.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Two-state vs four-state логика

В софте бит - это 0 или 1. В Verilog бит может быть одним из четырёх значений:

  • 0 - wire приведён к low.
  • 1 - wire приведён к high.
  • x - значение wire unknown. Симулятор не может определить.
  • z - wire high-impedance. Ничто не приводит его в действие.

Эта four-state-модель существует, потому что у железа такая же проблема. Реальный wire может быть притянут к low, к high, undefined (приводится двумя источниками, которые борются) или плавающим (никакого driver). Симулятор должен моделировать все четыре, чтобы быть полезным.

Как появляется x

Запусти это и посмотри вывод:

a объявлен, но никогда не записан, так что он сидит в x. x + 5 даёт x - любая арифметика с unknown даёт unknown. На выходе будет aaaa иксов.

Частые источники x в твоих проектах:

  • reg, объявленный, но никогда не сброшенный (большинство синтезируемого Verilog использует явный reset, чтобы их обнулить).
  • case-оператор без default, в который попало значение, ни одной ветке не подошедшее.
  • wire, который потерял свой единственный driver после рефакторинга.
  • Цепочка if/else, где одна ветка не присваивает сигнал, который присваивает другая (latch x, если не покрыто).
  • Чтение за пределами вектора или массива.

Распространение X: один бит x ломает всё

Жестокая штука с x - он распространяется. Один бит x в операнде превращает весь результат в x:

Заметь, что 0 & x - это 0 (AND с 0 всегда даёт 0), а 1 | x - это 1 (OR с 1 всегда даёт 1). Симулятор побитово пессимистичный, но всё равно уважает identities. Арифметика и сравнение не так щедры.

Вот почему один неинициализированный регистр может превратить всю шину выхода в xxxx. Трасируй назад от любого x - найдёшь источник.

Как появляется z

z - значение wire, который никто не приводит:

Два паттерна в этом фрагменте:

  • floating просто объявлен и никем не приводится. Дефолтно z.
  • data_out - намеренный tri-state. Когда enable low, выход явно отпускается в z. Так bus driver "отпускает", чтобы другой driver мог взять.

На внутренней логике z почти всегда ошибка. На двунаправленном pin или общей шине z - ровно то, что надо.

Сравнение через == vs ===

Обычный оператор равенства == возвращает x, когда у любого операнда есть бит x или z:

=== (и его пара !==) делает строгое побитовое сравнение, включая x и z. Используй его всегда, когда нужно проверить на или против x/z в testbench. === не синтезируется, но внутри блока initial в testbench это неважно.

Системная функция $isunknown(expr) - самый чистый способ спросить "есть ли в этом выражении биты x или z?" - возвращает 1, если да, 0, если нет.

Использование x как намеренного don't-care

Контроверсиальный, но легитимный паттерн: 'x в default-ветке state machine говорит синтезатору "это состояние недостижимо, оптимизируй свободно":

case (state)
    IDLE:    next_state = go ? RUNNING : IDLE;
    RUNNING: next_state = done ? IDLE : RUNNING;
    default: next_state = 'x;   // недостижимо
endcase

Синтезатор может использовать x, чтобы слить состояния и сократить количество вентилей. В симуляции, если твои рассуждения были неверны и default всё-таки срабатывает, увидишь, как x распространяется от next_state, и баг становится виден сразу.

Используй это только когда продумал, действительно ли default недостижим. Если нет - ставь default в безопасное состояние.

Стандартный рецепт отладки

Смотришь на waveform, полный x. Рецепт:

  1. Найди самый ранний x. Иди назад по waveform во времени. Самый ранний сигнал, ставший x, ближе всего к источнику.
  2. Найди его driver. Открой исходник. Что присваивает этому сигналу? assign? Блок always?
  3. Проверь входы driver. Если RHS у driver содержит x, propagation делает своё дело - баг выше по потоку.
  4. Если у driver чистые входы, но он выдаёт x, значит driver неполный. case без default, if без else, регистр без reset.

Большинство x-storm багов сводится к одному из: пропущенный reset, пропущенный default или подмодуль, который не подключён.

Что дальше

Теперь у тебя полная история про типы данных: wire/reg, векторы, parameters, числовые литералы и four-state-логика. Следующая глава начинает использовать всё это для построения выражений - операторы всех мастей, включая bit-level операторы, которые в софте не имели бы смысла.

Часто задаваемые вопросы

Что означает x в Verilog?

x - это unknown значение. Сигнал, который равен x, может быть и 0, и 1 - симулятор не может определить. Появляется, когда сигнал не приведён, когда два driver конфликтуют, когда регистр читают до reset или везде, где undefined-поведение иначе тихо распространялось бы. Относись к x как к сигналу бага: он почти никогда не значит то, что ты хочешь.

Что означает z в Verilog?

z - это high-impedance: wire вообще не приводится. Это легитимное состояние tri-state-выходов (data buses, двунаправленных pins), но на внутренних сигналах z обычно ошибка, означающая 'тут ничего не подключено'. Синтезаторы отвергают большинство паттернов с z вне явных tri-state output enables.

Почему мой Verilog-выход xxxx?

Почти всегда потому, что сигнал ничем не приводится, или операция распространила x от другого сигнала. Иди назад: какой сигнал x, что его кормит, активен ли driver? Частые виновники - пропущенные default в case-операторах, регистры без reset и wires, которые потеряли driver после рефакторинга.

Как проверить x или z в Verilog?

Используй оператор ===, который сравнивает бит-в-бит, включая x и z. a === 1'bx истинно, когда a реально x. Обычное == возвращает x, когда у любого операнда есть бит x, так что a == 1'bx никогда не даст нужного ответа. Есть ещё $isunknown(a) - аккуратный boolean.

Можно ли присваивать x как default в Verilog?

Да, и это умышленная техника в case-операторах: default: out = 'x; говорит синтезатору 'обещаю, эта ветка никогда не срабатывает, оптимизируй свободно'. Цена - если она всё-таки сработает в симуляции, x распространится и баг проявится. Используй это, когда уже доказал, что default недостижим, а не как способ не писать ветку.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ