CID Aplicatii 8

De la WikiLabs
Versiunea din 12 aprilie 2021 12:42, autor: Gvpopescu (discuție | contribuții) (Pagină nouă: ==1. Registrul== Pentru a stoca date cu o dimensiune mai mare de un bit, putem folosi registre. Un registru este o colectie de bistabili ce lucreaza in paralel, folosind acelasi se...)
(dif) ← Versiunea anterioară | Versiunea curentă (dif) | Versiunea următoare → (dif)
Jump to navigationJump to search

1. Registrul

Pentru a stoca date cu o dimensiune mai mare de un bit, putem folosi registre. Un registru este o colectie de bistabili ce lucreaza in paralel, folosind acelasi semnal de ceas. Cu alte cuvinte, vom avea cate un bistabil pentru fiecare bit ce trebuie stocat.

Structura registrelor, ca si cea a bistabilelor, poate diferi usor in functie de ce semnale de control dorim sa accepte. In urmatorul exemplu, vom avea un registru ce accepta atat un semnal de reset sincron prin care continutul acestuia va deveni 0, cat si un semnal de write enable, prin care dorim sa controlam procesul de scriere. Asadar, vom avea o conditie in plus: pentru a efectua o scriere in momentul aparitiei evenimentului de front crescator de ceas, va fi nevoie ca semnalul de reset sa fie inactiv si ca semnalul write_enable sa fie activ. Vom considera in exemplul nostru semnalul reset activ in 0 si semnalul write_enable activ in 1.

Semnalul de activare a scrierii poate bloca scrierea in doua moduri: fie este folosit pentru a bloca ceasul atunci cand este activ (clock gating), fie este folosit impreuna cu un multiplexor pentru a selecta intre data de intrare si cea memorata anterior pentru a face scrierea curenta. In urmatorul exemplu vom folosi a doua implementare, prezentata in schema de mai jos.

Register.png

Implementarea registrului cu reset si write_enable

module register
#(parameter WIDTH = 8)
(
    input [WIDTH-1:0] data_in,
    input clock,
    input reset,
    input we,
    output reg [WIDTH-1:0] data_out
);

always@(posedge clock) begin
    if(reset == 0)
        data_out <= 0;
    else if(we == 1)
        data_out <= data_in;
    else
        data_out <= data_out;
end

endmodule

Implementarea modulului de test

`timescale 1ns/1ps

module register_TB();

parameter WIDTH_T = 8;

reg [WIDTH_T-1:0] data_in_t;
reg clock_t, reset_t, we_t;
wire [WIDTH_T-1:0] data_out_t;

initial begin
       reset_t = 0;
    #2 reset_t = 1;
       we_t = 1;
       data_in_t = 8'h01;
    #2 data_in_t = 8'h02;
    #2 data_in_t = 8'h03;
       we_t = 0;
    #5 $stop();
end

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

register #(.WIDTH(WIDTH_T)) DUT(
    .data_in(data_in_t),
    .reset(reset_t),
    .clock(clock_t),
    .we(we_t),
    .data_out(data_out_t)
);

endmodule

Observatie: Parametrul WIDTH permite generalizarea dimensiunii registrului. Folosind aceeasi descriere de mai sus, dar folosind o alta valoare a parametrului la instantiere, putem obtine registre de orice dimensiune.

2. Memoria RAM

O memorie RAM este o colectie de celule de memorie organizate sub forma unei matrice. In general, partea de memorare efectiva a RAM este implementata cu latch-uri, datele si adresele fiind distribuite cu ajutorul altor elemente combinationale: multiplexoare, demultiplexoare, etc.

Indiferent de modul de implementare al acesteia, comportamentul unei memorii RAM este urmatorul: scrierea se va face mereu pe frontul crescator al ceasului, folosind pentru identificarea locatiei de memorie al carui continut va fi modificat, o adresa. Asadar, la fiecare front crescator de ceas, daca semnalul write_enable este activ, se va scrie data de intrare la adresa specificata pe portul de adrese. Pentru citire, avem doua posibilitati: daca memoria RAM este cu citire sincrona, la fiecare front crescator al ceasului pentru care read_enable este activ, vom citi continutul locatiei de memorie a carei adresa este specificata pe portul de adrese, iar daca citirea este asincrona, iesirea va fi modificata imediat, cu continutul locatiei de memorie a carei adresa este specificata pe portul de adrese.

Exista, asadar, multe modalitati de a implementa o memorie RAM. Noi ne vom ocupa in acest exemplu de o descriere comportamentala a unei memorii RAM cu porturi separate pentru datele si adresele de scriere si citire. Citirea se va face sincron.

RAM.png

Implementarea memoriei RAM dual-port cu porturi de scriere si citire sincrone

module RAMDP
#( parameter DATA_W = 8, ADDR_W = 5)
(
    input clock,
    input read_enable,
    input write_enable,
    input [ADDR_W-1:0] addr_read,
    input [ADDR_W-1:0] addr_write,
    input [DATA_W-1:0] data_in,
    output reg [DATA_W-1:0] data_out
);

reg [DATA_W-1:0] memory [0:2**ADDR_W-1];

always@(posedge clock) begin
    if(write_enable)
        memory[addr_write] <= data_in;
    if(read_enable)
        data_out <= memory[addr_read];
end

endmodule

Implementarea modulului de test

`timescale 1ns/1ps

module RAMDP_TB();

parameter DATA_W_T = 8;
parameter ADDR_W_T = 5;

reg clock_t, read_enable_t, write_enable_t;
reg [ADDR_W_T-1:0] addr_read_t, addr_write_t;
reg [DATA_W_T-1:0] data_in_t;
wire [DATA_W_T-1:0] data_out_t;

integer idx;

initial begin
       read_enable_t = 0;
       write_enable_t = 1;
       for(idx = 0; idx < 2**ADDR_W_T; idx = idx + 1) begin
           #2;
           data_in_t = idx;
           addr_write_t = idx;
       end
    #2 read_enable_t = 1;
       write_enable_t = 0;
       for(idx = 0; idx < 2**ADDR_W_T; idx = idx + 1) begin
           #2;
           addr_read_t = idx;
       end
    #10 $stop();
end

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

RAMDP #(.DATA_W(DATA_W_T), .ADDR_W(ADDR_W_T)) DUT (
    .clock(clock_t),
    .read_enable(read_enable_t),
    .write_enable(write_enable_t),
    .addr_read(addr_read_t),
    .addr_write(addr_write_t),
    .data_in(data_in_t),
    .data_out(data_out_t)
);

endmodule