Menu

Verilog X e Z Values: Sinais desconhecidos e em alta impedância explicados

Sinais Verilog têm quatro valores possíveis, não dois. Aqui está o que x (desconhecido) e z (alta impedância) realmente significam em simulação e como debugá-los.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Lógica de dois estados vs quatro estados

Em software, um bit é 0 ou 1. Em Verilog, um bit pode ser um de quatro valores:

  • 0 - o wire é conduzido para baixo.
  • 1 - o wire é conduzido para cima.
  • x - o valor do wire é desconhecido. O simulador não sabe.
  • z - o wire está em alta impedância. Nada está acionando ele.

Esse modelo de quatro estados existe porque hardware tem o mesmo problema. Um wire real pode estar amarrado em baixo, amarrado em alto, indefinido (acionado por duas fontes brigando) ou flutuando (sem driver nenhum). O simulador precisa modelar os quatro para ser útil.

Como x aparece

Execute isto e olhe a saída:

a é declarado mas nunca escrito, então fica em x. Somar x + 5 produz x - qualquer aritmética com um desconhecido produz um desconhecido. A saída diz xxxx na coluna de a.

Fontes comuns de x em seus designs:

  • Um reg declarado mas nunca resetado (a maioria do Verilog sintetizável usa um reset explícito para limpá-los).
  • Uma declaração case sem default, atingida por um valor de entrada que nenhum caso cobriu.
  • Um wire que perdeu seu único driver depois de um refactor.
  • Uma cadeia if/else onde um ramo não atribui um sinal que o outro atribui (latches x se descoberto).
  • Leitura além do fim de um vetor ou array.

Propagação de X: Um pouquinho de x arruína tudo

A coisa cruel sobre x é que ele se propaga. Um bit x em um operando vira o resultado inteiro x:

Note que 0 & x é 0 (AND com 0 é sempre 0) e 1 | x é 1 (OR com 1 é sempre 1). O simulador é bit-a-bit pessimista mas ainda respeita identidades. Aritmética e comparação não são tão generosas.

É por isso que um único register não inicializado pode fazer um barramento de saída inteiro virar xxxx. Trace para trás a partir de qualquer x e você encontrará a fonte.

Como z aparece

z é o valor de um wire que ninguém está conduzindo:

Dois padrões nesse trecho:

  • floating é só declarado e nunca conduzido. Por padrão é z.
  • data_out é um tri-state deliberado. Quando enable é baixo, a saída explicitamente libera para z. É assim que um driver de barramento "solta" para outro driver poder assumir.

Em lógica interna, z quase sempre está errado. Em um pino bidirecional ou um barramento compartilhado, z está exatamente certo.

Comparando com == vs ===

O operador de igualdade regular == retorna x quando algum operando tem um bit x ou z:

=== (e seu parceiro !==) faz uma comparação estrita bit-a-bit incluindo x e z. Use-o sempre que precisar testar para ou contra x/z em um testbench. === não é sintetizável, mas dentro de um bloco initial em um testbench isso não importa.

A função de sistema $isunknown(expr) é a forma mais limpa de perguntar "essa expressão tem algum bit x ou z?" - retorna 1 se sim, 0 se não.

Usando x como um don't-care intencional

Um padrão controverso mas legítimo: 'x em um caso default de uma máquina de estado diz ao sintetizador "esse estado é inalcançável, otimize livremente":

case (state)
    IDLE:    next_state = go ? RUNNING : IDLE;
    RUNNING: next_state = done ? IDLE : RUNNING;
    default: next_state = 'x;   // inalcançável
endcase

O sintetizador pode usar o x para fundir estados e reduzir contagem de gates. Em simulação, se seu raciocínio estava errado e o default é alcançado, você verá x se propagar de next_state e o bug se torna visível imediatamente.

Use isso só quando você pensou cuidadosamente se o default realmente é inalcançável. Se não pensou, defina o default para um estado seguro.

Receita comum de debug

Você está olhando para uma forma de onda cheia de x. A receita:

  1. Encontre o x mais cedo. Caminhe a forma de onda para trás no tempo. O sinal mais cedo a virar x é o mais próximo da fonte.
  2. Encontre seu driver. Abra o fonte. O que atribui esse sinal? Um assign? Um bloco always?
  3. Verifique as entradas do driver. Se o lado direito do driver tem algum x, propagação está fazendo o que devia - o bug é upstream.
  4. Se o driver tem entradas limpas mas produz x, o driver está incompleto. Um case sem default, um if sem else, um register sem reset.

A maioria dos bugs de tempestade de x colapsa em um de: reset ausente, default ausente ou um submódulo que não está conectado.

O que vem a seguir

Você agora tem a história completa de tipos de dados: wire/reg, vetores, parameters, literais numéricos e o modelo lógico de quatro estados. O próximo capítulo começa a usar tudo isso para construir expressões - operadores de cada tipo, incluindo os operadores bit a bit que não fariam sentido em software.

Perguntas frequentes

O que x significa em Verilog?

x é o valor desconhecido. Um sinal que é x pode ser tanto 0 quanto 1 - o simulador não sabe. Ele aparece quando um sinal não foi conduzido, quando dois drivers entram em contenção, quando um register é lido antes do reset, ou em qualquer lugar onde comportamento indefinido se propagaria silenciosamente. Trate x como um sinal de bug; quase nunca significa o que você quer.

O que z significa em Verilog?

z é o valor de alta impedância - o wire não está sendo conduzido de jeito nenhum. É o estado legítimo de saídas tri-state (barramentos de dados, pinos bidirecionais), mas em sinais internos z geralmente é um erro significando 'nada está conectado aqui'. Sintetizadores rejeitam a maioria dos padrões z fora de output enables tri-state explícitos.

Por que minha saída Verilog está xxxx?

Quase sempre porque um sinal não está sendo conduzido por nada, ou porque uma operação propagou x de outro sinal. Caminhe para trás: qual sinal é x, o que o alimenta, o driver está ativo? Culpados comuns são casos default ausentes em declarações case, registers que não são resetados e wires que perderam seu driver depois de um refactor.

Como verifico x ou z em Verilog?

Use o operador ===, que compara bit-a-bit exatamente incluindo x e z. a === 1'bx é verdadeiro quando a é de fato x. O == regular retorna x quando qualquer operando tem um bit x, o que significa que a == 1'bx nunca é a resposta que você quer. Existe também $isunknown(a), que é um booleano arrumadinho.

Posso atribuir x como padrão em Verilog?

Sim, e é uma técnica deliberada em declarações case: default: out = 'x; diz ao sintetizador 'eu prometo que esse caso nunca acontece, otimize livremente'. O custo é que se acontecer em simulação, x se propaga e você vê o bug. Use quando você já provou que o default é inalcançável, não como forma de evitar escrever o caso.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR