CID aplicatii 9 : Numaratorul

De la WikiLabs

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:

Numarator cu reset.png

Î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).

Divizare frecventa.png

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 clock,
    input reset_n,
    output reg [WIDTH-1:0] count  
);

always@(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;
reg reset_n_t, clock_t;
wire [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 Verilog a circuitului

module clock_generator(
    input clock,
    input reset,
    output clkout1,
    output clkout2,
    output clkout4,
    output clkout8,
);

reg [26:0] count;

always@(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.

Cid lab6.jpg

Implementarea modulului ROM

module ROM(
    input [3:0] in,
    output reg [3:0] out
);

always @(in)
    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 -> ...)