Układy cyfrowe w Verilog Elementy języka z przykładami wersja: 10.2009 cz.3 1
Układy sekwencyjne Układy sekwencyjne mają pamięć Układy synchroniczne najczęściej spotykane wszystkie elementy są kontrolowane (synchronizowane) globalnym sygnałem zegarowym W układzie synchronicznym dane są pobierane i zapamiętywane narastającym (zmiana z 0 na 1) bądź opadającym (zmiana z 1 na 0) zboczem sygnału zegara Podejście takie umożliwia rozłączenie elementów pamiętających od reszty logiki ułatwia proces projektowania dużych systemów cyfrowych 2
Przerzutnik, rejestr Przerzutnik typu podstawowy element pamiętający 1-bit informacji Asynchroniczny reset - sygnał reset niezależny od wejścia zeruje pamięć ane z wejścia d są zapamiętywane narastającym zboczem zegara Opcjonalne wejście zezwalające enable synchroniczne jeżeli enable=1 to możliwa zmiana pamięci, w przeciwnym wypadku wartość poprzednia Grupa przerzutników to rejestr 3
Przerzutnik, rejestr Przerzutnik typu q* 0 q 1 q d Przerzutnik typu z asynchronicznym reset reset d enable q Przerzutnik typu z asynchronicznym reset i synchronicznym enable reset d enable reset d enable reset d enable q q q Rejestr reset q* 1-0 0 0 q 0 1 q 0 d enable reset q* - 1-0 - 0 0 q - 0 1 q 0 0 q 1 0 d 4
Układy synchroniczne sygnały wejściowe logika wyliczająca stan następny state_next d q state_reg logika wyjściowa sygnały wyjściowe sygnał zegarowy Układ sekwencyjny składa się z rejestru stanu zbudowanego z przerzutników synchronizowanych tym samym sygnałem zegarowym, wyjście rejestru state_reg reprezentuje stan wewnętrzny układu ogika wyliczająca stan następny oblicza na podstawie wartości wejść i stanu wewnętrznego układu wartość state_next, która zostanie wpisana do rejestru w kolejnym cyklu zegara ogika wyjściowa oblicza wartość podawaną na wyjście na podstawie stanu wewnętrznego i wejść układu 5
Układy synchroniczne sygnały wejściowe logika wyliczająca stan następny state_next d q state_reg logika wyjściowa sygnały wyjściowe sygnał zegarowy Układ synchroniczny musi spełniać wymagania czasowe czas ustalania setup i czas trwania hold Tylko jeden element pamięciowy - rejestr stanów Wymagania czasowe określa f maks maksymalna częstotliwość sygnału zegarowego Następna poprawna wartość po okresie sygnału zegarowego t f maks = 1/(t ) = 1/(t komb + t cq + t setup ) t komb czas propagacji w bloku logiki wyliczającej stan następny, t cq opóźnienie między wejściem d a wyjściem q rejestru, t setup czas ustalania rejestru 6
Układy synchroniczne Wydzielenie z układu synchronicznego układu pamięciowego pozostała część to układ kombinacyjny czasami nieporęczne ale ułatwia uniknąć błędów (niechciana pamięć, drobne błędy logiczne) Z podziału można wyróżnić trzy typy układów: regularny układ synchroniczny licznik, rejestr przesuwający automat stanów FSM (finite state machine) logika wyliczająca stan następny nie ma cech regularności logika losowa automat stanów ze ścieżką danych FSM (FSM with data path) układ składa się z automatu (ścieżka sterująca) i regularnych układów synchronicznych (ścieżka danych) używany do opisu algorytmów na poziomie RT (register-transfer level) 7
Układy synchroniczne Wydzielona pamięć w układzie synchronicznym: przerzutnik najczęściej typu rejestr tablica rejestrów (register file) Opis pamięci w bloku always Sposób użycia przypisanie nieblokujące [nazwa_zmiennej] <= [wyrażenie]; 8
Przerzutnik typu Przerzutnik typu module d(d,, q) input d, ; output reg q; always@(posedge ) // nie ma sygnału d na liście czułości q <= d; Przerzutnik typu z asynchronicznym reset module d(d,, reset, q) input d,, reset; output reg q; always@(posedge, posedge reset) if(reset) q <= 1 b0; else q <= d; 9
Przerzutnik typu Przerzutnik typu z synchronicznym enable i asynchronicznym reset module d(d,, reset, enable, q) input d, ; output reg q; always@(posedge, posedge reset) if(reset) q <= 1 b0; else if(enable) q <= d; // brak else! 10
Przerzutnik typu Przerzutnik typu z synchronicznym enable i asynchronicznym reset schemat układu synchronicznego module d(d,, reset, enable, q) input d, ; output reg q; reg r_reg, r_next; // pamięć always@(posedge, posedge reset) if(reset) r_reg <= 1'b0; else if(enable) r_reg <= r_next; // logika wyliczająca stan następny always@* if(enable) r_next = d; else r_next = r_reg; // logika wyjściowa always@* q = r_reg; 11
Rejestr Rejestr zbudowany z przerzutników typu module rejestr(d,, reset, q); parameter n=8; input [n-1:0] d; input, reset; output reg [n-1:0] q; always@(posedge, posedge reset) if(reset) q <= 1'b0; else q <= d; 12
Tablica rejestrów module tablica_rej(, wr_en, w_addr, r_addr, w_data, r_data); parameter b=8; //# bitów słowa parameter n=2; //# bitów adresu input, wr_en; input [n-1:0] w_addr, r_addr; input [b-1:0] w_data; output [b-1:0] r_data; //tablica rejestów reg [b-1:0] array_reg [2**n-1:0]; //operacja zapisu always@(posedge ) if(wr_en) array_reg[w_addr] <= w_data; //operacja odczytu assign r_data = array_reg[r_addr]; adres n N=2 n b dane 13
Tablica rejestrów 14
Rejestr przesuwający Rejestr szeregowy przesuwający module rejestr_szer (, reset, s_in, s_out); parameter n=8; input, reset, s_in; output s_out; reg [n-1:0] r_reg; wire [n-1:0] r_next; s_in N 1 N 2... 1 0 s_out always@(posedge, posedge reset) if(reset) r_reg <= 0; else r_reg <= r_next; assign r_next = {s_in, r_reg[n-1:1]}; assign s_out = r_reg[0]; 15
Rejestr uniwersalny Rejestr uniwersalny, szeregowo-równoległy module rejestr_rown(, reset, p_in, p_out, mode); parameter n=8; input, reset; input [n-1:0] p_in; input [1:0] mode; output [n-1:0] p_out; reg [n-1:0] r_reg, r_next; always@(posedge, posedge reset) if(reset) r_reg <= 0; else r_reg <= r_next; p_in[n 1] N 1 N 2 p_in... 1 p_out p_in[0] 0 always@* case(mode) 0: r_next = r_reg; // stop 1: r_next = {p_in[n-1],r_reg[n-1:1]}; // shift right 2: r_next = {r_reg[n-2:0], p_in[0]}; // shift left default: r_next = p_in; // load endcase assign p_out = r_reg; 16
Rejestr uniwersalny Rejestr uniwersalny, szeregowo-równoległy module rejestr_rown(, reset, p_in, p_out, mode); parameter n=8; input, reset; p_in[n 1] p_in input [n-1:0] p_in; input [1:0] mode; output [n-1:0] p_out; reg [n-1:0] r_reg, r_next; N 1 N 2... 1 reset shift left stop load shift right always@(posedge, posedge reset) if(reset) r_reg <= 0; else r_reg <= r_next; always@* case(mode) 0: r_next = r_reg; // stop 1: r_next = {p_in[n-1],r_reg[n-1:1]}; // shift right 2: r_next = {r_reg[n-2:0], p_in[0]}; // shift left default: r_next = p_in; // load endcase assign p_out = r_reg; p_out p_in[0] 0 17
icznik binarny icznik binarny +1 z sygnałem max_tick module licznik(, reset, max_tick, cnt_out); parameter n=4; input, reset; output [n-1:0] cnt_out; output max_tick; reg [n-1:0] r_reg; wire [n-1:0] next_reg; always@(posedge, posedge reset) if(reset) r_reg <= 0; else r_reg <= r_next; assign r_next = r_reg + 1'b1; assign max_tick = &r_reg; // r_reg == 1..1 assign cnt_out = r_reg; 18
icznik binarny icznik binarny +1 z sygnałem max_tick module licznik(, reset, max_tick, cnt_out); parameter n=4; input, reset; output [n-1:0] cnt_out; output max_tick; reg [n-1:0] r_reg; wire [n-1:0] next_reg; always@(posedge, posedge reset) if(reset) r_reg <= 0; else r_reg <= r_next; assign r_next = r_reg + 1'b1; assign max_tick = &r_reg; // r_reg == 1..1 assign cnt_out = r_reg; 19
icznik uniwersalny icznik z funkcją:+1, -1, clear, stop, load module licznik(, reset, syn_clr, load, enable, up, max_tick, min_tick, p_in, cnt_out); parameter n=4; input, reset, syn_clr, load, enable, up; input [n-1:0] p_in; output [n-1:0] cnt_out; output max_tick, min_tick; reg [n-1:0] r_reg, r_next; always@(posedge, posedge reset) if(reset) r_reg <= 0; else r_reg <= r_next; assign max_tick = &r_reg; // 1..1 assign min_tick = ~( r_reg); // 0..0 assign cnt_out = r_reg; always@* if(syn_clr) r_next = 0; else if(load) r_next = p_in; else if(enable & up) r_next = r_reg + 1'b1; else if(enable & ~up) r_next = r_reg - 1'b1; else r_next = r_reg; syn_clr load enable up reg 1 - - - 0..0 clear 0 1 - - p_in load 0 0 1 1 reg+1 +1 0 0 1 0 reg-1-1 0 0 0 - reg stop 20
icznik uniwersalny icznik z funkcją:+1, -1, clear, stop, load module licznik(, reset, syn_clr, load, enable, up, max_tick, min_tick, p_in, cnt_out); parameter n=4; input, reset, syn_clr, load, enable, up; input [n-1:0] reset p_in; load +1-1 clear -1 output [n-1:0] cnt_out; output max_tick, min_tick; reg [n-1:0] r_reg, r_next; always@(posedge, posedge reset) if(reset) r_reg <= 0; else r_reg <= r_next; assign max_tick = &r_reg; // r_reg == 1..1 assign min_tick = ~( r_reg); // r_reg == 0..0 assign cnt_out = r_reg; 21
icznik uniwersalny icznik z funkcją:+1, -1, clear, stop, load 22
icznik modulo icznik modulo-m z funkcją +1 module licznik(, reset, max_tick, cnt_out); parameter n=3; parameter m=5; input, reset; output [n-1:0] cnt_out; output max_tick; reg [n-1:0] r_reg; wire [n-1:0] r_next; always@(posedge, posedge reset) if(reset) r_reg <= 0; else r_reg <= r_next; assign r_next = (r_reg == (m-1))? 0 : r_reg + 1'b1; assign max_tick = r_reg == (m-1); assign cnt_out = r_reg; 23
icznik modulo icznik modulo-m z funkcją +1 module licznik(, reset, max_tick, cnt_out); parameter n=3; parameter m=5; input, reset; output [n-1:0] cnt_out; output max_tick; reg [n-1:0] r_reg; wire [n-1:0] r_next; always@(posedge, posedge reset) if(reset) r_reg <= 0; else r_reg <= r_next; assign r_next = (r_reg == (m-1))? 0 : r_reg + 1'b1; assign max_tick = r_reg == (m-1); assign cnt_out = r_reg; 24
icznik modulo z funkcją log2 icznik modulo-m z funkcją +1 module licznik(, reset, max_tick, cnt_out); parameter m=5; //modulo m localparam n=log2(m); // # bitów input, reset; output [n-1:0] cnt_out; output max_tick; reg [n-1:0] r_reg; wire [n-1:0] r_next; function integer log2; input [31:0] value; for (log2=0; value>0; log2=log2+1) value = value>>1; endfunction always@(posedge, posedge reset) if(reset) r_reg <= 0; else r_reg <= r_next; Brak realizacji funkcji log2 w sprzęcie, tylko w trybie wyliczeniowym assign r_next = (r_reg == (m-1))? 0 : r_reg + 1'b1; assign max_tick = r_reg == (m-1); assign cnt_out = r_reg; 25
icznik BC icznik BC 0-999 BC: 139 = 0001 0011 1001 +1 140 = 0001 0100 0000 syn_clr enable reset d2 BC d1_tick d1 BC d0_tick d0 BC Wszystkie rejestry synchronizowane jednym sygnałem zegara Młodszy rejestr zezwala na pracę starszego rejestru Jeśli enable = 1 to +1 26
icznik BC module licznik(, reset, syn_clr, enable, d2, d1, d0); input, reset, syn_clr, enable; output [3:0] d2, d1, d0; reg [3:0] d2_reg, d1_reg, d0_reg; wire [3:0] d2_next, d1_next, d0_next; wire d2_enable, d1_enable, d0_enable; wire d1_tick, d0_tick; always@(posedge, posedge reset) if(reset) begin d2_reg <= 0; d1_reg <= 0; d0_reg <= 0; end else begin d2_reg <= d2_next; d1_reg <= d1_next; d0_reg <= d0_next; end 27
icznik BC assign d0_enable = enable; assign d0_next = (syn_clr (d0_enable && (d0_reg == 9)))? 0 : (d0_enable)? d0_reg + 1'b1 : d0_reg; assign d0_tick = (d0_reg == 9)? 1'b1 : 1'b0; assign d1_enable = enable & d0_tick; assign d1_next = (syn_clr (d1_enable && (d1_reg == 9)))? 0 : (d1_enable)? d1_reg + 1'b1 : d1_reg; assign d1_tick = (d1_reg == 9)? 1'b1 : 1'b0; assign d2_enable = enable & d0_tick & d1_tick; assign d2_next = (syn_clr (d2_enable && (d2_reg == 9)))? 0 : (d2_enable)? d2_reg + 1'b1 : d2_reg; assign d2 = d2_reg; assign d1 = d1_reg; assign d0 = d0_reg; 28
icznik BC wer.2 module licznik(, reset, syn_clr, enable, d2, d1, d0); input, reset, syn_clr, enable; output [3:0] d2, d1, d0; reg [3:0] d2_reg, d1_reg, d0_reg; reg [3:0] d2_next, d1_next, d0_next; always@(posedge, posedge reset) if(reset) begin d2_reg <= 0; d1_reg <= 0; d0_reg <= 0; end else begin d2_reg <= d2_next; d1_reg <= d1_next; d0_reg <= d0_next; end 29
icznik BC wer.2 always@* begin d0_next = d0_reg; // domyślnie: d1_next = d1_reg; // wartość d2_next = d2_reg; // poprzednia if(syn_clr) begin d0_next = 4'd0; // zerowanie d1_next = 4'd0; d2_next = 4'd0; end // if else if(enable) if(d0_reg!= 9) d0_next = d0_reg + 1'b1; else begin // XX9 d0_next = 0; if(d1_reg!= 9) d1_next = d1_reg + 1'b1; else begin // X99 d1_next = 0; if(d2_reg!= 9) d2_next = d2_reg + 1'b1; else d2_next = 0; end //else end //else end //always assign d2 = d2_reg; assign d1 = d1_reg; assign d0 = d0_reg; 30
Rejestr FIFO First In First Out Zastosowanie: blok pamięci łączący dwa systemy cyfrowe Sygnały sterujące: wr zapis do rejestru rd odczyt z rejestru (usunięcie pierwszej wartości i przesunięcie) rejestr FIFO wr rd full empty 31
Rejestr FIFO realizacja kołowa wa wskaźniki: write pointer początek kolejki, pierwszy wolny do zapisu, read pointer koniec kolejki, pierwszy do odczytu wa sygnały stanu: full nie można zapisać, empty nie można czytać Jeśli wr_ptr=rd_ptr, to rejestr fifo pusty (a., i.) lub pełny (f.) wr ptr 6 5 7 0 rd ptr wr ptr 1 2 6 5 7 4 3 4 3 4 3 wr ptr a. start fifo pusty b. 1 zapis c. 3 zapisy 6 5 6 5 7 4 0 3 1 2 rd ptr 6 5 7 4 0 0 3 rd ptr 1 2 1 2 wr ptr wr ptr rd ptr 6 5 6 5 7 7 4 0 0 3 rd ptr d. 1 odczyt e. 4 zapisy f. 1 zapis fifo pełny 7 0 1 2 wr ptr 6 5 7 0 rd ptr 1 2 wr ptr 4 3 4 3 4 3 rd ptr g. 2 odczyty h. 5 odczytów i. 1 odczyt fifo pusty 6 5 7 0 1 2 1 2 1 2 rd ptr 32 wr ptr rd ptr wr ptr
Rejestr FIFO realizacja kołowa module rejestr_fifo(, reset, rd, wr, w_data, r_data, empty, full); parameter n=3; // # bitów adresu fifo parameter b=8; // # bitów w słowie input, reset, rd, wr; input [b-1:0] w_data; output empty, full; output [b-1:0] r_data; reg [b-1:0] fifo_reg [2**n-1:0]; // rejestr fifo reg [n-1:0] wr_ptr_reg, wr_ptr_next, rd_ptr_reg, rd_ptr_next; reg empty_reg, empty_next, full_reg, full_next; always@(posedge ) if(wr_en) // zapis do fifo fifo_reg[wr_ptr_reg] <= w_data; assign r_data = fifo_reg[rd_ptr_reg]; // odczyt z fifo assign wr_en = wr & ~full_reg; // zezwolenie na zapis always@(posedge, posedge reset) if(reset) begin wr_ptr_reg <= 0; rd_ptr_reg <= 0; empty_reg <= 1'b1; full_reg <= 0; end else begin wr_ptr_reg <= wr_ptr_next; rd_ptr_reg <= rd_ptr_next; empty_reg <= empty_next; full_reg <= full_next; end always@* begin wr_ptr_next = wr_ptr_reg; // domyslne rd_ptr_next = rd_ptr_reg; // wartości, empty_next = empty_reg; // brak zapisu full_next = full_reg; // i odczytu case({wr,rd}) 2'b01: // odczyt if(~empty_reg) begin rd_ptr_next = rd_ptr_reg + 1'b1; full_next = 0; if((rd_ptr_reg + 1) == wr_ptr_reg) empty_next = 1'b1; end 2'b10: // zapis if(~full_reg) begin wr_ptr_next = wr_ptr_reg + 1'b1; empty_next = 0; if((wr_ptr_reg + 1) == rd_ptr_reg) full_next = 1'b1; end 2'b11: begin// zapis i odczyt wr_ptr_next = wr_ptr_reg + 1'b1; rd_ptr_next = rd_ptr_reg + 1'b1; end //default: // brak zapisu i odczytu endcase end //always assign empty = empty_reg; assign full = full_reg; 33
Rejestr FIFO realizacja kołowa Zapis 02 04 05 06 0A 0B 0C 0 0F 02 04 05 06 0A 0B 0C 0 0F Odczyt 34
Rejestr IFO ast In First Out Zastosowanie: rejestr stosu stack w systemach mikroprocesorowych Sygnały sterujące: push zapis do rejestru pop odczyt z rejestru (usunięcie ostatnio zapisanej wartości) push pop push pop rejestr IFO full empty 35