Menu

Operadores Bitwise e Reduction em Verilog

Os operadores em nível de bit em Verilog - AND/OR/XOR bitwise, as formas de inversão e os operadores de reduction que colapsam um vetor inteiro em um único bit.

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

Dois papéis para o mesmo símbolo

&, |, ^ e seus parentes invertidos aparecem em duas formas distintas:

  • Forma binária (dois operandos): a & b - operação bitwise entre vetores de mesma largura.
  • Forma unária (um operando): &a - reduction através de todos os bits de a em um único bit.

O compilador as distingue contando operandos. A convenção de nomes que as pessoas usam é "os operadores bitwise" para a forma binária e "os operadores de reduction" para a forma unária.

Bitwise: Um bit por vez

O conjunto completo:

OperadorNomeBit de saída na posição N
a & bANDa[N] AND b[N]
a | bORa[N] OR b[N]
a ^ bXORa[N] XOR b[N]
a ~& bNANDNOT (a[N] AND b[N])
a ~| bNORNOT (a[N] OR b[N])
a ~^ bXNORNOT (a[N] XOR b[N])
~aNOTNOT a[N]

Note que ~&, ~|, ~^ são escritos com o til primeiro e o operador depois. São um único token; não há espaço entre eles.

Quando os dois operandos têm larguras diferentes, o mais estreito é zero-extended à esquerda para combinar. Se você quer sign-extension, use operandos signed explicitamente com $signed().

Reduction: De muitos bits para um

As formas unárias dos mesmos operadores colapsam um vetor em um único bit:

O que cada um significa:

  • &data retorna 1 se todos os bits de data são 1, caso contrário 0. Útil para checks "isso é só 1s?".
  • |data retorna 1 se qualquer bit de data é 1, caso contrário 0. Útil para checks "isso é não-zero?".
  • ^data retorna a paridade - XOR de todos os bits. 1 se contagem ímpar de 1s, 0 se par.
  • ~&data, ~|data, ~^data são os inversos dos acima.

Você verá essas formas em todo lugar em código real:

wire empty       = ~|fifo_count;       // empty se e somente se count e zero
wire all_ones    = &mask;              // todos os bits setados
wire parity_bit  = ^data;              // paridade de um byte
wire any_request = |request_vector;    // alguma coisa requisitada?

As formas de reduction são concisas e sintetizam em árvores de gates óbvias: & reduz para um gate AND multi-entrada, | para um OR multi-entrada, ^ para uma árvore XOR (que também é o gerador de paridade em hardware).

Padrões comuns

Setando e limpando bits

wire [7:0] data;
wire [7:0] mask = 8'b0000_1000;

wire [7:0] set     = data | mask;     // forca bit 3 para 1
wire [7:0] cleared = data & ~mask;    // forca bit 3 para 0
wire [7:0] toggled = data ^ mask;     // inverte bit 3
wire [7:0] tested  = data & mask;     // zero se bit 3 era 0, nao-zero caso contrario

São os mesmos idiomas de manipulação de bit que você usaria em C. Sintetizam para operações de um único gate.

Verificando se um valor é igual a todos 1s

wire is_max = &counter;       // 1 se cada bit de counter e 1

Um AND de reduction com um gate, vs escrever counter == 8'hFF (que também funcionaria; sintetizadores geralmente produzem hardware idêntico).

Gerando um bit de paridade

Detectando qualquer sinal ativo

wire any_pending = |request_vector;

Se request_vector é largo (digamos 64 requisitantes), o OR de reduction colapsa em um único sinal que você pode alimentar em um encoder de prioridade ou árbitro.

Operadores de shift

Já que estamos em operadores em nível de bit, os shifts:

  • a << N faz shift de a para a esquerda em N posições de bit, preenchendo com zeros à direita.
  • a >> N faz shift de a para a direita em N posições de bit, preenchendo com zeros à esquerda.
  • a <<< N shift aritmético à esquerda (mesmo que << para unsigned).
  • a >>> N shift aritmético à direita - preenche com o sign bit quando a é signed.

Shifts por uma potência constante são grátis em hardware (só refiação). Shifts por uma variável de runtime produzem um barrel shifter, que é maior mas ainda barato.

O que vem a seguir

Você agora viu todo operador que retorna um único valor. O próximo doc - Concatenation and Replication - cobre a sintaxe {} e {N{...}} que constrói vetores mais largos a partir de pedaços, que você vai usar constantemente ao fazer interface entre modules de diferentes larguras.

Perguntas frequentes

O que são operadores bitwise em Verilog?

Operadores bitwise combinam dois vetores de mesma largura posição por posição. a & b faz AND do bit 0 de a com o bit 0 de b, do bit 1 com o bit 1, e assim por diante, produzindo um vetor da mesma largura que as entradas. O conjunto completo é & (AND), | (OR), ^ (XOR), ~ (NOT), e as formas de inversão ~&, ~|, ~^ (NAND, NOR, XNOR).

O que é um operador de reduction em Verilog?

Um operador de reduction é uma forma unária de um operador bitwise que colapsa um vetor inteiro em um único bit. &data retorna 1 só se cada bit de data é 1. |data retorna 1 se qualquer bit é 1. ^data retorna o XOR de todos os bits - a paridade. As formas de reduction não têm operando à esquerda, só à direita.

Qual a diferença entre & e && em Verilog?

& é AND bitwise - pareia bits posição por posição e o resultado tem a mesma largura dos operandos. && é AND lógico - trata cada operando como um booleano (zero vs não-zero) e retorna um resultado de 1 bit. 4'b1100 & 4'b0011 é 4'b0000; 4'b1100 && 4'b0011 é 1.

Como calculo paridade em Verilog?

Use o operador de reduction XOR: parity = ^data. Ele faz XOR de cada bit de data junto. Para um vetor de 8 bits isso é data[7] ^ data[6] ^ ... ^ data[0]. O resultado é 1 se um número ímpar de bits está setado, 0 se par. Invertendo com ~^ dá paridade par.

O que ~ faz em Verilog?

~ é NOT bitwise - inverte cada bit do seu operando. ~4'b1100 é 4'b0011. Não confunda com ! (NOT lógico), que colapsa o operando em um booleano de bit único e inverte isso. !4'b1100 é 1'b0 (o operando é não-zero, NOT-ando o valor de verdade dá 0).

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR