Menu

Konkatenacja i replikacja w Verilogu: operatory {}

Jak sklejać sygnały za pomocą {} i kopiować wzorzec N razy za pomocą {N{...}}. To niezbędne operatory Veriloga do budowania szerszych magistral z kawałków.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Dwa małe operatory, które naprawdę się przydają

Verilog ma dwie postacie {}:

  1. Konkatenacja, {a, b, c}, skleja kilka sygnałów w jeden szerszy.
  2. Replikacja, {N{pattern}}, kopiuje wzorzec N razy.

Będziesz używać obu nieustannie. Za każdym razem, gdy magistralę trzeba podzielić, połączyć, uzupełnić albo rozszerzyć ze znakiem, odpowiedzią jest jeden z tych dwóch operatorów.

Konkatenacja: {a, b, c}

Najprostszy przypadek to ułożenie dwóch sygnałów w jeden:

Czytając od lewej do prawej: a to starsza połowa, a b młodsza. Pierwszy argument wewnątrz {} jest najbardziej znaczący. Szerokość wyniku to suma szerokości argumentów.

Możesz skleić dowolną liczbę argumentów:

wire [31:0] word = {byte3, byte2, byte1, byte0};

A argumenty mogą mieć różne szerokości:

wire [11:0] frame = {start_bit, data_byte, parity, stop_bit};
//                    1 bit      8 bits     1 bit   2 bits  = 12

Częsta reguła: wszystkie argumenty konkatenacji muszą mieć jawnie podaną szerokość. Literały bez rozmiaru (1 zamiast 1'b1) powodują błędy, bo parser nie wie, ile bitów przydzielić. Zawsze pisz 1'b0, 1'b1, 4'd0, nigdy samo 0 albo 1 wewnątrz {}.

Konkatenacja po lewej stronie

Konkatenacja po lewej stronie przypisania dzieli szeroki sygnał z powrotem na części:

To kanoniczny wzorzec sumatora z przeniesieniem. {carry_out, sum_bits} to jeden 9-bitowy cel po lewej stronie przypisania, a bity są rozdzielane: najstarszy trafia do carry_out, a 8 młodszych do sum_bits.

Konkatenacja po lewej stronie działa w assign i w blokach proceduralnych:

always @(posedge clk) begin
    {high_byte, low_byte} <= incoming_word;
end

Replikacja: {N{pattern}}

Operator replikacji kopiuje wzorzec określoną liczbę razy. Ma postać {N{pattern}}: najpierw liczba, potem wzorzec we własnej parze nawiasów:

N musi być stałą, bo kompilator musi znać szerokość wyniku na etapie elaboracji. Wzorcem może być literał, parametr albo dowolne wyrażenie o znanej szerokości.

Nie zapominaj o wewnętrznych nawiasach. {8 1'b1} to błąd składni, a {8{1'b1}} jest poprawne. Zewnętrzne {} to operator, a wewnętrzne {...} obejmuje replikowany wzorzec.

Oba naraz: rozszerzenie ze znakiem i zerami

Oba operatory naturalnie się łączą i pozwalają budować szersze wartości:

{ {8{a_negative[7]}}, a_negative } to standardowy wzorzec rozszerzenia ze znakiem. Czytaj go tak: powtórz bit znaku osiem razy, potem doklej oryginalne 8 bitów. Wynik netto to 16-bitowa reprezentacja tej samej liczby w kodzie uzupełnień do dwóch.

Przy poszerzaniu wartości bez znaku możesz polegać na przypisaniu, bo Verilog automatycznie rozszerza zerami, gdy cel jest szerszy:

wire [15:0] zext_auto = a_unsigned;   // works, top 8 bits are 0

Ale rozszerzenie ze znakiem nigdy nie zachodzi niejawnie, chyba że zarówno argument, jak i cel są zadeklarowane jako signed. Jawny idiom z replikacją jest bezpieczniejszy.

Przydatne wzorce

Budowanie maski z N jedynek

parameter N = 5;
wire [31:0] mask = { {32-N{1'b0}}, {N{1'b1}} };
// e.g. with N=5, mask = 32'h0000_001F

Odwracanie wektora

Verilog nie ma wbudowanej funkcji odwracania, ale możesz ją zapisać w miejscu za pomocą konkatenacji:

wire [3:0] forward  = 4'b1010;
wire [3:0] reversed = {forward[0], forward[1], forward[2], forward[3]};
// reversed = 4'b0101

Dla szerszych wektorów czytelniejsza jest pętla generate albo function.

Pakowanie flag w bajt statusu

wire [7:0] status = {
    error_flag,    // [7]
    overflow,      // [6]
    underflow,     // [5]
    ready,         // [4]
    busy,          // [3]
    1'b0,          // [2] reserved
    1'b0,          // [1] reserved
    valid          // [0]
};

Jednobitowe zarezerwowane pozycje mają jawną szerokość: nigdy samo 0 wewnątrz {}.

Typowe błędy

Literały bez rozmiaru wewnątrz {}. {a, 0} to błąd składni albo zero o szerokości 32 bitów. Zawsze podawaj rozmiar: {a, 1'b0}.

Brak wewnętrznych nawiasów w replikacji. {8 1'b1} się nie parsuje, a {8{1'b1}} tak.

Pomylona kolejność. {a, b} umieszcza a po stronie starszych bitów, a b po stronie młodszych. Jeśli założysz odwrotnie, gdzieś pojawi się odwrócona kolejność bajtów.

Replikacja zero razy. {0{...}} jest niedozwolone w standardowym Verilogu. SystemVerilog na to pozwala (i daje wynik o zerowej szerokości). Zwykły Verilog to odrzuci.

Co dalej

Znasz już wszystkie operatory Veriloga. Następny rozdział zmienia kierunek i zajmuje się strukturą: jak moduły łączą się ze sobą, jakie reguły dotyczą portów i jak wygląda składnia tworzenia instancji podmodułów przy budowaniu większych projektów.

Najczęściej zadawane pytania

Jak połączyć sygnały w Verilogu?

Użyj nawiasów klamrowych: {a, b, c} tworzy jeden szeroki wektor, sklejając argumenty od lewej do prawej. MSB z a staje się MSB wyniku, a LSB z c staje się LSB. Szerokość wyniku to suma szerokości argumentów. Konkatenacja działa zarówno po prawej stronie przypisania (do budowania wartości), jak i po lewej (do dzielenia wartości).

Co oznacza {N{pattern}} w Verilogu?

To operator replikacji: tworzy N sklejonych kopii pattern. {8{1'b1}} to 8-bitowa wartość z samych jedynek (8'hFF). {4{2'b10}} to 8-bitowa wartość 8'b10101010. Replikacja pozwala rozszerzać zerami, rozszerzać ze znakiem albo budować dowolny powtarzalny wzorzec bitów bez wypisywania go ręcznie.

Jak rozszerzyć sygnał ze znakiem w Verilogu?

Użyj replikacji, żeby powtórzyć bit znaku: { {24{a[7]}}, a } rozszerza 8-bitowe a do 32 bitów, powtarzając bit 7 (bit znaku) 24 razy i doklejając oryginał. Dla rozszerzenia zerami bez znaku powtórz 1'b0 albo po prostu pozwól przypisaniu zrobić to niejawnie, gdy cel jest szerszy.

Czy konkatenacji można użyć po lewej stronie przypisania?

Tak. W ten sposób przypisujesz wiele celów z jednego szerokiego źródła. {carry, sum} = a + b; umieszcza wynik dodawania w carry (najstarszy bit) i sum (reszta) w jednej instrukcji. Każdy cel zachowuje swoją szerokość, a parser odpowiednio rozdziela bity prawej strony.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ