Diferență între revizuiri ale paginii „CID aplicatii 9 : Numaratorul”
(Nu s-au afișat 2 versiuni intermediare efectuate de alți 2 utilizatori) | |||
Linia 29: | Linia 29: | ||
#(parameter WIDTH = 8) | #(parameter WIDTH = 8) | ||
( | ( | ||
− | input clock, | + | input logic clock, |
− | input reset_n, | + | input logic reset_n, |
− | output | + | output logic [WIDTH-1:0] count |
); | ); | ||
− | + | always_ff @(posedge clock) begin | |
if(reset_n == 0) | if(reset_n == 0) | ||
count <= 0; | count <= 0; | ||
Linia 52: | Linia 52: | ||
parameter WIDTH_T = 5; | parameter WIDTH_T = 5; | ||
− | + | logic reset_n_t, clock_t; | |
− | + | logic [WIDTH_T-1:0] count_t; | |
initial begin | initial begin | ||
Linia 85: | Linia 85: | ||
* ''clkout8'': semnal de ceas cu frecvența de 8 Hz. | * ''clkout8'': semnal de ceas cu frecvența de 8 Hz. | ||
− | Dacă aplicăm formula '''''f<sub>bN</sub>'' = ''f<sub>clk</sub> / 2<sup>N+1</sup>''''' cunoscând că frecvența de ceas a FPGA-ului este de | + | Dacă aplicăm formula '''''f<sub>bN</sub>'' = ''f<sub>clk</sub> / 2<sup>N+1</sup>''''' cunoscând că frecvența de ceas a FPGA-ului este de 100 MHz, vom obține: |
* ''clkout1'' poate fi obținut prin bitul 26 al unui numărător. | * ''clkout1'' poate fi obținut prin bitul 26 al unui numărător. | ||
* ''clkout2'' poate fi obținut prin bitul 25 al unui numărător. | * ''clkout2'' poate fi obținut prin bitul 25 al unui numărător. | ||
Linia 93: | Linia 93: | ||
Din rezultatele de mai sus, rezultă că avem nevoie de un numărător pe 27 biți, astfel încât să putem folosi bitul cel mai semnificativ (bitul 26) pentru generarea semnalului de ceas cu frecvența cea mai mică. | Din rezultatele de mai sus, rezultă că avem nevoie de un numărător pe 27 biți, astfel încât să putem folosi bitul cel mai semnificativ (bitul 26) pentru generarea semnalului de ceas cu frecvența cea mai mică. | ||
− | '''Implementarea | + | '''Implementarea SystemVerilog a circuitului''' |
<syntaxhighlight lang="Verilog"> | <syntaxhighlight lang="Verilog"> | ||
module clock_generator( | module clock_generator( | ||
− | input clock, | + | input logic clock, |
− | input reset, | + | input logic reset, |
− | output clkout1, | + | output logic clkout1, |
− | output clkout2, | + | output logic clkout2, |
− | output clkout4, | + | output logic clkout4, |
− | output clkout8, | + | output logic clkout8, |
); | ); | ||
− | + | logic [26:0] count; | |
− | + | always_ff @(posedge clock) begin | |
if(reset == 1) | if(reset == 1) | ||
count <= 0; | count <= 0; | ||
Linia 168: | Linia 168: | ||
<syntaxhighlight lang="Verilog"> | <syntaxhighlight lang="Verilog"> | ||
module ROM( | module ROM( | ||
− | input [3:0] in, | + | input logic [3:0] in, |
− | output | + | output logic [3:0] out |
); | ); | ||
− | + | always_comb | |
case(in) | case(in) | ||
0: out = 4'b0000; | 0: out = 4'b0000; |
Versiunea curentă din 22 octombrie 2024 13:57
Numărătorul
Numărătorul este cel mai simplu automat, fiind format dintr-un registru (element de memorare) ce reține valoarea curentă a acestuia și un circuit de incrementare (circuit de reacție) ce generează la ieșirea sa valoarea ce va trebui stocată în registru la următorul front crescător de ceas (valoarea curentă incrementată cu 1).
În figura de mai jos este reprezentat un numărător cu reset sincron:
În cazul de mai sus, cât timp semnalul reset este activ, valoarea numărătorului va fi 0. Atunci când semnalul reset nu este activ, numărătorul își va incrementa valoarea la fiecare front crescător de ceas.
Divizor de frecvență cu factor de divizare putere a lui 2
În unele cazuri, apare necesitatea obținerii unor semnale de ceas cu frecvență mai mică decât cea a ceasului de sistem. Aceste noi semnale de ceas pot fi obținute chiar din semnalul de ceas al sistemului, prin circuite care realizează divizarea acestuia. Cea mai simplă divizare a unui semnal de ceas este cea cu factor de divizare egal cu o putere a lui 2. Pentru acest caz, putem folosi un numărător.
Dacă urmărim figura de mai jos în care sunt reprezentate variațiile fiecărui bit în timpul funcționării unui numărător pe 4 biți, observăm că, cu cât ordinul bitului crește (este mai semnificativ), frecvența de variație este mai mică. Între frecvențele de variație corespunzătoare a doi biți succesivi există următoarea relație: frecvența unui bit este de două ori mai mică decât frecvența bitului anterior (mai puțin semnificativ).
Observând că frecvența bitului 0 este de două ori mai mică decât cea a ceasului, putem concluziona că frecvența de variație a bitului cu indicele N este dată de: fbN = fclk / 2N+1
Așadar, pentru a realiza un divizor de frecvență cu factor de divizare putere a lui 2, putem folosi un numărător îndeajuns de mare, din care extragem bitul cu frecvența de variație egală cu frecvența dorită.
Exemple
Exemplul 1: Implementarea unui numărător cu reset sincron
În acest exemplu vom realiza implementarea numărătorului cu reset sincron prezentat în figura din secțiunea de introducere teoretică. În plus, vom folosi parametrul WIDTH pentru a controla dimensiunea acestuia.
Implementarea Numărătorului cu reset sincron
module Numarator
#(parameter WIDTH = 8)
(
input logic clock,
input logic reset_n,
output logic [WIDTH-1:0] count
);
always_ff @(posedge clock) begin
if(reset_n == 0)
count <= 0;
else
count <= count + 1;
end
endmodule
Implementarea unui modul de test pentru numărător
`timescale 1ns/1ps
module Numarator_TB();
parameter WIDTH_T = 5;
logic reset_n_t, clock_t;
logic [WIDTH_T-1:0] count_t;
initial begin
clock_t = 0;
forever #1 clock_t = ~clock_t;
end
initial begin
reset_n_t = 0;
#2 reset_n_t = 1;
#500 $stop();
end
Numarator #(.WIDTH(WIDTH_T)) DUT(
.clock(clock_t),
.reset_n(reset_n_t),
.count(count_t)
);
endmodule
Observație: Cât timp semnalul reset este inactiv, numărătorul își va incrementa valoarea la fiecare front crescător de ceas. Când va ajunge la valoarea sa maximă (de exemplu, 31 pentru un numărător pe 5 biți), va avea loc o depășire și având la dispozitie un număr limitat de biți (de exemplu, 5), semnalul de ieșire va deveni 0 (reținem doar cei mai puțin semnificativi 5 biți), circuitul reluând numărarea de la capat.
Exemplul 2: Observarea vitezei de variație a biților unui numărător
În acest exemplu vom realiza implementarea unui divizor de frecvență ce generează la ieșire 4 semnale de ceas cu următoarele frecvențe:
- clkout1: semnal de ceas cu frecvența de 1 Hz.
- clkout2: semnal de ceas cu frecvența de 2 Hz.
- clkout4: semnal de ceas cu frecvența de 4 Hz.
- clkout8: semnal de ceas cu frecvența de 8 Hz.
Dacă aplicăm formula fbN = fclk / 2N+1 cunoscând că frecvența de ceas a FPGA-ului este de 100 MHz, vom obține:
- clkout1 poate fi obținut prin bitul 26 al unui numărător.
- clkout2 poate fi obținut prin bitul 25 al unui numărător.
- clkout4 poate fi obținut prin bitul 24 al unui numărător.
- clkout8 poate fi obținut prin bitul 23 al unui numărător.
Din rezultatele de mai sus, rezultă că avem nevoie de un numărător pe 27 biți, astfel încât să putem folosi bitul cel mai semnificativ (bitul 26) pentru generarea semnalului de ceas cu frecvența cea mai mică.
Implementarea SystemVerilog a circuitului
module clock_generator(
input logic clock,
input logic reset,
output logic clkout1,
output logic clkout2,
output logic clkout4,
output logic clkout8,
);
logic [26:0] count;
always_ff @(posedge clock) begin
if(reset == 1)
count <= 0;
else
count <= count + 1;
end
assign clkout1 = count[26];
assign clkout2 = count[25];
assign clkout4 = count[24];
assign clkout8 = count[23];
endmodule
Observație: Cerința ca reset să fie activ în 1 este impusă de logica în care lucrează butoanele FPGA-ului.
Implementarea circuitului pe FPGA
Realizați sinteza circuitului pe FPGA, ținând cont de următoarele constrângeri de I/O:
Port | Conexiune |
clock | CLK_100MHz |
reset | Button 0 |
clkout1 | LED3 |
clkout2 | LED2 |
clkout4 | LED1 |
clkout8 | LED0 |
Observați viteza cu care fiecare din LED-uri se stinge și se aprinde.
Exerciții
Exercițiul 1
Implementați un numărător parametrizat cu reset sincron și semnal de enable. Semnalul enable controlează funcționarea numărătorului astfel: când este activ (egal cu 1), permite funcționarea normală (incrementare la fiecare front crescător de ceas); când este inactiv (egal cu 0), va determina numărătorul să își păstreze valoarea curentă.
Implementați și un modul de test care să testeze funcționarea corectă a circuitului.
Exercițiul 2
Implementați un numărător parametrizat cu reset sincron și capacitatea de a număra crescător sau descrescător. Controlul direcției de numărare se va realiza cu ajutorul unui semnal count_updown, care controlează funcționarea numărătorului astfel: când este 0, numărătorul va număra crescător, prin incrementarea valorii curente, iar când este 1, numărătorul va număra descrescător, prin decrementarea valorii curente
Implementați și un modul de test care să testeze funcționarea corectă a circuitului.
Exercițiul 3
Implementați circuitul descris prin schema de mai jos, știind că:
- COUNTER este un numărător pe 32 biți fără reset.
- RAM este o memorie 16x4b, cu citire sincrona.
- ROM este o memorie 16x4b, ce are descrierea prezentată mai jos.
Implementarea modulului ROM
module ROM(
input logic [3:0] in,
output logic [3:0] out
);
always_comb
case(in)
0: out = 4'b0000;
1: out = 4'b0110;
2: out = 4'b0011;
3: out = 4'b1110;
4: out = 4'b1011;
5: out = 4'b1111;
6: out = 4'b0111;
7: out = 4'b1100;
8: out = 4'b0001;
9: out = 4'b0101;
10: out = 4'b1101;
11: out = 4'b1010;
12: out = 4'b0010;
13: out = 4'b0100;
14: out = 4'b1000;
15: out = 4'b1001;
default: out = 4'b0000;
endcase
endmodule
Realizați un modul de test în care să scrieți memoria RAM cu date, astfel încât să se realizeze citirea memoriei ROM în ordine, la fiecare ciclu de ceas (adresa 0 -> adresa 1 -> ... -> adresa 15 -> adresa 0 -> ...)