FIFO (First-In, First-Out)
: 데이터가 입력된 순서대로 출력되는 queue 구조를 가진 메모리 관리 방식이다. read 하는 속도와 write 하는 속도가 다를 때 일반적으로 사용한다.
- First-In, First Out : 가장 먼저 입력된 데이터가 가장 먼저 출력되는 방식이다.
- Queue : 한쪽 끝에서는 데이터를 입력(enqueue)하고 반대쪽 끝에서는 데이터를 출력(dequeue)한다.
1. 'A' 입력 psuh
2. 'B' 입력 push
3. 'C' 입력 push
4. 'D' 입력 push → full
5. pop 출력
6. pop 출력
7. pop 출력
8. pop 출력 → empty
1. 초기상태 | wr_ptr == rd_ptr, empty = 1, full = 0 |
2. 'A' push mem[wr_ptr] = 'A'; wr_ptr++; |
wr_ptr != rd_ptr, empty = 0, full = 0 (memory에 data가 남아있다.) |
3. A, B, C, D push 2번 반복 |
wr_ptr == rd_ptr, empty = 0, full = 1 (push 할 때 wr_ptr == rd_ptr이면 full = 1 상태) |
4. pop read rdata = mem[rd_ptr]; rd_ptr ++; |
wr_ptr != rd_ptr, empty = 0, full = 0 (memory에 data가 남아있다.) |
5. A, B, C, D, pop read 4번 반복 |
wr_ptr == rd_ptr, empty = 1, full = 0 (pop할 때 wr_ptr == rd_ptr이면 empty = 1 상태) |
Full : Memory Full
Empty : Memory Empty
fifo.v
`timescale 1ns / 1ps
module fifo #(
parameter ADDR_WIDTH = 3,
DATA_WIDTH = 8
) (
input clk,
input reset,
input wr_en,
output full,
input [DATA_WIDTH - 1:0] wdata,
input rd_en,
output empty,
output [DATA_WIDTH - 1:0] rdata
);
wire w_full;
wire [ADDR_WIDTH-1:0] w_waddr, w_raddr;
register_file #(
.ADDR_WIDTH(ADDR_WIDTH),
.DATA_WIDTH(DATA_WIDTH)
) U_RegFile (
.clk (clk),
.reset(reset),
.wr_en(wr_en & ~full),
.waddr(w_waddr),
.wdata(wdata),
.rd_en(rd_en & ~empty),
.raddr(w_raddr),
.rdata(rdata)
);
fifo_control_unit #(
.ADDR_WIDTH(ADDR_WIDTH)
) U_FIFO_CU (
.clk (clk),
.reset(reset),
.wr_en(wr_en),
.full (full),
.waddr(w_waddr),
.rd_en(rd_en),
.empty(empty),
.raddr(w_raddr)
);
endmodule
module register_file #(
parameter ADDR_WIDTH = 3,
DATA_WIDTH = 8
) (
input clk,
input reset,
input rd_en,
input wr_en,
input [ADDR_WIDTH - 1:0] waddr,
input [DATA_WIDTH - 1:0] wdata,
input [ADDR_WIDTH - 1:0] raddr,
output [DATA_WIDTH - 1:0] rdata
);
reg [DATA_WIDTH-1:0] mem[0:2**ADDR_WIDTH-1];
always @(posedge clk) begin
if (wr_en) mem[waddr] <= wdata;
end
assign rdata = rd_en? mem[raddr] : 8'bz;
endmodule
module fifo_control_unit #(
parameter ADDR_WIDTH = 3
) (
input clk,
input reset,
input wr_en,
output full,
output [ADDR_WIDTH-1:0] waddr,
input rd_en,
output empty,
output [ADDR_WIDTH-1:0] raddr
);
reg [ADDR_WIDTH-1:0] wr_ptr_reg, wr_ptr_next;
reg [ADDR_WIDTH-1:0] rd_ptr_reg, rd_ptr_next;
reg full_reg, full_next, empty_reg, empty_next;
assign waddr = wr_ptr_reg;
assign raddr = rd_ptr_reg;
assign full = full_reg;
assign empty = empty_reg;
always @(posedge clk, posedge reset) begin
if (reset) begin
wr_ptr_reg <= 0;
rd_ptr_reg <= 0;
full_reg <= 1'b0;
empty_reg <= 1'b1;
end else begin
wr_ptr_reg <= wr_ptr_next;
rd_ptr_reg <= rd_ptr_next;
full_reg <= full_next;
empty_reg <= empty_next;
end
end
always @(*) begin
wr_ptr_next = wr_ptr_reg;
rd_ptr_next = rd_ptr_reg;
full_next = full_reg;
empty_next = empty_reg;
case ({
wr_en, rd_en
})
2'b01: begin // read
if (!empty_reg) begin
full_next = 1'b0;
rd_ptr_next = rd_ptr_reg + 1;
if (rd_ptr_next == wr_ptr_reg) begin
empty_next = 1'b1;
end
end
end
2'b10: begin // write
if (!full_reg) begin
empty_next = 1'b0;
wr_ptr_next = wr_ptr_reg + 1;
if (wr_ptr_next == rd_ptr_reg) begin
full_next = 1'b1;
end
end
end
2'b11: begin // write, read
if (empty_reg) begin
wr_ptr_next = wr_ptr_reg;
rd_ptr_next = rd_ptr_reg;
end else begin
wr_ptr_next = wr_ptr_reg + 1;
rd_ptr_next = rd_ptr_reg + 1;
end
end
endcase
end
endmodule
transaction.sv
`ifndef __TRANSACTION_SV_
`define __TRANSACTION_SV_
class transaction;
rand bit wr_en;
bit full;
rand bit [7:0] wdata;
rand bit rd_en;
bit empty;
bit [7:0] rdata;
constraint c_oper {wr_en != rd_en;} // read, write
constraint c_wr_en {
wr_en dist {
1 :/ 50,
0 :/ 50
};
} // 50%, 50%
task display(string name);
$display(
"[%s] wr_en:%x , wdata:%x, full:%x || rd_en:%x, rdata:%x, empty:%x",
name, wr_en, wdata, full, rd_en, rdata, empty);
endtask
endclass //transaction
`endif
interface.sv
`ifndef __INTERFACE_SV_
`define __INTERFACE_SV_
interface fifo_interface;
logic clk;
logic reset;
logic wr_en;
logic full;
logic [7:0] wdata;
logic rd_en;
logic empty;
logic [7:0] rdata;
endinterface //fifo_interface
`endif
generator.sv
`include "transaction.sv"
class generator;
transaction trans;
mailbox #(transaction) gen2drv_mbox;
event gen_next_event;
function new(mailbox #(transaction) gen2drv_mbox, event gen_next_event);
this.gen2drv_mbox = gen2drv_mbox;
this.gen_next_event = gen_next_event;
endfunction
task run(int count);
repeat(count) begin
trans = new();
assert (trans.randomize())
else $error("[GEN] trans.randomize() error!");
gen2drv_mbox.put(trans);
trans.display("GEN");
@(gen_next_event);
end
endtask
endclass //generator
driver.sv
`include "transaction.sv"
`include "interface.sv"
class driver;
transaction trans;
mailbox #(transaction) gen2drv_mbox;
virtual fifo_interface fifo_intf;
function new(virtual fifo_interface fifo_intf,
mailbox#(transaction) gen2drv_mbox);
this.fifo_intf = fifo_intf;
this.gen2drv_mbox = gen2drv_mbox;
endfunction //new()
task reset();
fifo_intf.reset <= 1'b1;
fifo_intf.wr_en <= 1'b0;
fifo_intf.wdata <= 0;
fifo_intf.rd_en <= 1'b0;
repeat(5) @(posedge fifo_intf.clk);
fifo_intf.reset <= 1'b0;
endtask
task run();
forever begin
gen2drv_mbox.get(trans);
fifo_intf.wr_en <= trans.wr_en;
fifo_intf.wdata <= trans.wdata;
fifo_intf.rd_en <= trans.rd_en;
trans.display("DRV");
@(posedge fifo_intf.clk);
end
endtask
endclass //driver
monitor.sv
`include "interface.sv"
`include "transaction.sv"
class monitor;
virtual fifo_interface fifo_intf;
mailbox #(transaction) mon2scb_mbox;
transaction trans;
function new(virtual fifo_interface fifo_intf,
mailbox#(transaction) mon2scb_mbox);
this.fifo_intf = fifo_intf;
this.mon2scb_mbox = mon2scb_mbox;
endfunction //new()
task run();
forever begin
trans = new();
#1;
trans.wr_en = fifo_intf.wr_en;
trans.wdata = fifo_intf.wdata;
trans.rd_en = fifo_intf.rd_en;
trans.rdata = fifo_intf.rdata;
@(posedge fifo_intf.clk);
trans.full = fifo_intf.full;
trans.empty = fifo_intf.empty;
mon2scb_mbox.put(trans);
trans.display("MON");
end
endtask
endclass //monitor
scoreboard
`include "transaction.sv"
class scoreboard;
transaction trans;
mailbox #(transaction) mon2scb_mbox;
event gen_next_event;
int total_cnt, pass_cnt, fail_cnt, write_cnt;
reg [7:0] scb_fifo[$:8]; // $ : Queue (fifo), [$] = infinite size, golden reference
reg [7:0] scb_fifo_data;
function new(mailbox#(transaction) mon2scb_mbox, event gen_next_event);
this.mon2scb_mbox = mon2scb_mbox;
this.gen_next_event = gen_next_event;
total_cnt = 0;
pass_cnt = 0;
fail_cnt = 0;
write_cnt = 0;
endfunction //new()
task run();
forever begin
mon2scb_mbox.get(trans);
trans.display("SCB");
if (trans.wr_en) begin
scb_fifo.push_back(trans.wdata);
$display("--> WRITE! fifo_data %x, queue size:%x", trans.wdata, scb_fifo.size());
write_cnt++;
end else if (trans.rd_en) begin
scb_fifo_data = scb_fifo.pop_front();
if (scb_fifo_data == trans.rdata) begin
$display("--> PASS! fifo_data %d == rdata %d, que size:%d",
scb_fifo_data, trans.rdata, scb_fifo.size());
pass_cnt++;
end else begin
$display("--> FAIL! fifo_data %d != rdata %d, que size:%d",
scb_fifo_data, trans.rdata, scb_fifo.size());
fail_cnt++;
end
end
total_cnt++;
-> gen_next_event;
end
endtask
endclass //scoreboard
environment.sv
`include "interface.sv"
`include "driver.sv"
`include "generator.sv"
`include "monitor.sv"
`include "scoreboard.sv"
class environment;
generator gen;
driver drv;
monitor mon;
scoreboard scb;
event gen_next_event;
mailbox #(transaction) gen2drv_mbox;
mailbox #(transaction) mon2scb_mbox;
function new(virtual fifo_interface fifo_intf);
gen2drv_mbox = new();
mon2scb_mbox = new();
gen = new(gen2drv_mbox, gen_next_event);
drv = new(fifo_intf, gen2drv_mbox);
mon = new(fifo_intf, mon2scb_mbox);
scb = new(mon2scb_mbox, gen_next_event);
endfunction
task report();
$display("=======================================");
$display(" Final Report");
$display("=======================================");
$display("Total Test : %d", scb.total_cnt);
$display("Pass Counter : %d", scb.pass_cnt);
$display("Fail Counter : %d", scb.fail_cnt);
$display("WRITE CNT : %d", scb.write_cnt);
$display("=======================================");
$display(" test bench is finished!");
$display("=======================================");
#10 $finish;
endtask
task pre_run();
drv.reset();
endtask
task run(int count);
fork
gen.run(count);
drv.run();
mon.run();
scb.run();
join_any
report();
#10 $finish;
endtask
task run_test(int count);
pre_run();
run(count);
endtask
endclass
tb_fifo.sv
`include "environment.sv"
module tb_fifo();
environment env;
fifo_interface fifo_intf ();
fifo #(.ADDR_WIDTH(3), .DATA_WIDTH(8)) dut (
.clk(fifo_intf.clk),
.reset(fifo_intf.reset),
.full(fifo_intf.full),
.wdata(fifo_intf.wdata),
.rd_en(fifo_intf.rd_en),
.empty(fifo_intf.empty),
.rdata(fifo_intf.rdata)
);
always #5 fifo_intf.clk = ~fifo_intf.clk;
initial begin
fifo_intf.clk = 0;
end
initial begin
env = new(fifo_intf);
env.run_test(10);
end
endmodule
simulation
'[하만]세미콘 아카데미 > verilog' 카테고리의 다른 글
0525 uart tx rx systemverilog (0) | 2024.07.09 |
---|---|
0524 uart fifo (0) | 2024.07.09 |
0522 RAM SystemVerilog verification (0) | 2024.07.09 |
0522 32bit register.sv (0) | 2024.07.09 |
0521 num.sv (0) | 2024.07.09 |