Dwie role tego samego symbolu
&, |, ^ i ich odwrócone odpowiedniki występują w dwóch różnych postaciach:
- Forma dwuargumentowa (dwa argumenty):
a & b, czyli operacja bitowa między wektorami o tej samej szerokości. - Forma jednoargumentowa (jeden argument):
&a, czyli redukcja wszystkich bitówado jednego bitu.
Kompilator odróżnia je po liczbie argumentów. Przyjęło się mówić "operatory bitowe" o formie dwuargumentowej i "operatory redukcyjne" o formie jednoargumentowej.
Operacje bitowe: bit po bicie
Pełny zestaw:
| Operator | Nazwa | Bit wyniku na pozycji N |
|---|---|---|
a & b | AND | a[N] AND b[N] |
a | b | OR | a[N] OR b[N] |
a ^ b | XOR | a[N] XOR b[N] |
a ~& b | NAND | NOT (a[N] AND b[N]) |
a ~| b | NOR | NOT (a[N] OR b[N]) |
a ~^ b | XNOR | NOT (a[N] XOR b[N]) |
~a | NOT | NOT a[N] |
Zwróć uwagę, że ~&, ~|, ~^ zapisuje się z tyldą na początku i operatorem po niej. To jeden token, bez spacji w środku.
Gdy argumenty mają różne szerokości, węższy jest rozszerzany zerami z lewej strony do szerokości drugiego. Jeśli chcesz rozszerzenia ze znakiem, użyj jawnie argumentów ze znakiem przez $signed().
Redukcja: z wielu bitów w jeden
Jednoargumentowe formy tych samych operatorów zwijają wektor do jednego bitu:
Co oznacza każdy z nich:
&datazwraca1, jeśli wszystkie bitydatasą równe 1, w przeciwnym razie0. Przydaje się do sprawdzania, czy wartość składa się z samych jedynek.|datazwraca1, jeśli którykolwiek bitdatajest równy 1, w przeciwnym razie0. Przydaje się do sprawdzania, czy wartość jest niezerowa.^datazwraca parzystość, czyli XOR wszystkich bitów.1przy nieparzystej liczbie jedynek,0przy parzystej.~&data,~|data,~^datato odwrotności powyższych.
Spotkasz je wszędzie w prawdziwym kodzie:
wire empty = ~|fifo_count; // puste wtedy i tylko wtedy, gdy count to zero
wire all_ones = &mask; // wszystkie bity ustawione
wire parity_bit = ^data; // parzystość bajtu
wire any_request = |request_vector; // czy jest jakieś żądanie?
Formy redukcyjne są zwięzłe i syntezują się do oczywistych drzew bramek: & do wielowejściowej bramki AND, | do wielowejściowej bramki OR, a ^ do drzewa XOR (które w sprzęcie jest zarazem generatorem parzystości).
Typowe wzorce
Ustawianie i zerowanie bitów
wire [7:0] data;
wire [7:0] mask = 8'b0000_1000;
wire [7:0] set = data | mask; // wymusza 1 na bicie 3
wire [7:0] cleared = data & ~mask; // wymusza 0 na bicie 3
wire [7:0] toggled = data ^ mask; // odwraca bit 3
wire [7:0] tested = data & mask; // zero, jeśli bit 3 był 0, w przeciwnym razie niezero
To te same idiomy manipulacji bitami, których używa się w C. Syntezują się do operacji na pojedynczych bramkach.
Sprawdzanie, czy wartość to same jedynki
wire is_max = &counter; // 1, jeśli każdy bit counter to 1
Redukcyjne AND z jedną bramką zamiast zapisu counter == 8'hFF (który też zadziała, a syntezatory zwykle tworzą z niego identyczny sprzęt).
Generowanie bitu parzystości
Wykrywanie dowolnego aktywnego sygnału
wire any_pending = |request_vector;
Jeśli request_vector jest szeroki (na przykład 64 zgłaszających), redukcyjne OR zwija go do jednego sygnału, który można podać na koder priorytetowy albo arbiter.
Operatory przesunięcia
Skoro mowa o operatorach bitowych, oto przesunięcia:
a << Nprzesuwaaw lewo oNpozycji bitowych i wypełnia zerami z prawej strony.a >> Nprzesuwaaw prawo oNpozycji bitowych i wypełnia zerami z lewej strony.a <<< Nto arytmetyczne przesunięcie w lewo (dla wartości bez znaku działa jak<<).a >>> Nto arytmetyczne przesunięcie w prawo: wypełnia bitem znaku, gdyajest ze znakiem.
Przesunięcia o stałą liczbę pozycji są w sprzęcie darmowe (to tylko inne połączenie przewodów). Przesunięcia o wartość znaną dopiero w czasie działania tworzą barrel shifter, który jest większy, ale wciąż tani.
Co dalej
Znasz już wszystkie operatory zwracające pojedynczą wartość. Następny artykuł, Konkatenacja i replikacja, omawia składnię {} i {N{...}}, która buduje szersze wektory z kawałków. Będziesz jej używać stale przy łączeniu modułów o różnych szerokościach.
Najczęściej zadawane pytania
Czym są operatory bitowe w Verilogu?
Operatory bitowe łączą dwa wektory o tej samej szerokości pozycja po pozycji. a & b wykonuje AND bitu 0 z a z bitem 0 z b, bitu 1 z bitem 1 i tak dalej, dając wektor o szerokości wejść. Pełny zestaw to & (AND), | (OR), ^ (XOR), ~ (NOT) oraz formy odwrócone ~&, ~|, ~^ (NAND, NOR, XNOR).
Czym jest operator redukcyjny w Verilogu?
Operator redukcyjny to jednoargumentowa forma operatora bitowego, która zwija cały wektor do jednego bitu. &data zwraca 1 tylko wtedy, gdy każdy bit data jest równy 1. |data zwraca 1, jeśli którykolwiek bit jest równy 1. ^data zwraca XOR wszystkich bitów, czyli parzystość. Formy redukcyjne nie mają argumentu po lewej, tylko po prawej.
Jaka jest różnica między & a && w Verilogu?
& to bitowe AND: łączy bity w pary pozycja po pozycji, a wynik ma tę samą szerokość co argumenty. && to logiczne AND: traktuje każdy argument jako wartość logiczną (zero lub niezero) i zwraca wynik 1-bitowy. 4'b1100 & 4'b0011 daje 4'b0000, a 4'b1100 && 4'b0011 daje 1.
Jak obliczyć parzystość w Verilogu?
Użyj redukcyjnego XOR: parity = ^data. Operator wykonuje XOR na wszystkich bitach data. Dla wektora 8-bitowego to data[7] ^ data[6] ^ ... ^ data[0]. Wynik to 1, gdy ustawionych bitów jest nieparzyście wiele, i 0, gdy parzyście. Odwrócenie przez ~^ daje drugi rodzaj parzystości.
Co robi ~ w Verilogu?
~ to bitowe NOT: odwraca każdy bit argumentu. ~4'b1100 daje 4'b0011. Nie myl go z ! (logiczne NOT), które zwija argument do jednobitowej wartości logicznej i ją odwraca. !4'b1100 daje 1'b0 (argument jest niezerowy, więc zaprzeczenie jego wartości logicznej daje 0).