Verilog – licznik i multiplekser
Język Verilog cieszy się coraz większą popularnością wśród elektroników zainteresowanych tematyką układów programowalnych. O tym jak rozpocząć przygodę z Verilogiem pisałem we wcześniejszcych wpisach (wstęp, moduł, testbench, pierwsze kroki, symulacja w ISE).
Ucząc się nowych rzeczy najlepiej przyswaja się wiedzę poprzez praktykę. W związku z tym, w tej częsci pokażę jak zaprojektować multiplekser, demuliteplekser oraz licznik w Verilogu.
Licznik
Opis licznika pojawił się już we wpisie dotyczącym symulacji układów cyfrowych opisanych w Verilogu. Teraz przyjrzymy się tej konstrukcji dokładniej.
Liczniki zliczające w górę
module cnt_up #(parameter d = 8) ( input clk, input rst, input E, output reg [d-1:0] Q ); always @(posedge clk or posedge rst) if(rst) Q <= 0; else if(E) Q <= Q + 1; endmodule
Powyższy przykład przedstawia licznik zliczający w górę. Zliczane są impulsy zegarowe (takty) w momencie, gdy sygnał zezwalający E ma stan wysoki.
Taka konstrukcja jest bardzo często wykorzystywana w praktyce. Np. aby zmierzyć długość impulsu (z dokładnościa do taktu zegarowego) wystarczy, że przypiszemy wejście na którym znajduje się mierzony impuls do sygnału zezwalającego E. Wynik pomiaru należy oczywiście odpowiednio przetworzyć (choćby wysłać do komputera portem szeregowym).
Licznik zliczający w dół z ładowaniem równoległym
module cnt_down #(parameter d = 8) ( input clk, input rst, input E, input L, input [d-1:0] R, output reg [d-1:0] Q ); always @(posedge clk or posedge rst) if(rst) Q <= 0; else if(L) Q <= R; else if(E) Q <= Q - 1; endmodule
Licznik zliczający w dół jest bardzo podobny do licznika zliczającego w górę, dlatego powyższy przykład został rozbudowany o ładowanie równoległe. Konstrukcja taka pozwala na załadowanie licznika określoną wartością pod wpływem wysokiego poziomu na linii L.
Licznik zliczający w wybranym kierunku
module cnt_updown #(parameter d = 8) ( input clk, input rst, input E, input L, input dir, input [d-1:0] R, output reg [d-1:0] Q ); always @(posedge clk or posedge rst) if(rst) Q <= 0; else if(L) Q <= R; else if(E) if(dir) Q <= Q + 1; else Q <= Q - 1; endmodule
Ten przykład pokazuje jak skonstruować licznik zliczający w wybranym kierunku (o kierunku decyduje stan linii dir) z ładowaniem równoległym.
Przedstawione powyżej przykłady nie są jedynymi mozliwymi konstrukcjami liczników, ale ukazują zasadę ich tworzenia i stosowania.
Multiplekser / demultiplekser
Multiplekser
Multiplekser można definiować na kilka sposobów. Ja najbardziej lubię sposób zwięzły, który opiszę jako drugi. Na początek sposób rozwięzły
.
module mux41( input [3:0] i_d, output reg o_d, input [1:0] i_s ); always @(i_d or i_s) case(i_s) 2'b00: o_d <= i_d[0]; 2'b01: o_d <= i_d[1]; 2'b10: o_d <= i_d[2]; 2'b11: o_d <= i_d[3]; endcase endmodule
Większe multipleksery można budować rozszerzając powyższy przykład, lub wykorzystać w tym celu hierarchiczne łączenie modułów.
A tak wygląda sposób zwięzły:
module mux41( assign o_d = i_d[s]; endmodule
Wynik identyczny j.w.
Demultilekser
module demux14( input i_d, output reg [3:0] o_d, input [1:0] i_s ); always @(i_d or i_s) case(i_s) 2'b00: o_d <= {1'b0,1'b0,1'b0,i_d}; 2'b01: o_d <= {1'b0,1'b0,i_d,1'b0}; 2'b10: o_d <= {1'b0,i_d,1'b0,1'b0}; 2'b11: o_d <= {i_d,1'b0,1'b0,1'b0}; endcase endmodule
Podsumowanie
Znajomość konstrukcji podstawowych elementów cyfrowych w języku Verilog jest niezbędna do projektowania złożonych układów. Liczniki są wykorzystywane niemal wszędzie, multipleksery również – warto więc umieć się nimi posługiwać (co wcale nie jest skomplikowane).
Kolejne podzespoły opiszę w następnych wpisach.





