CID Seminar 4: Diferență între versiuni

De la WikiLabs
Jump to navigationJump to search
m (a redenumit CID Seminar 5 în CID Seminar 4)
Fără descriere a modificării
Linia 1: Linia 1:
Numărătorul (''counter'') este unul din cele mai utilizate circuite digitale şi sigur unul dintre cele mai cunoscute în afara lumii electroniştilor. Orice  microprocesor are un numărător de program (''program counter'') fără de care nu poate executa programele din calculator. Numărătorul de program generează o secvenţă de adrese de memorie în ordine crescătoare, câte una în fiecare perioadă de ceas, şi din când în când, când se execută un salt în program, sare la o adresă diferită de cea imediat următoare.
Counter ('' counter '') is one of the most used digital circuits and surely one of the best known outside the world of electronics. Any microprocessor has a program counter ("program counter") without which it can not run the programs on the computer. The program counter generates a sequence of memory addresses in ascending order, one in each clock period, and occasionally when a jump in the program jumps to an address other than the next one.


== Exemplul 1 ==
== Example 1 ==


Cel mai simplu numărător este registrul cu incrementare. Exercitiul 4 din seminarul 2 v-a cerut descrierea unui circuit combinaţional de incrementare comandată, una din variantele posibile fiind:
The simplest count is the increment register. Exercise 4 in Seminar 2 required a description of a combined incremental command circuit, one of the possible variants being:


<syntaxhighlight lang="verilog">
<syntaxhighlight lang="verilog">
module increment(
increment modules (
     input inc,
     input inc,
     input [3:0] a,
     input [3: 0] and,
     output reg [3:0] out
     output reg [3: 0] out
);
)


always @(*)
always @ (*)
     out = inc ? a + 1 : a;
     out=inc? a + 1: a;


endmodule
endmodule
</syntaxhighlight>
<syntaxhighlight>


Cu minime modificări descrierea de mai sus poate fi transformată într-un numărător cu incrementare comandată. Pentru a genera o secvenţă crescătoare de numere, numărul de la ieşire este readus la intrare. Bucla o putem închide în interiorul modulului, renunţând la intrarea a:
With minimal modifications, the above description can be converted to a commanded increment counter. To generate an increasing sequence of numbers, the output number is returned to the input. The loop can be closed inside the module, giving up the input of:


<syntaxhighlight lang="verilog">
<syntaxhighlight lang="verilog">
module incorect(
incorrect modules (
     input inc,
     input inc,
     output reg [3:0] out
     output reg [3: 0] out
);
)


always @(*)
always @ (*)
     out = inc ? out + 1 : out; // buclă infinită!
     out=inc? out + 1: out; //infinite loop!


endmodule
endmodule
</syntaxhighlight>
<syntaxhighlight>


Închiderea unei bucle peste un circuit combinaţional duce de cele mai multe ori la un circuit instabil, iar în cazul descrierii de mai sus simularea va intra într-o buclă infinită. Reţineţi aşadar o regulă de proiectare:
Closing a loop over a combinational circuit often leads to an unstable circuit, and in the case of the above description the simulation will go into an infinite loop. Please note, therefore, a design rule:


<div class="regula">Într-un circuit combinaţional sunt interzise buclele! O variabilă nu poate fi folosită niciodată în calculul propriei sale valori!</div>
<div class="rule"> In a combined circuit, the loops are forbidden! A variable can never be used in calculating its own value! </div>


Construcţii precum
Buildings like
<syntaxhighlight lang="verilog">
<syntaxhighlight lang="verilog">
         out = inc ? out + 1 : out;
         out=inc? out + 1: out;
</syntaxhighlight>
<syntaxhighlight>
sau
or
<syntaxhighlight lang="verilog">
<syntaxhighlight lang="verilog">
out <= inc ? out + 1 : out;
out <= inc? out + 1: out;
</syntaxhighlight>
<syntaxhighlight>
nu pot fi folosite într-un bloc de descriere combinaţională, fie ''assign'', fie ''always @(*)''.
can not be used in a combination description block, either '' assign '' or '' always @ (*) ''.


Bucla poate fi închisă numai peste un circuit combinaţional şi un registru, obţinându-se un circuit secvenţial. Prin urmare vom transforma blocul always dintr-unul combinaţional (sensibil la orice modificare a variabilelor din expresiile din partea dreapta) într-unul secvenţial, sensibil numai la fronturile active ale semnalului de ceas (obs. 1). Semnalul de ceas trebuie declarat ca intrare (obs. 2). Şi, foarte important, atribuirea din blocul secvenţial este obligatoriu nonblocantă/non-blocking (obs. 3), regulă de bază în descrierea circuitelor secvenţiale (vezi exemplul 2 din seminarul 3):
The loop can only be closed over a combinational circuit and a register, obtaining a sequential circuit. Therefore, we will always convert the block from a combinational one (sensitive to any change of the variables in the right-hand expressions) into a sequential one, sensitive only to the active edge of the clock signal (1). The clock signal must be declared as input (2). And, very importantly, the assignment in the sequential block is obligatory nonblocking /non-blocking (3), a basic rule in the description of sequential circuits (see example 2 of seminar 3):


<syntaxhighlight lang="verilog">
<syntaxhighlight lang="verilog">
module counter(
module counter (
     input clk, // <-- obs. 2
     input clk, //<- obs. 2
     input inc,
     input inc,
     output reg [3:0] out
     output reg [3: 0] out
);
)


always @(posedge clk) // <-- obs. 1
always @ (posedge clk) //<- obs. 1
     out <= inc ? out + 1 : out; // <-- obs. 3
     out <= inc? out + 1: out; //<- obs. 3


endmodule
endmodule
</syntaxhighlight>
<syntaxhighlight>


Descrierea de mai sus este echivalentă următoarei scheme bloc:
The above description is equivalent to the following block scheme:


[[Fișier:sem5_fig1.png]]
[[File: sem5_fig1.png]]


''Scrieţi un modul de testare a modulului counter care să genereze semnalul periodic de ceas şi semnalul de comandă, iniţial inactiv, apoi activ, şi simulaţi circuitul. Ce se observă la ieşirea lui?''
'' Write a test module of the counter module that generates the clock and command signal, initially inactive, then active, and simulate the circuit. What's on his way out? "


Descrierea de mai sus este sintetizabilă şi numărătorul sintetizat funcţionează ireproşabil. Şi totuşi la simulare ieşirea este tot timpul nedefinită. Motivul îl reprezintă valoarea iniţială a acesteia. Orice actualizare a variabilei out se bazează pe valoarea ei precedentă, iar prima execuţie a instrucţiunii de atribuire (''out <= inc ? out + 1 : out;'') ia în calcul valoarea iniţială. Valoare care este nedefinită, adică x! Adunând 1 la o valoare nedefinită se obţine tot o valoare nedefinită. Simularea este totuşi doar o aproximare a realităţii şi trebuie să fim conştienţi de limitele acesteia.
The above description is synthesizable and the synthesized counter works flawlessly. And yet at simulation the output is indefinitely indefinite. The reason is the initial value of the reason. Any update of the out variable is based on its previous value, and the first execution of the assignment instruction ('' out <= inc? Out + 1: out; '') takes into account the initial value. Value that is undefined, ie x! Gathering 1 at an undefined value also gives an undefined value. Simulation is, however, only an approximation of reality, and we need to be aware of its limitations.


Un artificiu pentru a iniţializa variabila out ar fi forţarea valorii ei din afara modulului, din modulul de test.
An artifact to initialize the out variable would be forcing its value out of the module from the test module.


În timpul verificării putem avea acces direct la orice semnal (variabilă) din interiorul modulului testat dacă folosim numele complet al acestuia, nume unic în cadrul ierarhiei de blocuri din structura modulului testat. Numele complet se specifică asemenea numelui unei variabile dintr-o ierarhie de obiecte dintr-un program scris într-un limbaj orientat pe obiecte:
During verification, we can have direct access to any (variable) signal inside the tested module if we use its full name, unique name within the block hierarchy in the structure of the tested module. The full name is specified as the name of a variable in a hierarchy of objects in a program written in an object-oriented language:


<syntaxhighlight lang="verilog">
<syntaxhighlight lang="verilog">
Linia 79: Linia 79:


     numeInstanţăTop.numeInstanţăBloc.numeInstanţăSubbloc.semnal
     numeInstanţăTop.numeInstanţăBloc.numeInstanţăSubbloc.semnal
</syntaxhighlight>
<syntaxhighlight>


În cazul numărătorului nostru, dacă în modulul de test acesta este instanţiat cu numele uut, variabila out poate fi iniţializată în pasul 0 al simulării direct din modulul de test:
In the case of our counter, if in the test module it is instantiated with the ugly name, the out variable can be initialized in step 0 of the simulation directly from the test module:


<syntaxhighlight lang="verilog">
<syntaxhighlight lang="verilog">
     initial uut.out = 0;
     initial uut.out=0;
</syntaxhighlight>
<syntaxhighlight>


Acest mod de acces trebuie folosit cu parcimonie şi numai în module de test, nefiind o instrucţiune sintetizabilă! Se recomandă folosirea accesului direct doar în scopul citirii (monitorizării) unor variabile la care nu avem acces direct prin interfaţa instanţei de top. Atribuirile prin acces direct trebuie limitate strict la situaţiile în care ştim foarte clar toate efectele pe care le produce.
This mode of access should be used with parsimony and only in test modules, not a synthesized statement! It is recommended to use direct access only for the purpose of reading (monitoring) variables that we do not have direct access through the top court interface. Assignments through direct access must be strictly limited to situations where we know very clearly all the effects it produces.
Reţineţi:
note:


<div class="regula">Este interzisă în codul unui modul de circuit folosirea accesului direct la un semnal intern al altui modul.</div>
<div class="rule"> It is forbidden in the code of a circuit module to use direct access to an internal signal of another module. </div>


Mult mai elegant este să proiectăm numărătorul cu o intrare de reset sau de ştergere (clear), intrare utilă în practică oricând dorim să aducem imediat numărătorul la zero. Iniţializarea numărătorului va fi făcută prin activarea intrării de ştergere.
Much more elegant is to design the counter with a reset or clear input, a practical input whenever we want to bring the numerator to zero. Initializing the counter will be done by activating the delete entry.


== Exemplul 2 ==
== Example 2 ==


''Adăugaţi numărătorului o intrare de resetare sincronă (numită deseori clear) activă în 0. Verificaţi circuitul folosind acelaşi modul de test în care la începutul simulării să activaţi pentru cel puţin o perioadă de ceas intrarea de resetare.''
'' Add the numerator a synchronous reset (often called clear) input in 0. Check the circuit using the same test module in which you start the reset input for at least one clock time at the beginning of the simulation. ''


Intrarea de activare a numărării (intrarea ''inc'' în exemplul 1) este de obicei numită ''enable'' sau count enable (''ce'', ''cen'' etc).
The activation input of the count (entry '' inc '' in example 1) is usually called 'enable' 'or count enable (' 'ce' ',' 'cen' 'etc).


[[Fișier:sem5_fig2.png]]
[[File: sem5_fig2.png]]


Pentru a verifica întreaga secvenţă de numărare trebuie să lăsăm simularea să "curgă" mai mult de 16 perioade de ceas din momentul în care se activează comanda de incrementare (''enable''). Putem să folosim în modulul de test o întârziere calculată pe baza perioadei semnalului de ceas, de exemplu ''#170'' dacă perioada ceasului este 10, sau, mai comod, putem aştepta un număr precis de fronturi active ale ceasului folosind instrucţiunea ''repeat'':
To check the entire counting sequence, let the simulation "run" for more than 16 clock periods when the enable command is activated. We can use a clock delay calculated in the test module, for example '' #170 '' if the clock time is 10 or, more conveniently, we can expect a precise number of active clock fronts using the ' repeat ':


<syntaxhighlight lang="verilog">
<syntaxhighlight lang="verilog">
     repeat (17) @(posedge clk);
     repeat (17) @ (posedge clk);
</syntaxhighlight>
<syntaxhighlight>


''repeat(n)'' repetă de n ori execuţia blocului de instrucţiuni ataşat. În cazul de mai sus se repetă de 16 ori aşteptarea frontului activ al ceasului. Avantajul acestei instrucţiuni este că nu depindem de valoarea absolută a perioadei de ceas.
'' repeat (n) '' repeatedly executes the execution of the attached instruction block. In the above case, repeat 16 times the active front of the clock. The advantage of this instruction is that we do not depend on the absolute value of the clock.


Observaţi cum numărătorul "se dă peste cap" după ce ajunge la valoarea maximă (15), reluând numărarea de la zero (se incrementează modulo 16).
Observe how the count is "overhead" after reaching the maximum value (15), resuming the counting from zero (increments modulo 16).


== Exemplul 3 ==
== Example 3 ==


''Să se descrie comportamental un numărător sincron pe 4 biţi cu încărcare sincronă, cu o intrare  de activare a numărării şi o intrare de ştergere. Intrările de comandă a încărcării (''load'') şi de ştergere (''clear'') sunt active în zero. Toate comenzile acţionează numai sincron cu ceasul. Dacă sunt active simultan mai multe intrări de comandă, intrarea cea mai prioritară determină valoarea următoare a ieşirii. În ordinea descrescătoare a priorităţilor comenzile sunt: ''clear, load, enable.
'' Behaviorally describe a synchronous synchronous 4-bit counter with a synchronous load, a countdown input, and a deletion input. Load command ('' load '') and delete ('' clear '') are active in zero. All controls act only synchronously with the clock. If multiple command inputs are active at the same time, the highest priority input determines the next output value. In descending order of priority, the commands are: "clear, load, enable.


[[Fișier:sem5_fig3.png]]
[[File: sem5_fig3.png]]


Intrarea de încărcare este activă în 0. Când aceasta este activă, frontul activ al ceasului determină încărcarea numărătorului cu valoarea de pe intrarea target. Toate funcţiile acestui numărător pot fi aranjate într-un tabel:
The charging input is active in 0. When it is active, the active clock front causes the counter to load the value on the target entry. All functions of this counter can be arranged in a table:


{| class="wikitable"
{| class="wikitable"
|- bgcolor="#ddeeff" align="left"
|- bgcolor="#ddeeff" align="left"
|clear || load || enable ||cnt+ || funcţia
|clear || load || enable || cnt + || function
|- bgcolor="#ddffdd" align="left"
|- bgcolor="#ddffdd" align="left"
|0 ||X ||X ||0 ||ştergere
|0 || X || X || 0 || deletion
|- bgcolor="#ddffdd" align="left"
|- bgcolor="#ddffdd" align="left"
|1 ||0 ||X ||target ||încărcare
|1 || 0 || X || target || loading
|- bgcolor="#ddffdd" align="left"
|- bgcolor="#ddffdd" align="left"
|1 ||1 ||0 ||cnt ||memorare
|1 || 1 || 0 || cnt || memorization
|- bgcolor="#ddffdd" align="left"
|- bgcolor="#ddffdd" align="left"
|1 ||1 ||1 ||cnt+1 ||numărare
1 || 1 || 1 || cnt + 1 || counting
|}
|}


În tabelul de mai sus valoarea X din dreptul unor intrări semnifică faptul că valoarea logică a acelor intrări nu contează (''don't care''). Tabelul de adevăr poate fi astfel comprimat substanţial. De exemplu, atunci când intrarea de resetare este inactivă (''clear == 1'') şi cea de încărcare e activată (''load == 0'') numărătorul încarcă (valoarea ''target'') indiferent de valoarea logică a intrării enable. Cele două combinaţii de valori de pe intrările de comandă, 100 şi 101 sunt colapsate într-una singură, 10X.
In the table above, the value X at some inputs means that the logical value of those entries does not matter ('' do not care ''). The truth table can thus be substantially compressed. For example, when the reset input is inactive ('' clear == 1 '') and charging is enabled ('' load == 0 ''), the numerator loads ('target' value) the enable entry. The two value combinations on the control inputs 100 and 101 are collapsed in one, 10X.
Această compactare poate fi folosită şi în construcţiile tabelare din Verilog, precum instrucţiunea ''case''. Pentru a putea folosi valoarea X în specificarea cazurilor instrucţiunii trebuie utilizată instrucţiunea alternativă ''casex'', care are acelaşi format ca instrucţiunea ''case'' (vezi exemplul 4 din seminarul 2):
This compaction can also be used in Verilog table constructions, such as the 'case' instruction. In order to use the X value in specifying the cases of the instruction, the alternative casex, which has the same format as the 'case' instruction (see example 4 in the seminar 2), must be used:


<syntaxhighlight lang="verilog">
<syntaxhighlight lang="verilog">
casex (expresie_case)
casex (expression_case)
     valoare1: instrucţiune1
     value1: instruction1
     ...
     ...
     default: instrucţiune
     default: instruction
endcase
endcase
</syntaxhighlight>
<syntaxhighlight>


Spre deosebire de construcţia ''case'', care admite doar valori numerice precise pentru fiecare caz, construcţia ''casex'' permite ca unii biţi să fie nedefiniţi (ignoraţi) în unele cazuri. De exemplu cazul analizat anterior poate fi descris în Verilog astfel:
Unlike the 'case' construction, which admits only precise numerical values ​​for each case, the casex construction allows some bits to be undefined in some cases. For example, the previously analyzed case can be described in Verilog as follows:


<syntaxhighlight lang="verilog">
<syntaxhighlight lang="verilog">
     3'b10X: out <= target;
     3'b10X: out <= target;
</syntaxhighlight>
<syntaxhighlight>


''Verificaţi funcţionalitatea circuitului pentru toate combinaţiile de valori ale semnalelor de control.''
'' Check circuit functionality for all combinations of control signal values. ''


Generarea exhaustivă a tuturor combinaţiilor de valori ale semnalelor de control se face cel mai comod într-un ciclu de tip ''for'', în care tratăm combinaţia de valori ca un număr în binar pe care îl iniţializăm cu zero la începutul secvenţei de test şi pe care apoi îl incrementăm la fiecare iteraţie. Instrucţiunea ''for'' are în Verilog acelaşi format ca în C sau în Java:
Exhaustive generation of all combinations of control signal values ​​is most convenient in a 'for' type cycle, in which we treat the value combination as a binary number that we initialize zero at the beginning of the test sequence and then we increment it at each iteration. The instruction 'for' has in Verilog the same format as in C or in Java:


<syntaxhighlight lang="verilog">
<syntaxhighlight lang="verilog">
     for(iniţializări; condiţie de oprire; atribuiri la sfârşit de iteraţie)
     for (initializations, stop condition, iteration end assignments)
         bloc de instrucţiuni
         block of instructions


</syntaxhighlight>
<syntaxhighlight>


ca de exemplu
such as


<syntaxhighlight lang="verilog">
<syntaxhighlight lang="verilog">
     for (index = 0; index < 8; index = index + 1)
     for (index=0; index <8; index=index + 1)
     begin
     begin
         {clear, load, enable} = index;
         {clear, load, enable}=index;
         repeat (16) @(posedge clk);
         repeat (16) @ (posedge clk);
     end
     end
</syntaxhighlight>
<syntaxhighlight>


Controlul buclei ''for'' îl putem face cu un index întreg care se incrementează iteraţie cu iteraţie până la valoarea dorită.
We can control the loop control for an integer index that increments iteration with iteration to the desired value.
În modulul de test putem declara şi folosi variabile de tip întreg (''integer'') la fel ca în limbajele de programare, însă aceste variabile nu sunt semnale:
In the test module, we can declare and use integer variables as in programming languages, but these variables are not signals:


<syntaxhighlight lang="verilog">
<syntaxhighlight lang="verilog">
     integer index;
     integer index;
</syntaxhighlight>
<syntaxhighlight>


O iteraţie din bucla ''for'' de mai sus durează 16 perioade de ceas pentru fiecare combinaţie de valori testate. Observaţi modul compact în care se atribuie valori celor 3 semnale. În loc de atribuiri individuale, cele trei semnale au fost grupate într-un vector de 3 biţi căruia i se atribuie apoi o valoare întreagă. Ultimii trei biţi din reprezentarea binară a valorii variabilei ''index'' vor fi atribuiţi celor trei semnale. Gruparea unor semnale în Verilog este posibilă graţie operatorului de concatenare:
An iteration of the 'for' loop above takes 16 clock periods for each combination of values ​​tested. Observe the compact mode that assigns values ​​to the three signals. Instead of individual assignments, the three signals were grouped into a 3-bit vector which is then assigned a full value. The last three bits of the binary representation of the value of the 'index' variable will be assigned to the three signals. Grouping of signals in Verilog is possible thanks to the concatenation operator:


<syntaxhighlight lang="verilog">
<syntaxhighlight lang="verilog">
     {clear, load, enable}
     {clear, load, enable}
</syntaxhighlight>
<syntaxhighlight>


Numărul de biţi ai noii variabile este suma biţilor variabilelor grupate, iar biţii sunt dispuşi în ordinea concatenării variabilelor. În cazul nostru ''clear'' este bitul cel semnificativ (MSB) al vectorului semnalelor de control.
The number of bits of the new variable is the sum of the bits of the grouped variables, and the bits are arranged in the order of concatenation of the variables. In our case, 'clear' is the significant bit (MSB) of the control signal vector.


== Exemplul 4 ==
== Example 4 ==


''Cu ajutorul numărătorului precedent, realizaţi un divizor controlabil de frecvenţă, factorul de divizare fiind selectabil astfel încât frecvenţa de ieşire să fie 1/2, 1/4, 1/8 sau 1/16 din frecvenţa ceasului.''
'' Using the previous counter, perform a controllable frequency divider, the divider being selectable so that the output frequency is 1/2, 1/4, 1/8 or 1/16 of the clock frequency. ''


La instanţierea modulului numărător aveţi grijă ca toate intrările acestuia să fie conectate, fie la un semnal generat în modului de test, fie la masă (la valoarea 0), fie la alimentare (la valoarea 1). Pot fi lăsate neconectate (lăsate "în gol" sau "flotante") doar acele intrări care nu afectează în vreun fel funcţionarea circuitului. În exemplul nostru intrarea ''target'' poate fi neglijată deoarece numărătorul nu va încărca niciodată. În general însă, intrările neconectate pot duce la rezultate eronate datorită valorii nedefinite (X) a acestora.
At the instantiation of the counting module, make sure that all inputs are connected either to a signal generated in the test mode, either to the mass (at 0) or to the feed (at value 1). Only those inputs that do not affect the operation of the circuit can be left unconnected ("empty" or "floating"). In our example, the 'target' entry can be neglected because the numerator will never load. In general, however, unconnected entries can result in erroneous results due to their undefined value (X).
O regulă care vă scuteşte de simulări şi sinteze surprinzătoare:
A rule that relieves you of surprising simulations and syntheses:


<div class="regula">Nu lăsaţi intrări neconectate!</div>
<div class="rule"> Do not leave unconnected entries! </div>


== TEMA ==
== Homework ==


''Implementaţi un generator de pulsuri cu frecvenţă şi factor de umplere controlabile. Perioada semnalului generat va fi egală cu un multiplu al perioadei ceasului, Tw = Tclk * tw, iar durata pulsului este controlată de o altă intrare, Tp = Tclk * tp.''
'' Implement a pulse generator with controlled frequency and fill factor. The generated signal period will be equal to a multiple of the clock, Tw=Tclk * tw, and the duration of the pulse is controlled by another input, Tp=Tclk * tp.


# folosind numărătorul din exemplul 3;
#using the numerator of Example 3;
# folosind o descriere pur comportamentală;
#using a purely behavioral description;


[[Fișier:sem5_fig4.png]]
[[File: sem5_fig4.png]]

Versiunea de la data 26 aprilie 2018 07:26

Counter ( counter ) is one of the most used digital circuits and surely one of the best known outside the world of electronics. Any microprocessor has a program counter ("program counter") without which it can not run the programs on the computer. The program counter generates a sequence of memory addresses in ascending order, one in each clock period, and occasionally when a jump in the program jumps to an address other than the next one.

Example 1

The simplest count is the increment register. Exercise 4 in Seminar 2 required a description of a combined incremental command circuit, one of the possible variants being:

<syntaxhighlight lang="verilog"> increment modules (

   input inc,
   input [3: 0] and,
   output reg [3: 0] out

)

always @ (*)

   out=inc? a + 1: a;

endmodule <syntaxhighlight>

With minimal modifications, the above description can be converted to a commanded increment counter. To generate an increasing sequence of numbers, the output number is returned to the input. The loop can be closed inside the module, giving up the input of:

<syntaxhighlight lang="verilog"> incorrect modules (

   input inc,
   output reg [3: 0] out

)

always @ (*)

   out=inc? out + 1: out; //infinite loop!

endmodule <syntaxhighlight>

Closing a loop over a combinational circuit often leads to an unstable circuit, and in the case of the above description the simulation will go into an infinite loop. Please note, therefore, a design rule:

In a combined circuit, the loops are forbidden! A variable can never be used in calculating its own value!

Buildings like <syntaxhighlight lang="verilog">

       out=inc? out + 1: out;

<syntaxhighlight> or <syntaxhighlight lang="verilog"> out <= inc? out + 1: out; <syntaxhighlight> can not be used in a combination description block, either assign or always @ (*) .

The loop can only be closed over a combinational circuit and a register, obtaining a sequential circuit. Therefore, we will always convert the block from a combinational one (sensitive to any change of the variables in the right-hand expressions) into a sequential one, sensitive only to the active edge of the clock signal (1). The clock signal must be declared as input (2). And, very importantly, the assignment in the sequential block is obligatory nonblocking /non-blocking (3), a basic rule in the description of sequential circuits (see example 2 of seminar 3):

<syntaxhighlight lang="verilog"> module counter (

   input clk, //<- obs. 2
   input inc,
   output reg [3: 0] out

)

always @ (posedge clk) //<- obs. 1

   out <= inc? out + 1: out; //<- obs. 3

endmodule <syntaxhighlight>

The above description is equivalent to the following block scheme:

Write a test module of the counter module that generates the clock and command signal, initially inactive, then active, and simulate the circuit. What's on his way out? "

The above description is synthesizable and the synthesized counter works flawlessly. And yet at simulation the output is indefinitely indefinite. The reason is the initial value of the reason. Any update of the out variable is based on its previous value, and the first execution of the assignment instruction ( out <= inc? Out + 1: out; ) takes into account the initial value. Value that is undefined, ie x! Gathering 1 at an undefined value also gives an undefined value. Simulation is, however, only an approximation of reality, and we need to be aware of its limitations.

An artifact to initialize the out variable would be forcing its value out of the module from the test module.

During verification, we can have direct access to any (variable) signal inside the tested module if we use its full name, unique name within the block hierarchy in the structure of the tested module. The full name is specified as the name of a variable in a hierarchy of objects in a program written in an object-oriented language:

<syntaxhighlight lang="verilog">

   numeInstanţăTop.semnal
   numeInstanţăTop.numeInstanţăBloc.semnal
   numeInstanţăTop.numeInstanţăBloc.numeInstanţăSubbloc.semnal

<syntaxhighlight>

In the case of our counter, if in the test module it is instantiated with the ugly name, the out variable can be initialized in step 0 of the simulation directly from the test module:

<syntaxhighlight lang="verilog">

   initial uut.out=0;

<syntaxhighlight>

This mode of access should be used with parsimony and only in test modules, not a synthesized statement! It is recommended to use direct access only for the purpose of reading (monitoring) variables that we do not have direct access through the top court interface. Assignments through direct access must be strictly limited to situations where we know very clearly all the effects it produces. note:

It is forbidden in the code of a circuit module to use direct access to an internal signal of another module.

Much more elegant is to design the counter with a reset or clear input, a practical input whenever we want to bring the numerator to zero. Initializing the counter will be done by activating the delete entry.

Example 2

Add the numerator a synchronous reset (often called clear) input in 0. Check the circuit using the same test module in which you start the reset input for at least one clock time at the beginning of the simulation.

The activation input of the count (entry inc in example 1) is usually called 'enable' 'or count enable (' 'ce' ',' 'cen' 'etc).

To check the entire counting sequence, let the simulation "run" for more than 16 clock periods when the enable command is activated. We can use a clock delay calculated in the test module, for example #170 if the clock time is 10 or, more conveniently, we can expect a precise number of active clock fronts using the ' repeat ':

<syntaxhighlight lang="verilog">

   repeat (17) @ (posedge clk);

<syntaxhighlight>

repeat (n) repeatedly executes the execution of the attached instruction block. In the above case, repeat 16 times the active front of the clock. The advantage of this instruction is that we do not depend on the absolute value of the clock.

Observe how the count is "overhead" after reaching the maximum value (15), resuming the counting from zero (increments modulo 16).

Example 3

Behaviorally describe a synchronous synchronous 4-bit counter with a synchronous load, a countdown input, and a deletion input. Load command ( load ) and delete ( clear ) are active in zero. All controls act only synchronously with the clock. If multiple command inputs are active at the same time, the highest priority input determines the next output value. In descending order of priority, the commands are: "clear, load, enable.

The charging input is active in 0. When it is active, the active clock front causes the counter to load the value on the target entry. All functions of this counter can be arranged in a table:

1 || 1 || 1 || cnt + 1 || counting
clear load enable cnt + function
0 X X 0 deletion
1 0 X target loading
1 1 0 cnt memorization

In the table above, the value X at some inputs means that the logical value of those entries does not matter ( do not care ). The truth table can thus be substantially compressed. For example, when the reset input is inactive ( clear == 1 ) and charging is enabled ( load == 0 ), the numerator loads ('target' value) the enable entry. The two value combinations on the control inputs 100 and 101 are collapsed in one, 10X. This compaction can also be used in Verilog table constructions, such as the 'case' instruction. In order to use the X value in specifying the cases of the instruction, the alternative casex, which has the same format as the 'case' instruction (see example 4 in the seminar 2), must be used:

<syntaxhighlight lang="verilog"> casex (expression_case)

   value1: instruction1
   ...
   default: instruction

endcase <syntaxhighlight>

Unlike the 'case' construction, which admits only precise numerical values ​​for each case, the casex construction allows some bits to be undefined in some cases. For example, the previously analyzed case can be described in Verilog as follows:

<syntaxhighlight lang="verilog">

   3'b10X: out <= target;

<syntaxhighlight>

Check circuit functionality for all combinations of control signal values.

Exhaustive generation of all combinations of control signal values ​​is most convenient in a 'for' type cycle, in which we treat the value combination as a binary number that we initialize zero at the beginning of the test sequence and then we increment it at each iteration. The instruction 'for' has in Verilog the same format as in C or in Java:

<syntaxhighlight lang="verilog">

   for (initializations, stop condition, iteration end assignments)
       block of instructions

<syntaxhighlight>

such as

<syntaxhighlight lang="verilog">

   for (index=0; index <8; index=index + 1)
   begin
       {clear, load, enable}=index;
       repeat (16) @ (posedge clk);
   end

<syntaxhighlight>

We can control the loop control for an integer index that increments iteration with iteration to the desired value. In the test module, we can declare and use integer variables as in programming languages, but these variables are not signals:

<syntaxhighlight lang="verilog">

   integer index;

<syntaxhighlight>

An iteration of the 'for' loop above takes 16 clock periods for each combination of values ​​tested. Observe the compact mode that assigns values ​​to the three signals. Instead of individual assignments, the three signals were grouped into a 3-bit vector which is then assigned a full value. The last three bits of the binary representation of the value of the 'index' variable will be assigned to the three signals. Grouping of signals in Verilog is possible thanks to the concatenation operator:

<syntaxhighlight lang="verilog">

   {clear, load, enable}

<syntaxhighlight>

The number of bits of the new variable is the sum of the bits of the grouped variables, and the bits are arranged in the order of concatenation of the variables. In our case, 'clear' is the significant bit (MSB) of the control signal vector.

Example 4

Using the previous counter, perform a controllable frequency divider, the divider being selectable so that the output frequency is 1/2, 1/4, 1/8 or 1/16 of the clock frequency.

At the instantiation of the counting module, make sure that all inputs are connected either to a signal generated in the test mode, either to the mass (at 0) or to the feed (at value 1). Only those inputs that do not affect the operation of the circuit can be left unconnected ("empty" or "floating"). In our example, the 'target' entry can be neglected because the numerator will never load. In general, however, unconnected entries can result in erroneous results due to their undefined value (X). A rule that relieves you of surprising simulations and syntheses:

Do not leave unconnected entries!

Homework

Implement a pulse generator with controlled frequency and fill factor. The generated signal period will be equal to a multiple of the clock, Tw=Tclk * tw, and the duration of the pulse is controlled by another input, Tp=Tclk * tp.

  1. using the numerator of Example 3;
  2. using a purely behavioral description;