O que "operador" significa em Verilog
Operadores pegam expressões que carregam sinais e produzem novas expressões que carregam sinais. a + b é hardware - o sintetizador transforma em um somador. a == b é hardware - constrói um comparador. Nada disso é "código que roda"; é tudo "circuitos que o operador descreve".
O menu completo de operadores é pequeno o suficiente para memorizar. Este doc cobre os operadores aritméticos, de comparação, lógicos e condicional. Bitwise and Reduction cobre os operadores em nível de bit que não têm análogos diretos em software.
Aritméticos: + - * / %
Três coisas para saber:
- Divisão é divisão inteira.
7/2é3, não3.5. Verilog não tem um tipo float (bem,real, mas é só de simulação). - Divisão e módulo são caros em hardware. Um
/4está ok (o sintetizador transforma em um shift à direita), mas um/nparanvariável em runtime constrói um divisor multi-ciclo que você quase nunca quer. - Overflow dá a volta. Somar dois valores unsigned de N bits que excedem N bits trunca para N bits. Se você quer capturar o carry, declare o resultado um bit mais largo:
wire [7:0] a, b;
wire [8:0] sum = a + b; // soma de 9 bits captura carry out
Comparação: == != < > <= >=
Os seis operadores de comparação retornam um resultado de 1 bit: 1 se verdadeiro, 0 se falso, x se algum operando tem um bit x ou z. Para lidar com o caso x/z, recorra a === e !== (os operadores de case-equality) cobertos em X e Z Values.
<= aqui é o operador de comparação (menor-ou-igual). Os mesmos caracteres também significam non-blocking assignment em um bloco procedural (q <= d). O contexto os distingue: dentro de uma expressão é uma comparação; à esquerda de um valor sendo atribuído, é a atribuição. A sobreposição é confusa mas inambígua para o parser.
Lógicos: && || !
Operadores lógicos tratam seus operandos como booleanos (qualquer coisa não-zero é verdadeiro, zero é falso) e produzem um resultado de 1 bit:
A distinção crucial para internalizar: && lógico não é & bitwise.
4'b1100 && 4'b0011 // 1 (ambos sao nao-zero, AND logico e verdadeiro)
4'b1100 & 4'b0011 // 4'b0000 (nenhuma posicao de bit e 1 em ambos)
O mesmo para || vs |. Use lógico em condições if e predicados ?:; use bitwise quando quiser que cada posição de bit faça AND/OR independentemente.
&& e || são short-circuit no sentido do C: o lado direito não é avaliado se o resultado já estiver determinado pelo lado esquerdo. Isso importa para testbenches que verificam ponteiros ou chamadas de função; raramente importa para RTL sintetizável onde tudo acontece em paralelo de qualquer forma.
O operador condicional: ?:
O ternário é seu melhor amigo para construir muxes e expressões condicionais:
O padrão cond ? a : b é um multiplexador 2-para-1 com select de 1 bit em hardware. Encadear é fine para selects de 3 e 4 vias, mas além disso uma declaração case (coberta em Case Statement) é dramaticamente mais legível.
O operador ?: também é a forma padrão de dar a um register um valor padrão que você só sobrescreve às vezes:
next_value = update ? new_data : next_value;
Regras de largura: A pegadinha do iniciante
As regras de largura do Verilog para operadores aritméticos são notoriamente surpreendentes. A versão curta:
- A largura do resultado é o máximo das larguras das entradas.
- Se o resultado é atribuído a algo mais largo, é zero-extended (ou sign-extended se signed).
- A largura de computações intermediárias também é a largura do resultado, o que significa que overflow acontece no intermediário.
A primeira multiplicação dá overflow porque as larguras dos operandos são ambas 8 bits, a largura do resultado é portanto 8 bits, e 40000 não cabe. A segunda funciona porque alargamos a explicitamente antes de multiplicar.
As técnicas de correção:
- Use o operador de concatenação
{8'b0, a}para zero-extend. - Use
$unsigned(a)ou$signed(a)para influenciar interpretação. - Faça cast de expressões intermediárias para larguras maiores quando na dúvida.
Precedência de operadores
Você não vai memorizar a tabela, mas vai se apoiar em algumas regras:
*,/,%ligam mais fortemente que+,-.- Comparação liga mais fortemente que lógicos (
&&,||). - O operador condicional
?:tem precedência muito baixa - geralmente precisa de parênteses em torno de seus ramos em expressões maiores. - Na dúvida, parêntese. O simulador não cobra por caractere.
out = (mode == 2'd0) ? a + b : a - b; // mais claro com parens
O que vem a seguir
Você cobriu cada operador que parece com seu primo de software. O próximo doc - Bitwise and Reduction - lida com os operadores que não têm análogos diretos em software: AND/OR/XOR bitwise por bit, mais as formas de reduction que colapsam um vetor inteiro em um único bit.
Perguntas frequentes
Quais operadores o Verilog suporta?
Verilog tem quatro famílias amplas de operadores: aritméticos (+, -, *, /, %), comparação (==, !=, <, >, <=, >=), lógicos (&&, ||, !) e bitwise/reduction (&, |, ^, ~, ~&, ~|, ~^). Mais operadores de shift (<<, >>, <<<, >>>), concatenação {}, replicação {N{...}} e o condicional ?:.
Qual a diferença entre && e & em Verilog?
&& é AND lógico - trata cada operando como um booleano (zero ou não-zero) e retorna um resultado de 1 bit. & é AND bitwise - pareia bits posição por posição. 4'b1100 && 4'b0011 é 1 (ambos são não-zero); 4'b1100 & 4'b0011 é 4'b0000. Use && em condições, & para operações em nível de bit.
Como funciona a divisão em Verilog?
Divisão inteira - a parte fracionária é descartada. 7 / 2 é 3, não 3.5. O operador % dá o resto: 7 % 2 é 1. Divisão por zero produz x (desconhecido) na simulação. Ambos operadores são tecnicamente sintetizáveis mas produzem hardware muito grande e lento em FPGAs e ASICs - evite-os em caminhos sintetizáveis a menos que o divisor seja uma potência de 2.
O que é o operador condicional em Verilog?
?: é o operador ternário ou condicional, copiado do C. cond ? a : b avalia para a se cond é verdadeiro (não-zero) e b caso contrário. É a construção mais usada para lógica tipo multiplexador: out = sel ? in1 : in0 constrói um mux 2-para-1. Cadeias de ?: constroem muxes mais largos, embora case geralmente seja mais claro além de duas entradas.