[하만]세미콘 아카데미/verilog

0523 FIFO Systemverilog verification

내머리속은이런걸로 2024. 7. 9. 17:43

FIFO (First-In, First-Out)

: 데이터가 입력된 순서대로 출력되는 queue 구조를 가진 메모리 관리 방식이다. read 하는 속도와 write 하는 속도가 다를 때 일반적으로 사용한다.

 

  • First-In, First Out : 가장 먼저 입력된 데이터가 가장 먼저 출력되는 방식이다.
  • Queue : 한쪽 끝에서는 데이터를 입력(enqueue)하고 반대쪽 끝에서는 데이터를 출력(dequeue)한다.

 

FIFO

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 schematic

 


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