Visokonivojsko načrtovanje vezja predstavlja model obnašanja (behavioral), ki je najbolj podoben pisanju kode v nekem programskem jeziku. Kljub temu pa so pomembne razlike med strojno-opisnim jezikom, ki opisuje paralelne strukture vezja in običajnim programskim jezikom, ki določa zaporedje izvajanja ukazov.
Model obnašanja opisujejo proceduralni bloki v katerih zapisujemo stavke med
begin
in end
.
Ločimo dve vrsti blokov:
initial
, se izvedejo ob začetku simulacijealways
, se izvajajo v zankiPrimer generatorja periodičnega signala:
reg c;
initial begin
c = 1’b0;
end
always begin
#50 c = ~c;
end
Blok initial poskrbi, da dobi spremenljivka c ob začetku simulacije vrednost 0. Hkrati se izvede tudi blok always, ki pa ima pred prireditvenim stavkom zapisano zakasnitev, zato se negacija vrednosti izvede šele po 50 časovnih enotah.
Prireditev z operatorjem =
(blocking assignment)
znotraj proceduralnega bloka deluje tako, da je izvajanje stavkov
blokirano, dokler se ne izvede. S takšnimi stavki lahko opišemo zaporedje dogodkov.
Stavek always se izvaja v zanki, tako da dobimo na izhodu vezja periodičen signal.
Opisan primer proceduralnih blokov je lahko del simulacijske testne strukture, ni pa primeren za sintezo vezja zaradi uporabe zakasnitev. V nadaljevanju bomo prestavili nekaj primerov kode, ki omogoča sintezo kombinacijskih in sekvenčni vezij.
Kombinacijska vezja opišemo z blokom always@ in seznamom signalov, ki predstavlja
pogoj za izvedbo stavkov v bloku. Pogoj za kombinacijska vezja so vsi vhodni signali,
saj v splošnem lahko pride do spremembe izhoda ob spremembi kateregakoli vhoda. Verilog'95
zahteva seznam signalov v obliki (a or b or c)
,
Verilog 2001 pa omogoča naštevanje signalov z vejico. Primer opisa polnega seštevalnika:
module adder(
input a,
input b,
input cin,
output reg s,
output reg cout
);
always @(a, b, cin)
begin
s = a ^ b ^ cin;
cout = (a & b) | (cin & (a ^ b));
end
endmodule
Za opis kombinacijskih vezij vedno uporabljamo prireditvene stavke z operatorjem =.
Izhodi prireditvenih stavkov morajo biti deklarirani kot spremenljivke.
Namesto naštevanja vseh vhodnih signalov smemo v opisu kombinacijskih vezij uporabiti
krajši zapis z zvezdico: always @*
.
Pogojni stavek ima obliko: if (<pogoj>) stavek1; else stavek2;
, pri čemer lahko else izpustimo. Stavek je lahko tudi nov pogojni
stavek, kar je uporabno za opis zaporednih primerjav. Če naj se ob pogoju izvede več
stavkov, jih zapišemo med begin
in end
.
Primer opisa vezja, ki da na izhod maksimalno vrednost:
module max(
input [3:0] a,
input [3:0] b,
output reg [3:0] m
);
always @* begin
if (a>b) m = a;
else m = b;
end
endmodule
V opisu kombinacijskih vezij je potrebno izhod določiti ob vseh pogojih, kar pomeni za if in za else. Če izpustimo stavek else, moramo predhodno določiti privzeto vrednost signalov, sicer se bo vrednost ohranjala in bi dobili sekvenčni zapah:
always @* begin
y = 1'b0; // privzeta vrednost kadar pogoj ni izpolnjen
yn = 1'b1;
if (trig) begin
y = x;
yn = ~x;
end
end
Stavek case
uporabljamo za pogojno izvedbo stavkov ob naštetih vrednostih signala ali izraza. Uporaben je za opis dekodirnikov, naprimer
dekodirnika iz 4-bitne bcd v sedem segmentno kodo:
always @* begin
case (bcd)
4'h0: seg = 8'b11111100;
4'h1: seg = 8'b01100000;
4'h2: seg = 8'b11011010;
4'h3: seg = 8'b11110010;
4'h4: seg = 8'b01100110;
4'h5: seg = 8'b10110110;
4'h6: seg = 8'b10111110;
4'h7: seg = 8'b11100000;
4'h8: seg = 8'b11111110;
4'h9: seg = 8'b11110110;
default: seg = 8'b00000000; // ugasni segmente če ni BCD koda
endcase
end
Naštejemo lahko tudi več vrednosti, ki jih med seboj ločimo z vejico. Primer vezja za detekcijo, ali je BCD vrednost praštevilo:
always @* begin
case (bcd)
1,2,3,5,7: prime = 1'b1; // BCD je praštevilo
default : prime = 1'b0;
endcase
end
Sekvenčna vezja opišemo z blokom always@ in pogojem za fronto ure
(posedge
ali
gegedge
) v seznamu signalov.
Pogoj za naraščajočo (prvo) fronto ure posedge clk
je edini zapis v seznamu, kadar opisujemo popolnoma sinhrono vezje.
V prireditvenih stavkih za opis sekvenčnih vezij uporabimo operator
<=
(nonblocking assignment), ki ne blokira izvajanja ostalih
stavkov. Primer opisa sinhronega podatkovnega flop-flopa:
always @(posedge clk)
if (reset)
q <= 1’b0;
else
q <= d;
Podatkovni-flip flop z asinhronim reset signalom opišemo tako, da dodamo še fronto signala reset v seznam signalov. Če je signal reset aktiven ob logični 1, zapišemo pozitivno fronto, če ja aktiven ob 0 pa negativno fronto:
// D flip-flop z asinhronim resetom (pozitivna logika)
always @(posedge clk or posedge reset)
if (reset) // reset == 1
q <= 1’b0;
else
q <= d;
// D flip-flop z asinhronim resetom (negativna logika)
always @(posedge clk or negedge reset)
if (~reset) // reset == 0
q <= 1’b0;
else
q <= d;
Zaporedni prireditveni stavki z operatorjem <=
predstavljajo v vezju
registre ali flip-flope. Primer 4-bitnega zaporednega pomikalnega registra (SISO):
module siso(
input clk;
input en;
input din;
output reg dout
);
reg d1; // notranji flip-flopi
reg d2;
reg d3;
always @(posedge clk)
if (en) begin // pomikanje ob fronti ure in en==1
d1 <= din;
d2 <= d1;
d3 <= d2;
dout <= d3;
end
endmodule
Delovanje sekvenčnega avtomata predstavimo z diagramom prehajanja stanj. Poglejmo si avtomat za detekcijo pritiska tipke.Ob pritisku na tipko, gre avtomat iz mirovnega stanja v stanje impulz, ob naslednjem urinem ciklu pa v stanje čakaj, kjer ostane tako dolgo, dokler je tipka pritisnjena. Avtomat v stanju impulz generira izhodni signal, ki je dolg natanko eno urino periodo.
Za zapis stanj deklariramo 2-bitno spremenljivko, ki ji priredimo začetno stanje. Stanja bomo v opisu avtomata označevali s parametri (konstantami) v obliki zaporednih vrednosti.
module fsm(clk, t, izh);
input clk, t;
output izh;
parameter mir=0, impulz=1, cakaj=2;
reg [1:0] st = mir;
always @(posedge clk) begin
case (st)
mir:
if (t==1) st <= impulz;
impulz:
st <= cakaj;
cakaj:
if (t==0) st <= mir;
default:
st <= mir;
endcase
end
assign izh = (st==impulz) ? 1 : 0;
endmodule