.. _w02-verilog: ======================================== WykĹad 2: Wprowadzenie do jÄzyka Verilog ======================================== Data: 15.10.2019 .. toctree:: .. contents:: O jÄzyku ======== Verilog jest jÄzykiem opisu sprzÄtu. Ma nastÄpujÄ ce zastosowania: 1. Opis zachowania ukĹadĂłw logicznych do celĂłw symulacji. 2. Opis zachowania ukĹadĂłw logicznych do celĂłw syntezy. 3. Tworzenie testĂłw do ukĹadĂłw logicznych. W tych trzech zastosowaniach uĹźywa siÄ róşnych podzbiorĂłw jÄzyka -- o ile symulator zaakceptuje dowolny kod Veriloga, w przypadku syntezy moĹźemy uĹźywaÄ tylko ĹciĹle okreĹlonych konstrukcji. PodzbiĂłr Veriloga akceptowany przez narzÄdzia do syntezy nazywa siÄ syntezowalnym Verilogiem. ProjektujÄ c ukĹady logiczne, uĹźywamy Veriloga nastÄpujÄ co: - piszemy naszÄ logikÄ w syntezowalnym Verilogu - uĹźywamy (niekoniecznie syntezowalnego) opisu gotowych blokĂłw logicznych dostÄpnych na ukĹadzie FPGA do celĂłw symulacji (ten opis jest dostarczany przez producenta) - piszemy testy do naszej logiki uĹźywajÄ c peĹnego jÄzyka Verilog Verilog jest jÄzykiem programowania i moĹźe byÄ uĹźywany jak zwykĹy jÄzyk programowania, gdy piszemy testy (moĹźemy nawet np. uĹźywaÄ funkcji dostÄpu do plikĂłw) lub model istniejÄ cego ukĹadu do celĂłw symulacji. Gdy jednak piszemy model, ktĂłry bÄdziemy syntezowaÄ, musimy uwaĹźaÄ na uĹźywane konstrukcje, aby nie stworzyÄ bardzo nieefektywnego sprzÄtu (na przykĹad uĹźycie w syntezowalnym kodzie operatora dzielenia prawie na pewno Ĺşle siÄ skoĹczy). WaĹźna dyrektywa =============== PiszÄ c jakikolwiek kod w Verilogu, najlepiej dodaÄ na sam poczÄ tek nastÄpujÄ cy kod:: `default_nettype none Bez tej dyrektywy, zapis do dowolnej niezdefiniowanej nazwy automatycznie utworzy sieÄ poĹÄ czeĹ o danej nazwie, powodujÄ c ciÄĹźkie w wykryciu bĹÄdy. ModuĹy ====== Kod w Verilogu skĹada siÄ z moduĹĂłw. ModuĹ jest opisem bloku logiki z ustalonym zbiorem wejĹÄ i wyjĹÄ. ModuĹy mogÄ byÄ parametryzowane (np. blok mnoĹźÄ cy w ktĂłrym moĹźemy wybraÄ szerokoĹÄ liczb w bitach). Gdy uruchamiamy symulacjÄ bÄ dĹş syntezÄ, musimy wybraÄ jeden z moduĹĂłw dostÄpnych w projekcie jako gĹĂłwny moduĹ (ktĂłry moĹźe rekurencyjnie instancjonowaÄ inne moduĹy). MoĹźemy mieÄ w projekcie wiele instancji danego moduĹu (np. dowolny duĹźy ukĹad DSP bÄdzie wymagaĹ podstawowego ukĹadu mnoĹźÄ cego w wielu kopiach). PrzykĹadowe moduĹy w Verilogu mogÄ wyglÄ daÄ tak:: `default_nettype none // PĂłĹ-sumator: dodaje dwa bity, generujÄ c bit wyjĹcia i bit przeniesienia. module half_adder( input wire a, // WejĹcie 1 input wire b, // WejĹcie 2 output wire o, // WyjĹcie gĹĂłwne output wire co // WyjĹcie przeniesienia ); // Instrukcja assign jest syntezowana w ukĹad kombinacyjny obliczajÄ cy // dane wyraĹźenie. assign o = a ^ b; assign co = a & b; endmodule // PeĹny sumator: dodaje dwa bity i bit przeniesienia, generujÄ c bit // wyjĹcia i bit przeniesienia. module full_adder( input wire a, // WejĹcie 1 input wire b, // WejĹcie 2 input wire ci, // WejĹcie przeniesienia output wire o, // WyjĹcie gĹĂłwne output wire co // WyjĹcie przeniesienia ); // Sieci wewnÄtrzne -- 1-bitowe. wire h1o; wire h1c; wire h2c; // Instancjonujemy pod-moduĹy (dwie kopie pĂłĹ-sumatora). // MoĹźemy teĹź przekazywaÄ wejĹcia/wyjĹcia pozycyjnie, ale // nie jest to zalecane. half_adder h1(.a(a), .b(b), .o(h1o), .co(h1c)); half_adder h2(.a(h1o), .b(ci), .o(o), .co(h2c)); // Instancjonujemy wbudowany prymityw (bramkÄ or) -- moĹźemy tego uĹźywaÄ // zamiennie z rĂłwnowaĹźnÄ instrukcjÄ assign. or carry_or(co, h1c, h2c); endmodule Sieci ===== SieciÄ w ukĹadzie logicznym nazywamy poĹÄ czony zbiĂłr wejĹÄ/wyjĹÄ prymitywĂłw. Sieci definiuje siÄ sĹowem ``wire`` (jak widzimy w powyĹźszym przykĹadzie) bÄ dĹş kilkoma podobnymi (ktĂłrych nie uĹźywa siÄ we wspĂłĹczesnych FPGA). MoĹźemy przekazaÄ sieÄ jako parametr do pod-moduĹu. Verilog pozwala na podĹÄ czenie kilku wyjĹÄ do jednej sieci -- taka konstrukcja nie jest bĹÄdem, jeĹli moĹźemy zapewniÄ, Ĺźe w danym momencie bÄdzie aktywne tylko jedno z nich (jest to uĹźywane np. w dwukierunkowych szynach komunikacyjnych), bÄ dĹş uĹźyjemy specjalnych typĂłw sieci ze zdefiniowanymi reguĹami rozwiÄ zywania konfliktĂłw (``wand``, ``wor`` zamiast ``wire``). Takie konstrukcje jednak nie sÄ wspierane wewnÄ trz wspĂłĹczesnych FPGA (Ĺźeby osiÄ gnÄ Ä duĹźÄ wydajnoĹÄ, kaĹźda sieÄ powinna mieÄ tylko jedno ĹşrĂłdĹo) i bÄdÄ przetĹumaczone w nieefektywnÄ logikÄ przez syntezÄ. Nie naleĹźy ich wiÄc uĹźywaÄ, z wyjÄ tkiem wyprowadzeĹ dwukierunkowych (``inout``) na zewnÄ trz FPGA. Typy danych i operatory ======================= W Verilogu istnieje jeden podstawowy typ danych, ktĂłrego moĹźemy uĹźyÄ w syntezowalnym kodzie -- wektor bitĂłw, byÄ moĹźe traktowany jako liczba bez znaku. JeĹli chcemy, moĹźemy teĹź wybraÄ traktowanie go jak liczbÄ ze znakiem:: wire a; // Jeden bit. wire [0:7] d; // 8 bitĂłw, numerowanie big-endian (najbardziej znaczÄ cy bit to 0). wire [13:0] d2; // 14 bitĂłw, numerowanie little-endian (najmniej znaczÄ cy bit to 0). wire signed [1:10] d3; // 10 bitĂłw, numerowanie big-endian liczÄ c od 1, ze znakiem. StaĹe zapisujemy nastÄpujÄ co:: wire [0:3] a; assign a = 4; // StaĹa bez szerokoĹci, automatycznie staje siÄ 4-bitowa przy zapisie. assign b = 4'd5; // 4-bitowa staĹa o wartoĹci 5 (zapis dziesiÄtny) assign b = 4'b1101; // 4-bitowa staĹa o wartoĹci 13 (zapis binarny) assign b = 4'hd; // 4-bitowa staĹa o wartoĹci 13 (zapis szesnastkowy) assign b = 27'h123_4567; // 27-bitowa staĹa szesnastkowa, znak _ jest ignorowany assign c = -5'sha; // 5-bitowa staĹa ze znakiem o wartoĹci -10, zapis szesnastkowy. Operatory sÄ doĹÄ podobne do C, ale znajdziemy teĹź wiele przydatnych dodatkĂłw: - ``a[2:3]`` -- wyciÄcie kilku bitĂłw z wektora - ``{a, b, c}`` -- konkatenacja kilku wektorĂłw - ``|a`` -- szeroki OR (wszystkie bity w wektorze sÄ ORowane w jeden bit) Bit w logice cyfrowej moĹźe mieÄ 4 wartoĹci: - 0 (nie wymaga tĹumaczenia) - 1 (j/w) - X: stan nieustalony. Powstaje sam, gdy dwa róşne wyjĹcia na jednej sieci majÄ stany 0 i 1 (prawdziwy sprzÄt nie znosi tego dobrze). MoĹźemy sami przypisaÄ X do jakiejĹ sieci -- mĂłwimy wtedy syntezie, Ĺźe nie obchodzi nas wartoĹÄ tego bitu w danej sytuacji, pozwalajÄ c na wiÄkszÄ dowolnoĹÄ w optymalizacji ukĹadu. - Z: brak wyjĹcia. PrzypisujÄ c tÄ wartoĹÄ do sieci, wyĹÄ czamy dany ukĹad wyjĹciowy, pozwalajÄ c innemu wyjĹciu kontrolowaÄ jej stan. We wspĂłĹczesnych ukĹadach FPGA powinno byÄ uĹźywane tylko do dwukierunkowych sygnaĹĂłw zewnÄtrznych. Procesy ======= Proces jest blokiem kodu w Verilogu uruchamianym zawsze, gdy zmieni siÄ stan danych sieci:: // UkĹad kombinacyjny zrealizowany przez proces -- bÄdzie wykonywane // zawsze, gdy zmieni siÄ stan ktĂłregoĹ z wejĹÄ. reg [1:0] d; always @(a, b, c) begin d = 0; if (a) d = 1; if (b) d = 2; if (c) d = 3; end GĹĂłwnym zadaniem procesĂłw w syntezowalnym kodzie jest zapis do rejestrĂłw -- rejestry sÄ definiowane sĹowem ``reg`` i sÄ efektywnie zmiennymi. MoĹźemy je czytaÄ podobnie jak sieci (i przekazywaÄ je do wejĹÄ pod-moduĹĂłw, bÄ dĹş do wĹasnych wyjĹÄ). W procesach mamy dwie instrukcje przypisania: - ``=``: stan rejestru zmienia siÄ natychmiast (nowa wartoĹÄ jest widoczna w kolejnej instrukcji) - ``<=``: stan rejestru zmienia siÄ w nastÄpnym cyklu symulacji (czytanie rejestru w kolejnej instrukcji da starÄ wartoĹÄ) W syntezowalnym kodzie naleĹźy przestrzegaÄ nastÄpujÄ cych reguĹ: - dany rejestr moĹźe byÄ zapisywany tylko przez jeden proces - dany proces moĹźe byÄ albo kombinacyjny albo tworzyÄ przerzutniki - w przypadku procesu kombinacyjnego, naleĹźy w liĹcie ``@`` wypisaÄ wszystkie sieci uĹźywane wewnÄ trz procesu do liczenia nowych wartoĹci rejestrĂłw - w przypadku procesu do przerzutnikĂłw, naleĹźy w liĹcie ``@`` uĹźyÄ tylko jednego zbocza jednego zegara i ew. asynchronicznych sygnaĹĂłw resetu oraz uĹźywaÄ *tylko* przypisania ``<=`` PrzykĹad syntezy przerzutnika jest nastÄpujÄ cy:: wire [3:0] i; // MoĹźemy zdefiniowaÄ poczÄ tkowy stan rejestru. reg [3:0] a = 0; wire clk, rst; // Prosty akumulator -- w kaĹźdym zegarze zwiÄksza licznik o wartoĹÄ i. always @(posedge clk, posedge rst) begin if rst a <= 0; else a <= a + i; end PamiÄÄ ====== RolÄ tablic w Verilogu peĹni tzw. pamiÄÄ. Tworzy siÄ jÄ uĹźywajÄ c nawiasĂłw kwadratowych po nazwie rejestru:: // PamiÄÄ 128-bajtowa (z 7-bitowymi bajtami). reg [6:0] m [127:0]; always @(posedge clk) begin if (write) m[addr] <= wrdata; rddata <= m[addr]; end Aby pamiÄÄ byĹa sensownie syntezowalna, naleĹźy uĹźywaÄ ĹciĹle okreĹlonych konstrukcji (omĂłwionych na późniejszych wykĹadach). Case ==== WewnÄ trz procesĂłw moĹźemy uĹźywaÄ instrukcji ``case`` (rĂłwnowaĹźnej ``switch`` z jÄzyka C):: wire [3:0] bits; reg err; reg [1:0] which; always @(bits) begin case (bits) 4'b0001 : begin which <= 0; err <= 0; end 4'b0010 : begin which <= 1; err <= 0; end 4'b0100 : begin which <= 2; err <= 0; end 4'b1000 : begin which <= 3; err <= 0; end default : begin which <= 4'hx; err <= 1; end endcase end CzÄsto przydaje siÄ instrukcja ``casex``, ktĂłra dziaĹa jak instrukcja ``case``, ale gdy uĹźyjemy bitu ``x`` w staĹej, bÄdzie do niego pasowaÄ zarĂłwno ``0`` jak i ``1`` (normalna instrukcja ``case`` dopasowaĹaby w tym wypadku tylko bit ``x``, co oczywiĹcie nie jest syntezowalne):: wire [3:0] bits; reg err; reg [1:0] maxbit; always @(bits) begin casex (bits) 4'b0001 : begin maxbit <= 0; err <= 0; end 4'b001x : begin maxbit <= 1; err <= 0; end 4'b01xx : begin maxbit <= 2; err <= 0; end 4'b1xxx : begin maxbit <= 3; err <= 0; end // Uruchomi siÄ tylko dla bits == 0 default : begin maxbit <= 4'hx; err <= 1; end endcase end .. warning:: UĹźywajÄ c instrukcji case do syntezy logiki kombinacyjnej, naleĹźy pamiÄtaÄ, by w kaĹźdej gaĹÄzi przypisaÄ wartoĹci do tego samego zbioru rejestrĂłw -- w przeciwnym wypadku, zsyntezujemy logikÄ z zatrzaskami, a to rzadko jest poĹźÄ dane. JeĹli nie obchodzi nas wartoĹÄ przypisana do niektĂłrych rejestrĂłw w pewnych gaĹÄziach, moĹźemy tam przypisaÄ X (jak w powyĹźszym przykĹadzie). Pozwoli to uproĹciÄ zsyntezowanny ukĹad. .. warning:: Symulatory traktujÄ x jako osobnÄ wartoĹÄ od 0 i 1. JeĹli ``bit == 1'bx``, w poniĹźszym kodzie nie wykona siÄ *Ĺźadna* gaĹÄ Ĺş ``case``:: wire bit; always @(bit) begin case (bit) 1'b0: [...] 1'b1: [...] endcase end Z tego powodu, dobrym pomysĹem jest uĹźywanie zawsze gaĹÄzi default, nawet w przypadku case, ktĂłry pokrywa "wszystkie" przypadki. Parametry i generate ==================== ModuĹy mogÄ byÄ parametryzowane:: module avg ( input wire [BITS-1:0] a, input wire [BITS-1:0] b, input wire rounddir, output wire [BITS-1:0] res ); // DomyĹlnie piszemy 24-bitowy procesor. parameter BITS = 24; // Konkatenujemy jeden bit zerowy do a, Ĺźeby otrzymaÄ (BITS+1)-bitowÄ // liczbÄ (pozostaĹe skĹadniki siÄ same rozszerzÄ ). assign res = ({1'b0,a} + b + rounddir) >> 1; endmodule [...] // UĹźycie 24-bitowego moduĹu. avg avg1(.a(a1), .b(b1), .rounddir(rounddir1), .res(res1)); // UĹźycie 8-bitowego moduĹu. avg #(.BITS(8)) avg2(.a(a2), .b(b2), .rounddir(rounddir2), .res(res2)); Ze wzglÄdu na brak enumĂłw w Verilogu (sÄ one dostÄpne dopiero w SystemVerilogu, ktĂłrego ISE niestety nie wspiera), parametrĂłw uĹźywa siÄ rĂłwnieĹź do definiowania staĹych. Czasem chcemy wygenerowaÄ wiele kopii danego ukĹadu. PomoĹźe nam w tym konstrukcja generate:: module multi_adder(a, b, ci, o, co); parameter BITS = 8; // Nie moĹźemy uĹźyÄ parametru przed deklaracjÄ -- stÄ d definicje typĂłw // po deklaracji moduĹu. input wire [BITS-1:0] a, input wire [BITS-1:0] b, input wire ci, output wire [BITS-1:0] o, output wire co wire [BITS:0] carry; assign co = carry[BITS]; assign carry[0] = ci; genvar i; generate // PÄtlÄ generate-for trzeba nazwaÄ. for (i = 0; i < BITS; i = i + 1) begin : gen_fa full_adder fa( .a(a[i]), .b(b[i]), .ci(carry[i]), .o(o[i]), .co(carry[i+1]) ); end endgenerate endmodule Analogicznie do pÄtli ``for`` moĹźna uĹźywaÄ konstrukcji ``if`` oraz ``case``. WiÄcej informacji ================= Szybki kurs Veriloga: http://www.asic-world.com/verilog/veritut.html (uwaga: w wielu miejscach uĹźywa starej skĹadni Verilog 95). Do wydrukowania i powieszenia na Ĺcianie: http://www.ece.uvic.ca/~fayez/courses/ceng465/vlogref.pdf .