Diferență între revizuiri ale paginii „SystemVerilog”

De la WikiLabs
Jump to navigationJump to search
m (Mihai.antonescu a redenumit pagina Verilog în SystemVerilog)
 
(Nu s-au afișat 40 de versiuni intermediare efectuate de alți 4 utilizatori)
Linia 1: Linia 1:
 
== Module (sintetizabile) ==
 
== Module (sintetizabile) ==
  
Limabjul Verilog este structurat pe module. Fiecare modul reprezintă un circuit care implementează o anume funcție. Spre exemplu un modul poate reprezenta un sumator, adică un circuit care are două intrări ce specifică cei doi operanzi și o ieșire ce reprezintă rezultatul adunării. Conținutul modulului reprezintă descrierea (structurală sau comportamentală) a porților care calculeaza suma celor două intrări. Prin urmare, definiția unui modul Verilog are două părți:
+
Limbajul SystemVerilog este structurat pe module. Fiecare modul reprezintă un circuit care implementează o anume funcție. Spre exemplu un modul poate reprezenta un sumator, adică un circuit care are două intrări ce specifică cei doi operanzi și o ieșire ce reprezintă rezultatul adunării. Conținutul modulului reprezintă descrierea (structurală sau comportamentală) a porților care calculeaza suma celor două intrări. Prin urmare, definiția unui modul SystemVerilog are două părți:
 
* interfață - lista tuturor porturilor de intrare și ieșire ale circuitului, specificate prin nume și dimensiune;
 
* interfață - lista tuturor porturilor de intrare și ieșire ale circuitului, specificate prin nume și dimensiune;
 
* implementare - descrierea efectivă a circuitului care se folosește de valorile de intrare pentru a calcula valorile de ieșire;
 
* implementare - descrierea efectivă a circuitului care se folosește de valorile de intrare pentru a calcula valorile de ieșire;
  
=== Interfața modulelor Verilog ===
+
=== Interfața modulelor SystemVerilog ===
 
Interfața modulului '''Adder''' este prezentată mai jos. '''''Observație:''''' Ca și în sistemul numeral zecimal, unde suma a două numere de '''n''' cifre are nevoie de '''n + 1''' cifre (9 + 9 = 18), și în sistemul binar, suma a două numere de '''n''' biți va fi pe '''n + 1''' biți.
 
Interfața modulului '''Adder''' este prezentată mai jos. '''''Observație:''''' Ca și în sistemul numeral zecimal, unde suma a două numere de '''n''' cifre are nevoie de '''n + 1''' cifre (9 + 9 = 18), și în sistemul binar, suma a două numere de '''n''' biți va fi pe '''n + 1''' biți.
 
[[Fișier:Adder_interface.svg|thumb|Reprezentarea interfetei modulului "Adder" (black box)]]
 
[[Fișier:Adder_interface.svg|thumb|Reprezentarea interfetei modulului "Adder" (black box)]]
 
<syntaxhighlight lang="verilog">
 
<syntaxhighlight lang="verilog">
 
module Adder(
 
module Adder(
     output [4:0] out,
+
     output logic [4:0] out,
     input [3:0] in0,
+
     input logic [3:0] in0,
     input [3:0] in1
+
     input logic [3:0] in1
 
);
 
);
  
Linia 42: Linia 42:
  
 
<syntaxhighlight lang="verilog">
 
<syntaxhighlight lang="verilog">
input oneBitSignal,
+
input semnal_de_un_bit,
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Linia 57: Linia 57:
 
</syntaxhighlight>
 
</syntaxhighlight>
 
-->
 
-->
=== Implementarea modulelor Verilog ===
 
  
Implementarea modulelor Verilog se face prin blocuri. Aceste blocuri pot avea sau nu corespondent într-un ciruit fizic. Dacă toate blocurile unui modul au corespondent într-un circuit fizic, atunci modulul este sintetizabil și poate fi transformat într-un circuit fizic. Blocurile care pot genera construcții sintetizabile sunt de patru tipuri:
+
=== Implementarea modulelor SystemVerilog ===
 +
 
 +
Implementarea modulelor SystemVerilog se face prin blocuri. Aceste blocuri pot avea sau nu corespondent într-un ciruit fizic. Dacă toate blocurile unui modul au corespondent într-un circuit fizic, atunci modulul este sintetizabil și poate fi transformat într-un circuit fizic. Blocurile care pot genera construcții sintetizabile sunt de patru tipuri:
 
* blocuri '''assign'''
 
* blocuri '''assign'''
 
* blocuri '''always'''
 
* blocuri '''always'''
Linia 65: Linia 66:
 
* blocuri '''generate'''
 
* blocuri '''generate'''
  
În plus, în Verilog se pot defini fire ('''wire''') și registre ('''reg''').
+
În SystemVerilog toate semnalele sunt de tip ('''logic'''), acesta acoperind atat fire cat si registre.
  
 
Blocurile care sunt întotdeauna nesintetizabile și sunt folosite exclusiv pentru simulare:
 
Blocurile care sunt întotdeauna nesintetizabile și sunt folosite exclusiv pentru simulare:
 
* blocuri '''initial'''
 
* blocuri '''initial'''
* blocuri '''forever'''
 
  
 
'''''Observație:''''' Nu orice bloc '''assign''' sau '''always''' este sintetizabil. Există construcții valide sintactic dar care nu au corespondent în circuit. Aceste blocuri pot fi simulate dar nu pot fi utilizate pentru programarea unei plăci [[FPGA]].
 
'''''Observație:''''' Nu orice bloc '''assign''' sau '''always''' este sintetizabil. Există construcții valide sintactic dar care nu au corespondent în circuit. Aceste blocuri pot fi simulate dar nu pot fi utilizate pentru programarea unei plăci [[FPGA]].
Linia 75: Linia 75:
 
'''''Observație:''''' Ordinea blocurilor într-un modul nu contează.
 
'''''Observație:''''' Ordinea blocurilor într-un modul nu contează.
  
==== Fire (wire) și registre (reg) ====
+
==== Cuvantul cheie "logic" ====
 
 
Firele sunt utilizate pentru legături între module și pentru asignarea de rezultate parțiale în circuite combinaționale, prin urmare:
 
 
 
<div class="regula">
 
<font color="red">'''''Regulă:'''''</font> Un element de tip '''wire''' își schimbă valoarea doar în blocuri de tip '''assign''' ''sau'' ca ieșire a unui modul (niciodată ambele simultan).
 
</div>
 
 
 
Firele într-un modul Verilog se definesc în felul următor:
 
<syntaxhighlight lang="Verilog">
 
 
 
wire [3:0] fir;
 
 
 
</syntaxhighlight>
 
 
 
Registrele sunt utilizate uzual pentru implementarea ciruitelor secvențiale, și atunci ele definesc registre fizice, dar:
 
  
'''''Observație:''''' Un element de tip '''reg''' nu se translatează neapărat într-un registru fizic. Translatarea lui depinde de modul în care se utilizează.
+
In SystemVerilog, toate semnalele sunt de tip '''logic''', indiferent de natura lor. In urma sintezei semnalele vor deveni fire, latch-uri sau registre.
  
Un registru se definește în felul următor:
+
Semnalele într-un modul SystemVerilog se definesc în felul următor:
 
<syntaxhighlight lang="Verilog">
 
<syntaxhighlight lang="Verilog">
  
reg [3:0] registru;
+
logic [3:0] semnal;
  
 
</syntaxhighlight>
 
</syntaxhighlight>
  
<div class="regula">
 
<font color="red">'''''Regulă:'''''</font> Un element de tip '''reg''' își schimbă valoarea doar în blocuri de tip '''always''' sau '''initial'''.
 
</div>
 
  
 
<div class="regula">
 
<div class="regula">
<font color="red">'''''Regulă:'''''</font> Niciun element nu își poate schimba valoarea în mai mult de un bloc. Adică pentru un fir ('''wire'''), nu pot exista două blocuri '''assign''' în care acesta ia valori, iar pentru elemente '''reg''', nu există decât un bloc '''always''' sau '''initial''' în care acesta își schimbă valoarea.
+
<font color="red">'''''Regulă:'''''</font> Niciun element nu își poate schimba valoarea în mai mult de un bloc.
 
</div>
 
</div>
  
'''''Observație:''''' O intrare a unui modul este întotdeauna de tip '''wire'''. Adică declarația
 
<syntaxhighlight lang="Verilog">
 
 
    input [3:0] in0,
 
 
</syntaxhighlight>
 
 
este echivalentă cu
 
 
<syntaxhighlight lang="Verilog">
 
 
    input wire [3:0] in0,
 
 
</syntaxhighlight>
 
 
 
'''''Observație:''''' O ieșire a unui modul poate fi de tip '''wire''' sau '''reg'''. Dacă nu se specifică, ea este implict de tip '''wire'''. Adică declarația
 
<syntaxhighlight lang="Verilog">
 
 
    output [4:0] out,
 
 
</syntaxhighlight>
 
 
este echivalentă cu
 
 
<syntaxhighlight lang="Verilog">
 
 
    output wire [4:0] out,
 
 
</syntaxhighlight>
 
  
 
==== Blocuri '''assign''' ====
 
==== Blocuri '''assign''' ====
Linia 144: Linia 96:
 
[[Fișier:Adder.png|thumb|Modulul Adder implementat]]
 
[[Fișier:Adder.png|thumb|Modulul Adder implementat]]
  
'''Assign''' este un cuvănt cheie care generează [[circuite combinaționale]]. Cum sumatorul este un [[circuite combinaționale|circuit combinațional]], și ieșirea acestuia este implicit de tip wire, îl putem implementa cu un bloc '''assign''':
+
'''Assign''' este un cuvănt cheie care generează [[circuite combinaționale]]. Cum sumatorul este un [[circuite combinaționale|circuit combinațional]], îl putem implementa cu un bloc '''assign''':
 
<syntaxhighlight lang="Verilog">
 
<syntaxhighlight lang="Verilog">
 
module Adder(
 
module Adder(
     output [4:0] out,
+
     output logic [4:0] out,
     input [3:0] in0,
+
     input logic [3:0] in0,
     input [3:0] in1
+
     input logic [3:0] in1
 
);
 
);
  
Linia 167: Linia 119:
 
==== Blocuri '''always''' combinaționale ====
 
==== Blocuri '''always''' combinaționale ====
  
Întotdeauna, un bloc '''always''' e utilizat pentru a da valori unor semnale de tip '''reg'''. Un bloc '''always''' se poate traduce în [[circuite combinaționale]] sau [[circuite secvențiale]], în funcție de lista acestuia de sensitivități. Formatul general pentru un bloc '''always''' este următorul:
+
Formatul pentru un bloc '''always''' combinational este următorul:
  
 
<syntaxhighlight lang="Verilog">
 
<syntaxhighlight lang="Verilog">
  
always@(<lista_sensitivitati>) begin
+
always_comb begin
 
     //...
 
     //...
 
end
 
end
Linia 177: Linia 129:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
După cum îi spune și numele, lista de sensitivități este lista semnalelor la care este sensibil, adică de care depind registrele descrise de blocul '''always'''. Dacă în lista de sensitivități este trecut doar numele unui semnal, fără nici un alt specificator suplimentar, atunci blocul este sensibil la orice schimbare a acestui semnal. Dacă un bloc '''always''' are mai multe semnale la care acesta este sensibil, ele se despart în lista de sensitivități folosind cuvântul cheie '''or'''.  
+
Un bloc de tip '''always_comb''' este sensibil la modificare oricarui semnal din cadrul modulului unde a fost definit, iesirile comandate de acest bloc sunt recalculate la orice modificare a unui semnal de intrare.
  
'''''Observație:''''' Dacă lista de sensitivități conține doar semnale fără alți specificatori, atunci rezultatul sintezei blocului va fi un circuit combinațional.
+
În acest caz, putem reface implementarea sumatorului folosind un bloc '''always_comb''' în felul următor:
 
 
În acest caz, putem reface implementarea sumatorului folosind un bloc '''always''' în felul următor:
 
  
 
<syntaxhighlight lang="Verilog">
 
<syntaxhighlight lang="Verilog">
 
module Adder(
 
module Adder(
     output reg [4:0] out,
+
     output logic [4:0] out,
     input [3:0] in0,
+
     input logic [3:0] in0,
     input [3:0] in1
+
     input logic [3:0] in1
 
);
 
);
  
always@(in0 or in1) begin
+
always_comb begin
 
     out = in0 + in1;
 
     out = in0 + in1;
 
end
 
end
Linia 199: Linia 149:
 
==== Blocuri '''always''' secvențiale. Asignări non-blocante (non-blocking assignments) ====
 
==== Blocuri '''always''' secvențiale. Asignări non-blocante (non-blocking assignments) ====
  
[[Circuite secvențiale|Circuitele secvențiale]] sunt circuitele care sunt sincronizate de [[semnalul de ceas]]. Acest semnal este, de obicei, produs de un generator de ceas și se definește ca intrare pentru fiecare modul secvențial (în care există cel puțin un registru). Putem modifica exemplul anterior, astfel încât ieșirea modulului de sumare să fie sincronă (adică să se modifice doar pe frontul pozitiv de ceas). Astfel, în interfața modulului, apare și semnalul de ceas:
+
[[Circuite secvențiale|Circuitele secvențiale]] sunt circuitele care sunt sincronizate de [[Circuite secvențiale#Semnalul de ceas|semnalul de ceas]]. Acest semnal este, de obicei, produs de un generator de ceas și se definește ca intrare pentru fiecare modul secvențial (în care există cel puțin un registru). Putem modifica exemplul anterior, astfel încât ieșirea modulului de sumare să fie sincronă (adică să se modifice doar pe frontul pozitiv de ceas). Astfel, în interfața modulului, apare și semnalul de ceas:
  
 
<syntaxhighlight lang="Verilog">
 
<syntaxhighlight lang="Verilog">
 
module SyncedAdder(
 
module SyncedAdder(
     output reg [4:0] out,
+
     output logic [4:0] out,
     input [3:0] in0,
+
     input logic [3:0] in0,
     input [3:0] in1,
+
     input logic [3:0] in1,
     input       clock
+
     input logic clock
 
);
 
);
  
Linia 215: Linia 165:
  
 
<div class="regula">
 
<div class="regula">
<font color="red">'''''Regulă:'''''</font> Pentru descrierea circuitelor secvențiale, se folosește întotdeauna un bloc '''always'''.  
+
<font color="red">'''''Regulă:'''''</font> Pentru descrierea circuitelor secvențiale, se folosește întotdeauna un bloc '''always_ff''' sau '''always_latch'''.  
 
</div>
 
</div>
  
 
[[Fișier:SyncedAdder.png|thumb|Modulul SyncedAdder]]
 
[[Fișier:SyncedAdder.png|thumb|Modulul SyncedAdder]]
Blocul '''always''' care descrie un circuit secvențial are în lista de sensitivități numai semnalul de ceas iar el nu este sensibil la orice tranziție a ceasului ci numai la unul din fronturi (de obicei cel pozitiv). Astfel, putem implementa sumatorul sincron în felul următor:
+
Blocul '''always_ff''' care descrie un circuit secvențial are în lista de sensitivități numai semnalul de ceas iar el nu este sensibil la orice tranziție a ceasului ci numai la unul din fronturi (de obicei cel pozitiv). Astfel, putem implementa sumatorul sincron în felul următor:
  
 
<syntaxhighlight lang="Verilog">
 
<syntaxhighlight lang="Verilog">
 
module SyncedAdder(
 
module SyncedAdder(
     output reg [4:0] out,
+
     output logic [4:0] out,
     input [3:0] in0,
+
     input logic [3:0] in0,
     input [3:0] in1,
+
     input logic [3:0] in1,
     input clock
+
     input logic clock
 
);
 
);
  
always@(posedge clock) begin
+
always_ff@(posedge clock) begin
 
     out <= in0 + in1;
 
     out <= in0 + in1;
 
end
 
end
Linia 245: Linia 195:
 
);
 
);
  
reg [31:0] reg0;
+
logic [31:0] reg0;
reg [31:0] reg1;
+
logic [31:0] reg1;
  
 
//cod corect
 
//cod corect
always@(posedge clock) begin
+
always_ff@(posedge clock) begin
 
     reg0 <= reg1;
 
     reg0 <= reg1;
 
     reg1 <= reg0;
 
     reg1 <= reg0;
Linia 264: Linia 214:
 
);
 
);
  
reg [31:0] reg0;
+
logic [31:0] reg0;
reg [31:0] reg1;
+
logic [31:0] reg1;
  
 
//cod incorect
 
//cod incorect
always@(posedge clock) begin
+
always_ff@(posedge clock) begin
 
     reg0 = reg1;
 
     reg0 = reg1;
 
     reg1 = reg0;
 
     reg1 = reg0;
Linia 278: Linia 228:
  
 
<div class="regula">
 
<div class="regula">
<font color="red">'''''Regulă:'''''</font> Operatorul de asignare non-blocantă ('''<=''') se folosește doar în blocuri '''always''', '''initial''' sau '''forever'''.  
+
<font color="red">'''''Regulă:'''''</font> Operatorul de asignare non-blocantă ('''<=''') se folosește doar în blocuri '''always_ff''', '''always_latch''' sau '''initial'''.  
 
</div>
 
</div>
  
Linia 303: Linia 253:
 
     .nume_intrare_sau_iesire_1(nume_semnal_legat_la_intrare_sau_iesire_1),
 
     .nume_intrare_sau_iesire_1(nume_semnal_legat_la_intrare_sau_iesire_1),
 
     //...
 
     //...
     .nume_intrare_sau_iesire_n(nume_semnal_legat_la_intrare_sau_iesire_1),
+
     .nume_intrare_sau_iesire_n(nume_semnal_legat_la_intrare_sau_iesire_n),
 
);
 
);
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Haideți deci să vedem cum arată codul Verilog pentru modulul '''Adder4''':
+
Haideți deci să vedem cum arată codul SystemVerilog pentru modulul '''Adder4''':
 
<syntaxhighlight lang="Verilog">
 
<syntaxhighlight lang="Verilog">
 
module Adder4(
 
module Adder4(
     output [4:0] out,
+
     output logic [4:0] out,
     input [2:0] in0,
+
     input logic [2:0] in0,
     input [2:0] in1,
+
     input logic [2:0] in1,
     input [2:0] in2,
+
     input logic [2:0] in2,
     input [2:0] in3,
+
     input logic [2:0] in3
 
);
 
);
  
 
//definirea firelor de legatura
 
//definirea firelor de legatura
wire [3:0] subsum0;
+
logic [3:0] subsum0;
wire [3:0] subsum1;
+
logic [3:0] subsum1;
  
 
//instantierea modulelor
 
//instantierea modulelor
Linia 347: Linia 297:
 
<syntaxhighlight lang="Verilog">
 
<syntaxhighlight lang="Verilog">
 
module Adder4(
 
module Adder4(
     output [4:0] out,
+
     output logic [4:0] out,
     input [2:0] in0,
+
     input logic [2:0] in0,
     input [2:0] in1,
+
     input logic [2:0] in1,
     input [2:0] in2,
+
     input logic [2:0] in2,
     input [2:0] in3,
+
     input logic [2:0] in3,
 
);
 
);
  
 
//definirea firelor de legatura
 
//definirea firelor de legatura
wire [4:0] subsum0; //definim firele pe 5 biți deoarece iesirea instantelor adder0 si adder1 este pe 5 biti
+
logic [4:0] subsum0; //definim firele pe 5 biți deoarece iesirea instantelor adder0 si adder1 este pe 5 biti
wire [4:0] subsum1;
+
logic [4:0] subsum1;
  
 
//instantierea modulelor
 
//instantierea modulelor
Linia 362: Linia 312:
 
     .out(subsum0),    //iesirea numita out a instantei adder0 se leaga la firul subsum0
 
     .out(subsum0),    //iesirea numita out a instantei adder0 se leaga la firul subsum0
 
     .in0({1'b0,in0}), //la intrarea numita in0 a instantei adder0 se leaga un bit 0 concatenat cu intrarea in0 a modulului
 
     .in0({1'b0,in0}), //la intrarea numita in0 a instantei adder0 se leaga un bit 0 concatenat cu intrarea in0 a modulului
     .in1({1'b0,in0})  //la intrarea numita in1 a instantei adder0 se leaga un bit 0 concatenat cu intrarea in1 a modulului
+
     .in1({1'b0,in1})  //la intrarea numita in1 a instantei adder0 se leaga un bit 0 concatenat cu intrarea in1 a modulului
 
);
 
);
  
Linia 386: Linia 336:
 
Modulele de test sunt folosite pentru verificarea funcționalității unui alt modul sintetizabil. Figura alăturată prezintă schema bloc pentru un modul de test pentru sumatorul pe 4 biți. Un modul de test generic urmărește câteva reguli clare:
 
Modulele de test sunt folosite pentru verificarea funcționalității unui alt modul sintetizabil. Figura alăturată prezintă schema bloc pentru un modul de test pentru sumatorul pe 4 biți. Un modul de test generic urmărește câteva reguli clare:
  
* pentru fiecare intrare a modulului de testat, se definește o variabilă tip '''reg''', de aceeași dimensiune cu intrarea, folosite pentru a genera stimuli pentru circuit;
+
* pentru fiecare intrare a modulului de testat, se definește o variabilă tip '''logic''', de aceeași dimensiune cu intrarea, folosite pentru a genera stimuli pentru circuit;
* pentru fiecare ieșire a modulului de testat, se definește o variabilă tip '''wire''', de aceeași dimensiune cu ieșirea, folosite pentru verificarea comportamentului (de obicei prin afișarea de forme de undă);
+
* pentru fiecare ieșire a modulului de testat, se definește o variabilă tip '''logic''', de aceeași dimensiune cu ieșirea, folosite pentru verificarea comportamentului (de obicei prin afișarea de forme de undă);
 
* se instanțiază modulul de testat și se leagă variabilele definite anterior la intrările și ieșirile instanței;
 
* se instanțiază modulul de testat și se leagă variabilele definite anterior la intrările și ieșirile instanței;
 
* se scrie un generator de stimuli (un bloc '''initial''') în care se programeaza o secvență temporizată de tranziții pentru intrări;
 
* se scrie un generator de stimuli (un bloc '''initial''') în care se programeaza o secvență temporizată de tranziții pentru intrări;
Linia 398: Linia 348:
 
//this is the definition of variables and wires
 
//this is the definition of variables and wires
 
//for driving signals   
 
//for driving signals   
reg [3:0] in0;
+
logic [3:0] i0;
reg [3:0] in1;
+
logic [3:0] i1;
wire [4:0] out;
+
logic [4:0] o;
  
 
//this is where the stimuls is added for the module
 
//this is where the stimuls is added for the module
 
initial begin
 
initial begin
     in0 = 0;
+
     i0 = 0;
     in1 = 0;
+
     i1 = 0;
   #5 in0 = 3;
+
   #5 i0 = 3;
   #2 in0 = 2;
+
   #2 i0 = 2;
     in1 = 1;
+
     i1 = 1;
   #3 in0 = 1;
+
   #3 i0 = 1;
     in1 = 5;
+
     i1 = 5;
 
   #5 $stop();
 
   #5 $stop();
 
end
 
end
Linia 416: Linia 366:
 
//this is where the tested module is instantiated   
 
//this is where the tested module is instantiated   
 
Adder deviceUnderTest(
 
Adder deviceUnderTest(
   .out(out),
+
   .out(o),
   .in0(in0),
+
   .in0(i0),
   .in1(in1)
+
   .in1(i1)
 
);   
 
);   
 
    
 
    
Linia 424: Linia 374:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
== Sintaxa Verilog ==
+
Blocul '''initial''' este folosit pentru a da valori intrărilor modulului de testat. El iși începe execuția la momentul de timp t = 0. Fiecare linie este executată la același moment de tip, până când este întâlnit operatorul #, care așteaptă trecerea unor unități de timp. Astfel, în exemplul de mai sus, primele două linii se execută la t = 0, linia 3 se execută la t = 5, liniile 4 și 5 la t = 7, liniile 6 și 7 la t = 10 și ultima linie, la t = 15. Apelul funcției $stop() determină sfârșitul simulării.
 +
 
 +
=== Semnalul de ceas în module de test ===
 +
 
 +
Cuvântul cheie '''forever''' este folosit pentru a executa o atribuire sau apel de funcție în mod repetat, până la sfârșitul simulării. Acesta este în particular folositor pentru a genera un semnal de ceas. Secvența următoare de cod generează un semnal de ceas cu perioada de două unități de timp:
 +
<syntaxhighlight lang="Verilog">
 +
reg clock;
 +
 
 +
initial begin
 +
    clock = 0;
 +
    forever #1 clock = !clock;
 +
end
 +
</syntaxhighlight>
 +
 
 +
 
 +
<div class="regula"><font color="red">Regulă:</font>Pentru circuitele secvențiale, modulul de test va avea două blocuri '''initial'''. Primul este similar celui de mai sus, și utilizat pentru generarea semnalului de ceas. Al doilea este folosit pentru generarea celorlalte semnale de intrare și directivei de oprire a simulării. Motivul pentru această segregare este faptul că instrucțiunea '''forever''' este blocantă (adică instrucțiunile puse după ea nu se execută). Toate blocurile '''initial''' dintr-un modul de test se execută în paralel.</div>
 +
 
 +
== Sintaxa SystemVerilog ==
 +
 
 +
=== Constante ===
 +
 
 +
Constantele sunt valori numerice. Pentru fiecare valoare, se specifică numărul de biți pe care reprezentată si baza în care este scrisă. Spre exemplu '''8'b111''' este o constantă pe 8 biți a cărei valoare în binar este 111 (adică în decimal, 7). În același fel, '''16'd10''' reprezintă valoarea 10 în decimal reprezentată pe 16 biți. Este posibilă specificarea valorii fără număr de biți sau bază (în care caz baza implicită este cea decimală, și numărul de biți este aproximat de către compilator).
  
 
=== Operatori ===
 
=== Operatori ===
  
=== Blocuri condiționale ===
+
==== Operatori aritmetici ====
 +
 
 +
{| class="wikitable"
 +
|- bgcolor="#ddeeff" align="center"
 +
|'''Operație''' || '''Operator''' || '''Exemplu'''
 +
|- bgcolor="#ddffdd" align="center"
 +
|Adunare || + || a + b
 +
|- bgcolor="#ddffdd" align="center"
 +
|Scădere || - || a - b
 +
|- bgcolor="#ddffdd" align="center"
 +
|Înmulțire || * || a * b
 +
|- bgcolor="#ddffdd" align="center"
 +
|Împărțire || / || a / b
 +
|}
 +
 
 +
==== Operatori logici ====
 +
 
 +
{| class="wikitable"
 +
|- bgcolor="#ddeeff" align="center"
 +
|'''Operație''' || '''Operator''' || '''Exemplu'''
 +
|- bgcolor="#ddffdd" align="center"
 +
|Egal || == || a == b
 +
|- bgcolor="#ddffdd" align="center"
 +
|Diferit || != || a != b
 +
|- bgcolor="#ddffdd" align="center"
 +
|Mai mic || < || a < b
 +
|- bgcolor="#ddffdd" align="center"
 +
|Mai mic sau egal || <= || a <= b
 +
|- bgcolor="#ddffdd" align="center"
 +
|Mai mare || > || a > b
 +
|- bgcolor="#ddffdd" align="center"
 +
|Mai mare sau egal || >= || a >= b
 +
|}
 +
 
 +
==== Operatori binari logici pe biți ====
 +
 
 +
{| class="wikitable"
 +
|- bgcolor="#ddeeff" align="center"
 +
|'''Operație''' || '''Operator''' || '''Exemplu'''
 +
|- bgcolor="#ddffdd" align="center"
 +
|Și || & || a & b
 +
|- bgcolor="#ddffdd" align="center"
 +
|Sau || <nowiki>|</nowiki> || a <nowiki>|</nowiki> b
 +
|- bgcolor="#ddffdd" align="center"
 +
|Sau exclusiv || ^ || a ^ b
 +
|}
 +
 
 +
 
 +
==== Operatori unari logici pe biți ====
 +
 
 +
{| class="wikitable"
 +
|- bgcolor="#ddeeff" align="center"
 +
|'''Operație''' || '''Operator''' || '''Exemplu'''
 +
|- bgcolor="#ddffdd" align="center"
 +
|Negare || ~ || ~b
 +
|- bgcolor="#ddffdd" align="center"
 +
|Și pe biții unui semnal|| & || &a
 +
|- bgcolor="#ddffdd" align="center"
 +
|Sau pe biții unui semnal|| <nowiki>|</nowiki> || <nowiki>|</nowiki>a
 +
|- bgcolor="#ddffdd" align="center"
 +
|Sau exclusiv pe biții unui semnal|| ^ || ^a
 +
|}
 +
 
 +
==== Operatori de shiftare pe biți ====
 +
 
 +
{| class="wikitable"
 +
|- bgcolor="#ddeeff" align="center"
 +
|'''Operație''' || '''Operator''' || '''Exemplu'''
 +
|- bgcolor="#ddffdd" align="center"
 +
|Shiftare la stânga  || << || a << b
 +
|- bgcolor="#ddffdd" align="center"
 +
|Shiftare la dreapta  || >> || a >> b
 +
|}
 +
 
 +
==== Operatorul de acces la biți ====
 +
 
 +
Dându-se un semnal pe n biți, operatorul de access la o sub-secvență din biții semnalului este '''[m:k]''' unde m și k sunt indecșii biților doriți, cu n < m < k. Ca exemplu:
 +
 
 +
<syntaxhighlight lang="verilog">
 +
logic [31:0] un_registru;
 +
logic [7:0] un_fir;
 +
logic alt_fir;
 +
 
 +
assign un_fir = un_registru[30:23]; //firul "un_fir" ia valoarea data de secventa de biti de la 23 la 30 din registrul "un_registru"
 +
assign alt_fir = un_fir[3];        //firul "alt_fir" va lua valoarea bitului 3 din semnalul "un_fir" care este bitul 26 din semnalul "un_registru"
 +
</syntaxhighlight>
 +
 
 +
==== Operatorul de concatenare ====
 +
 
 +
Operatorul de concatenare este "{" și "}". Astfel, o serie de semnale separate prin virgulă și închise între două acolade va rezulta într-un singur semnal de dimensiune egală cu suma dimensiunilor semnalelor componente.
 +
 
 +
<syntaxhighlight lang="Verilog">
 +
logic [3:0] a;
 +
logic [4:0] b;
 +
logic c;
 +
logic [9:0] d;
 +
 
 +
//semnalul d este format din concatenarea semnalelor a,b,c
 +
//unde a este pe pozitia cea mai semnificativa si c
 +
//pe pozitia cea mai putin semnificativa
 +
assign d = {a,b,c};
 +
</syntaxhighlight>
 +
 
 +
==== Operatorul de replicare ====
 +
 
 +
Operatorul de replicare este folosit când se dorește replicarea unui semnal de un număr mare sau necunoscut de ori (dat ca parametru). Astfel, construcția '''{n{m}}''' reprezintă semnalul '''m''', multiplcat de '''n''' ori.
 +
 
 +
<syntaxhighlight lang="Verilog">
 +
logic a;
 +
logic [4:0] b;
 +
logic [7:0] c;
 +
logic [15:0] d;
 +
 
 +
assign b = {5{a}}; //b va lua valoarea data de a, replicata de 5 ori
 +
assign d = {2{c}}; //d va lua valoarea lui c (8 biti) replicata de 2 ori
 +
</syntaxhighlight>
 +
 
 +
=== Blocuri și operatori condiționali ===
 +
 
 +
Condițiile se pot exprima în trei moduri:
 +
* folosing operatorul condițional cunoscut din limbajul C (condiție '''?''' valoare_pentru_adevărat ''':''' valoare_pentru_fals);
 +
* folosind construcția '''if - else''';
 +
* folosind constructia '''case - endcase''' respectiv '''casex - endcase''' sau '''casez - endcase'''.
 +
 
 +
Operatorul condițional se poate utiliza în orice tip de bloc. Exemplu:
 +
<syntaxhighlight lang="Verilog">
 +
logic a;
 +
logic [3:0] b;
 +
logic [3:0] c;
 +
logic [3:0] d;
 +
 
 +
assign b = a ? c : d; //daca a este adevarat (adica daca este egal cu 1), atunci b ia valoarea lui c, altfel, b ia valoarea lui d
 +
</syntaxhighlight>
 +
 
 +
Blocurile '''if - else''', '''case - endcase''' se pot utiliza doar în blocuri '''always'''. Exemplu:
 +
<syntaxhighlight lang="Verilog">
 +
logic a;
 +
logic [3:0] b;
 +
logic [3:0] c;
 +
logic [3:0] d;
 +
 
 +
always_comb begin
 +
    if(a) begin
 +
        b = c;
 +
    end else begin
 +
        b = d;
 +
    end
 +
end
 +
 
 +
</syntaxhighlight>
 +
 
 +
Exemplu '''case - endcase''':
 +
<syntaxhighlight lang="Verilog">
 +
logic [1:0] a;
 +
logic  [3:0] e;
 +
logic [3:0] c;
 +
logic [3:0] d;
 +
 
 +
//daca a este egal cu 0, atunci e ia valoarea lui c
 +
//daca a este egal cu 1, atunci e ia valoarea lui d
 +
//in orice alt caz, b ia valoarea 0
 +
always_comb begin
 +
    case(a)
 +
        2'b00: e = c;
 +
        2'b01: e = d;
 +
        default: e = 3'b000;
 +
    endcase
 +
end
 +
 
 +
</syntaxhighlight>

Versiunea curentă din 22 octombrie 2024 11:03

Module (sintetizabile)

Limbajul SystemVerilog este structurat pe module. Fiecare modul reprezintă un circuit care implementează o anume funcție. Spre exemplu un modul poate reprezenta un sumator, adică un circuit care are două intrări ce specifică cei doi operanzi și o ieșire ce reprezintă rezultatul adunării. Conținutul modulului reprezintă descrierea (structurală sau comportamentală) a porților care calculeaza suma celor două intrări. Prin urmare, definiția unui modul SystemVerilog are două părți:

  • interfață - lista tuturor porturilor de intrare și ieșire ale circuitului, specificate prin nume și dimensiune;
  • implementare - descrierea efectivă a circuitului care se folosește de valorile de intrare pentru a calcula valorile de ieșire;

Interfața modulelor SystemVerilog

Interfața modulului Adder este prezentată mai jos. Observație: Ca și în sistemul numeral zecimal, unde suma a două numere de n cifre are nevoie de n + 1 cifre (9 + 9 = 18), și în sistemul binar, suma a două numere de n biți va fi pe n + 1 biți.

Reprezentarea interfetei modulului "Adder" (black box)
module Adder(
    output logic [4:0] out,
    input logic [3:0] in0,
    input logic [3:0] in1
);

//implementare

endmodule

Cuvintele cheie module și endmodule sunt folosite pentru a începe și a încheia definirea unui modul. Imediat după cuvântul cheie module urmează numele modulului.

Convenție: Numele unui modul va începe cu literă mare.

După definirea numelui modulului, urmează lista de porturi, plasată între paranteze rotunde și separate prin virgulă. Cuvintele cheie acceptate sunt output (reprezentând un port de ieșire), input (reprezentând un port de intrare) și inout (reprezentând un port bidirecțional).

Sfat: Când aveți de ales în privința interfeței unui modul, se evită utilizarea semnalelor de tip inout. Acestea introduc elemente de tip Tri-state Buffer care sunt ineficiente. O alternativă mai eficientă este definirea a două porturi, unul de intrare și unul de ieșire, cu nume similare (ex: data_in și data_out).

Convenție: Întâi se definesc ieșirile, apoi intrările unui modul.

Convenție: Porturile unui modul se scriu unul sub altul, pe câte o linie, aliniate cu un tab la dreapta față de cuvântul cheie module.

După tipul portului, urmează dimensiunea acestuia, specificată în indecșii biților, unde cel mai puțin semnificativ bit are indexul 0. Spre exemplu, un semnal de 4 biți va avea următoarea specificație [3:0] (bitul cel mai semnificativ are indexul 3, cel mai puțin semnificativ 0, în total 4 biți). Observație: Semnalelor de un bit le lipsește specificația de dimensiune:

input semnal_de_un_bit,

După lista de porturi aflată între paranteze, definirea interfeței se termină cu caracterul ;.


Implementarea modulelor SystemVerilog

Implementarea modulelor SystemVerilog se face prin blocuri. Aceste blocuri pot avea sau nu corespondent într-un ciruit fizic. Dacă toate blocurile unui modul au corespondent într-un circuit fizic, atunci modulul este sintetizabil și poate fi transformat într-un circuit fizic. Blocurile care pot genera construcții sintetizabile sunt de patru tipuri:

  • blocuri assign
  • blocuri always
  • blocuri de instanțiere
  • blocuri generate

În SystemVerilog toate semnalele sunt de tip (logic), acesta acoperind atat fire cat si registre.

Blocurile care sunt întotdeauna nesintetizabile și sunt folosite exclusiv pentru simulare:

  • blocuri initial

Observație: Nu orice bloc assign sau always este sintetizabil. Există construcții valide sintactic dar care nu au corespondent în circuit. Aceste blocuri pot fi simulate dar nu pot fi utilizate pentru programarea unei plăci FPGA.

Observație: Ordinea blocurilor într-un modul nu contează.

Cuvantul cheie "logic"

In SystemVerilog, toate semnalele sunt de tip logic, indiferent de natura lor. In urma sintezei semnalele vor deveni fire, latch-uri sau registre.

Semnalele într-un modul SystemVerilog se definesc în felul următor:

logic [3:0] semnal;


Regulă: Niciun element nu își poate schimba valoarea în mai mult de un bloc.


Blocuri assign

Modulul Adder implementat

Assign este un cuvănt cheie care generează circuite combinaționale. Cum sumatorul este un circuit combinațional, îl putem implementa cu un bloc assign:

module Adder(
    output logic [4:0] out,
    input logic [3:0] in0,
    input logic [3:0] in1
);

assign out = in0 + in1;

endmodule

Observație: În general, un bloc assign va genera un circuit sintetizabil. Există și excepții, atunci când operația dorită este prea complexă pentru a fi implementată printr-un circuit combinațional, în mod eficient. Spre exemplu:

assign out = in0 / in1;

nu este un cod sintetizabil pentru majoritatea tool-urilor de sinteză, dar funcționează perfect într-o simulare.

Blocuri always combinaționale

Formatul pentru un bloc always combinational este următorul:

always_comb begin
    //...
end

Un bloc de tip always_comb este sensibil la modificare oricarui semnal din cadrul modulului unde a fost definit, iesirile comandate de acest bloc sunt recalculate la orice modificare a unui semnal de intrare.

În acest caz, putem reface implementarea sumatorului folosind un bloc always_comb în felul următor:

module Adder(
    output logic [4:0] out,
    input logic [3:0] in0,
    input logic [3:0] in1
);

always_comb begin
    out = in0 + in1;
end

endmodule

Blocuri always secvențiale. Asignări non-blocante (non-blocking assignments)

Circuitele secvențiale sunt circuitele care sunt sincronizate de semnalul de ceas. Acest semnal este, de obicei, produs de un generator de ceas și se definește ca intrare pentru fiecare modul secvențial (în care există cel puțin un registru). Putem modifica exemplul anterior, astfel încât ieșirea modulului de sumare să fie sincronă (adică să se modifice doar pe frontul pozitiv de ceas). Astfel, în interfața modulului, apare și semnalul de ceas:

module SyncedAdder(
    output logic [4:0] out,
    input logic [3:0] in0,
    input logic [3:0] in1,
    input logic clock
);

//implementation here

endmodule

Regulă: Pentru descrierea circuitelor secvențiale, se folosește întotdeauna un bloc always_ff sau always_latch.

Modulul SyncedAdder

Blocul always_ff care descrie un circuit secvențial are în lista de sensitivități numai semnalul de ceas iar el nu este sensibil la orice tranziție a ceasului ci numai la unul din fronturi (de obicei cel pozitiv). Astfel, putem implementa sumatorul sincron în felul următor:

module SyncedAdder(
    output logic [4:0] out,
    input logic [3:0] in0,
    input logic [3:0] in1,
    input logic clock
);

always_ff@(posedge clock) begin
    out <= in0 + in1;
end

endmodule

Observație: Cuvântul cheie care specifică frontul pozitiv al unui semnal este posedge iar cel pentru frontul negativ al semnalului este negedge.

Observați operatorul <= folosit pentru atribuirea sumei registrului destinație. Acesta NU este operatorul logic de mai mic sau egal, ci reprezintă un mod de atribuire care se numește non-blocantă. Diferența dintre atribuirea blocantă (=) și cea non-blocantă (<=) este că cea de-a doua întâi evalueaza toate expresiile din partea dreaptă a operatorului de atribuire (în cazul de față, suma in0 + in1) și abia apoi asignează rezultatul registrului destinație. Spre exemplu, dacă dorim să inversăm valorile a două registre, reg0 și reg1, astfel încât reg0 să ia valoarea lui reg1 și invers, vom face în felul următor:

module RegSwapper(
    //...
);

logic [31:0] reg0;
logic [31:0] reg1;

//cod corect
always_ff@(posedge clock) begin
    reg0 <= reg1;
    reg1 <= reg0;
end

endmodule

Modulul se va comporta conform așteptărilor. Operatorul <= nu va bloca restul evaluărilor din blocul always și prin urmare toate asignările se fac simultan, după evaluarea expresiilor din partea dreaptă a operatorului. Pe de altă parte, dacă facem asignarea blocantă:

module RegSwapper(
    //...
);

logic [31:0] reg0;
logic [31:0] reg1;

//cod incorect
always_ff@(posedge clock) begin
    reg0 = reg1;
    reg1 = reg0;
end

endmodule

aceasta va bloca restul operațiilor până când asignarea se termină. În acest caz, după primul front de ceas, ambele registre vor conține valoarea inițială a lui reg1.

Regulă: Operatorul de asignare non-blocantă (<=) se folosește doar în blocuri always_ff, always_latch sau initial.

Regulă: În același bloc always, se folosește același tip de operator de asignare pentru toate operațiile.

Sfat: Utilizați asignare non-blocantă pentru toate blocurile always care generează circuite secvențiale (sunt sincronizate de ceas) și asignare blocantă în toate celelalte cazuri.

Blocuri de instanțiere

Un sumator de patru numere format din trei sumatoare de două numere

Odată definit un modul, acesta poate fi folosit de oricâte ori pe parcursul unuia sau mai multor proiecte. Acest sistem se numește instanțiere. Să luam, ca exemplu, un modul care trebuie să facă suma a 4 numere pe 3 biți. Putem să folosim în acest scop modulul Adder pe 4 biți definit mai sus, în configurația din figură. Observați că se utilizează același modul de trei ori. Este ineficient și inutil să scriem de trei ori implementarea modulului, așa că vom recurge la metoda instanțierii. Orice instanță a unui modul trebuie să aibă un nume unic, după cum și într-un limbaj de programare, variabilele de același fel trebuie sa aibă un nume unic.

Se observă în continuare că există semnale (fire) în modulul Adder4, care nu sunt nici intrări, nici ieșiri, și sunt folosite doar pentru conectarea modulelor Adder (cel roșu și cel verde). Aceste semnale sunt fire (wire) și trebuie declarate ca atare.

Sintaxa pentru instanțierea unui modul este următoarea:

NumeModulInstantiat numeInstanta(
    .nume_intrare_sau_iesire_0(nume_semnal_legat_la_intrare_sau_iesire_0),
    .nume_intrare_sau_iesire_1(nume_semnal_legat_la_intrare_sau_iesire_1),
    //...
    .nume_intrare_sau_iesire_n(nume_semnal_legat_la_intrare_sau_iesire_n),
);

Haideți deci să vedem cum arată codul SystemVerilog pentru modulul Adder4:

module Adder4(
    output logic [4:0] out,
    input logic [2:0] in0,
    input logic [2:0] in1,
    input logic [2:0] in2,
    input logic [2:0] in3
);

//definirea firelor de legatura
logic [3:0] subsum0;
logic [3:0] subsum1;

//instantierea modulelor
Adder adder0(
    .out(subsum0),    //iesirea numita out a instantei adder0 se leaga la firul subsum0
    .in0(in0),        //la intrarea numita in0 a instantei adder0 se leaga intrarea in0 a modulului
    .in1(in1)         //la intrarea numita in1 a instantei adder0 se leaga intrarea in1 a modulului
);

Adder adder1(
    .out(subsum1),    //iesirea numita out a instantei adder1 se leaga la firul subsum1
    .in0(in2),        //la intrarea numita in0 a instantei adder1 se leaga intrarea in2 a modulului
    .in1(in3)         //la intrarea numita in1 a instantei adder1 se leaga intrarea in3 a modulului
);

Adder adder2(
    .out(out),        //iesirea numita out a instantei adder2 se leaga la iesirea out a modulului
    .in0(subsum0),    //la intrarea numita in0 a instantei adder2 se leaga firul subsum0
    .in1(subsum1)     //la intrarea numita in1 a instantei adder2 se leaga firul subsum1
);

endmodule

Observație: Instanțele adder0 și adder1 au legate la intrări semnale de 3 biți și la ieșire legate fire de 4 biți, cu toate că porturile lor au 4, respectiv 5 biți (intrările și ieșirile). În acest caz, semnalele se leagă fiind aliniate la cel mai puțin semnificativ bit și se generează un mesaj de avertizare (port mismatch). Cu toate că programele folosite de noi acceptă codul de mai sus, există programe care nu acceptă astfel de construcții, și se opresc cu eroari. Ca să fim foarte riguroși, vom folosi operatorul de concatenare pentru a corecta codul în felul următor (mai multe despre operatori veți putea afla mai jos):

module Adder4(
    output logic [4:0] out,
    input logic [2:0] in0,
    input logic [2:0] in1,
    input logic [2:0] in2,
    input logic [2:0] in3,
);

//definirea firelor de legatura
logic [4:0] subsum0; //definim firele pe 5 biți deoarece iesirea instantelor adder0 si adder1 este pe 5 biti
logic [4:0] subsum1;

//instantierea modulelor
Adder adder0(
    .out(subsum0),    //iesirea numita out a instantei adder0 se leaga la firul subsum0
    .in0({1'b0,in0}), //la intrarea numita in0 a instantei adder0 se leaga un bit 0 concatenat cu intrarea in0 a modulului
    .in1({1'b0,in1})  //la intrarea numita in1 a instantei adder0 se leaga un bit 0 concatenat cu intrarea in1 a modulului
);

Adder adder1(
    .out(subsum1),    //iesirea numita out a instantei adder1 se leaga la firul subsum1
    .in0({1'b0,in2}), //la intrarea numita in0 a instantei adder1 se leaga un bit 0 concatenat cu intrarea in2 a modulului
    .in1({1'b0,in3})  //la intrarea numita in1 a instantei adder1 se leaga un bit 0 concatenat cu intrarea in3 a modulului
);

Adder adder2(
    .out(out),           //iesirea numita out a instantei adder2 se leaga la iesirea out a modulului
    .in0(subsum0[3:0]),  //la intrarea numita in0 a instantei adder2 se leaga cei mai putin semnificativ 4 biti din firul subsum0
    .in1(subsum1[3:0])   //la intrarea numita in1 a instantei adder2 se leaga cei mai putin semnificativ 4 biti din firul subsum1
);

endmodule

Module de test (nesintetizabile)

Modul de test pentru sumatorul pe 4 biți

Modulele de test sunt folosite pentru verificarea funcționalității unui alt modul sintetizabil. Figura alăturată prezintă schema bloc pentru un modul de test pentru sumatorul pe 4 biți. Un modul de test generic urmărește câteva reguli clare:

  • pentru fiecare intrare a modulului de testat, se definește o variabilă tip logic, de aceeași dimensiune cu intrarea, folosite pentru a genera stimuli pentru circuit;
  • pentru fiecare ieșire a modulului de testat, se definește o variabilă tip logic, de aceeași dimensiune cu ieșirea, folosite pentru verificarea comportamentului (de obicei prin afișarea de forme de undă);
  • se instanțiază modulul de testat și se leagă variabilele definite anterior la intrările și ieșirile instanței;
  • se scrie un generator de stimuli (un bloc initial) în care se programeaza o secvență temporizată de tranziții pentru intrări;
  • se folosește un software de simulare pentru a rula testul și a vedea formele de undă.

Astfel, modulul de test pentru sumatorul pe 4 biți va arăta astfel:

module AdderTester;

//this is the definition of variables and wires
//for driving signals  
logic [3:0] i0;
logic [3:0] i1;
logic [4:0] o;

//this is where the stimuls is added for the module
initial begin
     i0 = 0;
     i1 = 0;
  #5 i0 = 3;
  #2 i0 = 2;
     i1 = 1;
  #3 i0 = 1;
     i1 = 5;
  #5 $stop();
end
  
//this is where the tested module is instantiated  
Adder deviceUnderTest(
  .out(o),
  .in0(i0),
  .in1(i1)
);  
  
endmodule

Blocul initial este folosit pentru a da valori intrărilor modulului de testat. El iși începe execuția la momentul de timp t = 0. Fiecare linie este executată la același moment de tip, până când este întâlnit operatorul #, care așteaptă trecerea unor unități de timp. Astfel, în exemplul de mai sus, primele două linii se execută la t = 0, linia 3 se execută la t = 5, liniile 4 și 5 la t = 7, liniile 6 și 7 la t = 10 și ultima linie, la t = 15. Apelul funcției $stop() determină sfârșitul simulării.

Semnalul de ceas în module de test

Cuvântul cheie forever este folosit pentru a executa o atribuire sau apel de funcție în mod repetat, până la sfârșitul simulării. Acesta este în particular folositor pentru a genera un semnal de ceas. Secvența următoare de cod generează un semnal de ceas cu perioada de două unități de timp:

reg clock;

initial begin
    clock = 0;
    forever #1 clock = !clock;
end


Regulă:Pentru circuitele secvențiale, modulul de test va avea două blocuri initial. Primul este similar celui de mai sus, și utilizat pentru generarea semnalului de ceas. Al doilea este folosit pentru generarea celorlalte semnale de intrare și directivei de oprire a simulării. Motivul pentru această segregare este faptul că instrucțiunea forever este blocantă (adică instrucțiunile puse după ea nu se execută). Toate blocurile initial dintr-un modul de test se execută în paralel.

Sintaxa SystemVerilog

Constante

Constantele sunt valori numerice. Pentru fiecare valoare, se specifică numărul de biți pe care reprezentată si baza în care este scrisă. Spre exemplu 8'b111 este o constantă pe 8 biți a cărei valoare în binar este 111 (adică în decimal, 7). În același fel, 16'd10 reprezintă valoarea 10 în decimal reprezentată pe 16 biți. Este posibilă specificarea valorii fără număr de biți sau bază (în care caz baza implicită este cea decimală, și numărul de biți este aproximat de către compilator).

Operatori

Operatori aritmetici

Operație Operator Exemplu
Adunare + a + b
Scădere - a - b
Înmulțire * a * b
Împărțire / a / b

Operatori logici

Operație Operator Exemplu
Egal == a == b
Diferit != a != b
Mai mic < a < b
Mai mic sau egal <= a <= b
Mai mare > a > b
Mai mare sau egal >= a >= b

Operatori binari logici pe biți

Operație Operator Exemplu
Și & a & b
Sau | a | b
Sau exclusiv ^ a ^ b


Operatori unari logici pe biți

Operație Operator Exemplu
Negare ~ ~b
Și pe biții unui semnal & &a
Sau pe biții unui semnal | |a
Sau exclusiv pe biții unui semnal ^ ^a

Operatori de shiftare pe biți

Operație Operator Exemplu
Shiftare la stânga << a << b
Shiftare la dreapta >> a >> b

Operatorul de acces la biți

Dându-se un semnal pe n biți, operatorul de access la o sub-secvență din biții semnalului este [m:k] unde m și k sunt indecșii biților doriți, cu n < m < k. Ca exemplu:

logic [31:0] un_registru;
logic [7:0] un_fir;
logic alt_fir;

assign un_fir = un_registru[30:23]; //firul "un_fir" ia valoarea data de secventa de biti de la 23 la 30 din registrul "un_registru"
assign alt_fir = un_fir[3];         //firul "alt_fir" va lua valoarea bitului 3 din semnalul "un_fir" care este bitul 26 din semnalul "un_registru"

Operatorul de concatenare

Operatorul de concatenare este "{" și "}". Astfel, o serie de semnale separate prin virgulă și închise între două acolade va rezulta într-un singur semnal de dimensiune egală cu suma dimensiunilor semnalelor componente.

logic [3:0] a;
logic [4:0] b;
logic c;
logic [9:0] d;

//semnalul d este format din concatenarea semnalelor a,b,c
//unde a este pe pozitia cea mai semnificativa si c
//pe pozitia cea mai putin semnificativa
assign d = {a,b,c};

Operatorul de replicare

Operatorul de replicare este folosit când se dorește replicarea unui semnal de un număr mare sau necunoscut de ori (dat ca parametru). Astfel, construcția {n{m}} reprezintă semnalul m, multiplcat de n ori.

logic a;
logic [4:0] b;
logic [7:0] c;
logic [15:0] d;

assign b = {5{a}}; //b va lua valoarea data de a, replicata de 5 ori
assign d = {2{c}}; //d va lua valoarea lui c (8 biti) replicata de 2 ori

Blocuri și operatori condiționali

Condițiile se pot exprima în trei moduri:

  • folosing operatorul condițional cunoscut din limbajul C (condiție ? valoare_pentru_adevărat : valoare_pentru_fals);
  • folosind construcția if - else;
  • folosind constructia case - endcase respectiv casex - endcase sau casez - endcase.

Operatorul condițional se poate utiliza în orice tip de bloc. Exemplu:

logic a;
logic [3:0] b;
logic [3:0] c;
logic [3:0] d;

assign b = a ? c : d; //daca a este adevarat (adica daca este egal cu 1), atunci b ia valoarea lui c, altfel, b ia valoarea lui d

Blocurile if - else, case - endcase se pot utiliza doar în blocuri always. Exemplu:

logic a;
logic [3:0] b;
logic [3:0] c;
logic [3:0] d;

always_comb begin
    if(a) begin
        b = c;
    end else begin
        b = d;
    end
end

Exemplu case - endcase:

logic [1:0] a;
logic  [3:0] e;
logic [3:0] c;
logic [3:0] d;

//daca a este egal cu 0, atunci e ia valoarea lui c
//daca a este egal cu 1, atunci e ia valoarea lui d
//in orice alt caz, b ia valoarea 0
always_comb begin
    case(a)
        2'b00: e = c;
        2'b01: e = d;
        default: e = 3'b000;
    endcase
end