CID Aplicatii 11
1. Memoria FIFO
Memoria FIFO este o structura de memorare ce respecta regula First-In First-Out. Asadar, din aceasta memorie se va citi mereu cea mai veche data scrisa. Structura interna a acestui circuit este prezentata in figura urmatoare.
Dupa cum se poate observa, nu mai este nevoie de magistrale de adrese pentru scriere si pentru citire. Acestea sunt calculate intern cu ajutorul unor numaratoare cu enable (WRITE_PTR si READ_PTR). Pentru usurinta intelegerii, putem privi FIFO ca o memorie circulara, cu adrese de citire si scriere ce se „urmaresc” una pe cealalta. Memoria tinde spre „umplere” prin scrieri si spre „golire” prin citiri:
FIFO descris mai sus are trei semnale de feedback:
- fifo_counter, care ne spune cate locatii de memorie din cele totale sunt ocupate.
- empty, activ atunci cand memoria este goala (0 locatii de memorie ocupate)
- full, activ atunci cand toate locatiile sunt ocupate. Citirea este blocata din momentul in care full devine activ pana se elibereaza locatii prin citire.
Semnalele write_enable si read_enable care sunt transmise modulului din exterior nu pot ajunge direct la memorie deoarece trebuie impiedicate scrierile sau citirile eronate: o citire eronata apare atunci cand se incearca citirea dintr-o memorie goala, iar o scriere eronata apare atunci cand se incearca scrierea intr-o memorie complet ocupata. De aceea, semnalul real de comanda a scrierii (wr_en_mem) este activ doar daca se declanseaza o scriere si memoria nu este plina, iar semnalul real de citire (rd_en_mem) este activ doar daca se declanseaza o citire dintr-o memorie care nu este goala.
Principalele blocuri ale memoriei FIFO sunt:
- RAMDP: memorie dual-port, cu citire sincrona. Acesta este elementul de memorare efectiva.
- WRITE_PTR: numarator ce incrementeaza adresa de scriere, daca apare o scriere valida (wr_en_mem este 1).
- READ_PTR: numarator ce incrementeaza adresa de citire, daca apare o citire valida (rd_en_mem este 1).
- FIFO_COUNTER: numarator ce contorizeaza locatiile ocupate: se incrementeaza daca are loc o scriere fara citire (wr_en_mem este 1 si rd_en_mem este 0) si se decrementeaza daca are loc o citire fara scriere (rd_en_mem este 1 si wr_en_mem este 0).
- CONTROL: modul combinational care genereaza semnalele de control: empty, full, wr_en_mem si rd_en_mem, cu functionalitatile descrise anterior.
Implementarea memoriei RAMDP
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 memoriei FIFO
module FIFO
#( parameter DATA_W = 8, ADDR_W = 5)
(
input clock, reset, // semnal de ceas si reset
input write_enable, // comanda de scriere
input read_enable, // comanda de citire
input [DATA_W-1 : 0] data_in, // date de intrare
output [DATA_W-1 : 0] data_out, // date de iesire
output empty, // semnal activ cand memoria este goala
output full, // semnal activ cand memoria este plina
output reg [ADDR_W:0] fifo_counter //nr locatii folosite
);
wire rd_en_mem, wr_en_mem;
reg [ADDR_W-1:0] write_ptr_reg, read_ptr_reg;
//WRITE POINTER
always@(posedge clock) begin
if(reset == 0)
write_ptr_reg <= 0;
else if (wr_en_mem == 1)
write_ptr_reg <= write_ptr_reg + 1;
end
//READ POINTER
always@(posedge clock) begin
if(reset == 0)
read_ptr_reg <= 0;
else if (rd_en_mem == 1)
read_ptr_reg <= read_ptr_reg + 1;
end
//FIFO COUNTER
always@(posedge clock) begin
if(reset == 0)
fifo_counter <= 0;
else if (rd_en_mem && !wr_en_mem)
fifo_counter <= fifo_counter - 1;
else if (!rd_en_mem && wr_en_mem)
fifo_counter <= fifo_counter + 1;
end
//READ_ENABLE AND WRITE_ENABLE
assign wr_en_mem = write_enable & ~full;
assign rd_en_mem = read_enable & ~empty;
//FULL AND EMPTY
assign full = (fifo_counter == 2**ADDR_W);
assign empty = (fifo_counter == 0);
RAMDP #(.DATA_W(DATA_W), .ADDR_W(ADDR_W)) MEM(
.clock(clock),
.read_enable(rd_en_mem),
.write_enable(wr_en_mem),
.addr_read(read_ptr_reg),
.addr_write(write_ptr_reg),
.data_in(data_in),
.data_out(data_out)
);
endmodule
Implementarea unui test bench pentru memoria FIFO
`timescale 1ns/1ps
module FIFO_TB;
parameter DATA_W_T = 8;
parameter ADDR_W_T = 5;
reg clock_t, reset_t;
reg write_enable_t, read_enable_t;
reg [DATA_W_T-1 : 0] data_in_t;
wire [DATA_W_T-1 : 0] data_out_t;
wire empty_t, full_t;
wire [ADDR_W_T:0] fifo_counter_t;
integer i;
integer j;
initial begin
reset_t = 0;
#2 reset_t = 1;
read_enable_t = 0;
for(j = 0; j<= 2**ADDR_W_T; j = j + 1) begin //umplem complet FIFO. La final, semnalul full va deveni HIGH
#2;
data_in_t = j;
write_enable_t = 1;
end
#2 read_enable_t = 1;
write_enable_t = 0; //pornim citirea pana cand FIFO se va goli complet, iar semnalul empty va deveni HIGH
#80 $stop();
end
initial begin
clock_t = 0;
forever #1 clock_t = ~clock_t;
end
FIFO #(.DATA_W(DATA_W_T), .ADDR_W(ADDR_W_T)) DUT(
.clock(clock_t),
.reset(reset_t),
.write_enable(write_enable_t),
.read_enable(read_enable_t),
.data_in(data_in_t),
.data_out(data_out_t),
.empty(empty_t),
.full(full_t),
.fifo_counter(fifo_counter_t)
);
endmodule
2. Stiva
Stiva este o structura de memorie care se supune regulii LIFO (Last-In First-Out). Asadar, data citita din stiva va fi ultima data introdusa.
Introducerea elementelor in stiva se face prin operatia push, iar extragerea lor prin pop. Asadar, vom avea doua semnale care vor comanda scrierea, respectiv citirea: push si pop. Considerand structura stivei, cele doua operatii nu vor avea loc in acelasi timp, spre deosebire de FIFO. Umplerea si golirea completa a stivei vor fi semnalizate cu ajutorul semnalelor full, respectiv empty.
Deoarece scrierea si citirea sunt ambele legate de varful stivei, este nevoie de o singura adresa pentru acestea: adresa varfului stivei (ptr_reg). Aceasta se va incrementa atunci cand un element va fi introdus in varful stivei si se va descrementa atunci cand un element va fi extras din stiva. Atat scrierea cat si citirea sunt sincrone si au loc atunci cand se comanda o operatie de push si stiva nu este plina, respectiv atunci cand se comanda o operatie de pop si stiva nu este goala.
Implementarea modulului Stack
module STACK
#( parameter DATA_W = 8, ADDR_W = 5)
(
input clock, reset, // semnal de ceas si reset
input push, // comanda de scriere
input pop, // comanda de citire
input [DATA_W-1 : 0] data_in, // date de intrare
output reg [DATA_W-1 : 0] data_out, // date de iesire
output empty, // semnal activ cand memoria este goala
output full // semnal activ cand memoria este plina
);
wire rd_en_mem, wr_en_mem;
reg [ADDR_W:0] ptr_reg;
reg [DATA_W-1:0] stack [0:2**ADDR_W - 1];
//WRITE POINTER
always@(posedge clock) begin
if(reset == 0)
ptr_reg <= 0;
else if (wr_en_mem == 1)
ptr_reg <= ptr_reg + 1;
else if (rd_en_mem == 1)
ptr_reg <= ptr_reg - 1;
end
//READ_ENABLE AND WRITE_ENABLE
assign wr_en_mem = push & ~full;
assign rd_en_mem = pop & ~empty;
//FULL AND EMPTY
assign full = (ptr_reg == 2**ADDR_W);
assign empty = (ptr_reg == 0);
always@(posedge clock) begin
if (wr_en_mem == 1)
stack[ptr_reg] <= data_in;
if (rd_en_mem == 1)
data_out <= stack[ptr_reg-1];
end
endmodule
Implementarea unui test bench pentru modulul Stack
`timescale 1ns/1ps
module STACK_TB;
parameter DATA_W_T = 8;
parameter ADDR_W_T = 5;
reg clock_t, reset_t;
reg push_t, pop_t;
reg [DATA_W_T-1 : 0] data_in_t;
wire [DATA_W_T-1 : 0] data_out_t;
wire empty_t, full_t;
integer i;
integer j;
initial begin
reset_t = 0;
push_t = 0;
pop_t = 0;
#2;
//umplerea completa a stivei
reset_t = 1;
push_t = 1;
for(j = 0; j<= 2**ADDR_W_T; j = j + 1) begin
data_in_t = j;
#2;
end
//golirea completa a stivei
push_t = 0;
pop_t = 1;
#80;
//intraoducerea a 5 elemente in stiva
push_t = 1;
pop_t = 0;
for(j = 0; j<= 4; j = j + 1) begin
data_in_t = j*2;
#2;
end
//extragerea celor 5 elemente din stiva
push_t = 0;
pop_t = 1;
#80;
$stop();
end
initial begin
clock_t = 0;
forever #1 clock_t = ~clock_t;
end
STACK #(.DATA_W(DATA_W_T), .ADDR_W(ADDR_W_T)) DUT(
.clock(clock_t),
.reset(reset_t),
.push(push_t),
.pop(pop_t),
.data_in(data_in_t),
.data_out(data_out_t),
.empty(empty_t),
.full(full_t)
);
endmodule