VERIFICATION ASYNCHRONOUS FIFO CUMMINGS

In reply to ben@SystemVerilog.us:

Thank you for the FIFO model you provided me, it is really clear and, despite not knowing that much of assertions because my supervisor told me to not use them, I believe I understood what the entire code is doing.

I was trying to create each components when my supervisor told me that things got way complicated than he expected so he basically provided me a verification plan which is this one:

!(file:///C:/Users/Trematerra/Downloads/889_786_1.png)

don’t know if you will be able to see it, but basically the Verification plan consists of: one Generator with one Transaction, two drivers (one for each side, write and read), at least 4/5 interfaces (one for write, one for read, one for the clock signals, one for the output flags and one for the internal signals such as pointers), two stimulus Monitor (always one for each side, write and read), one DUT Monitor shared by the two sides, a reference MODEL and a Scoreboard. Kinda tricky as TB architecture.

Anyway, until now I have came up with this code:
Transaction code:

class full_transaction;
    typedef enum {RD, WR, RD_WR} situation;
    
    rand situation cases;
    rand bit rinc;
    rand bit winc;
    rand bit [7:0] wdata;
    
    bit wfull;
    bit rempty;
    bit [7:0] rdata;
    
    constraint read_only {cases == RD -> rinc==1; winc==0;}
    constraint write_only {cases == WR -> rinc==0; winc==1;}
    constraint both_operation {cases == RD_WR -> rinc==1; winc==1;}
    //constraint write_full {}
    
    
    function void print_all();
        $display("rinc=");
    endfunction
    
endclass


Generator code:

class full_generator;
    //typedef enum {RD, WR, RD_WR, RD_EMPTY, WR_FULL} situation;
    
    int num_transactions = 200;
    full_transaction trans;
    mailbox gen2wdriv;
    mailbox gen2rdriv;
    
   
    event gen_done;
    //situation cases;
    
    function new (mailbox gen2wdriv, event gen_done, mailbox gen2rdriv);
        this.gen2wdriv = gen2wdriv;
        this.gen2rdriv = gen2rdriv;
        this.gen_done = gen_done;
    endfunction
    
    
    task main();
        repeat (num_transactions) begin
            trans = new();
            if (!trans.randomize()) begin
                $fatal(0,"Gen:: write_trans randomization failed.");
            end
            
            gen2wdriv.put(trans);
            gen2rdriv.put(trans);
            
        end
        -> gen_done;
        
    endtask
    
            
            
            //if (!randomize.(cases) with {cases dist {RD:=2, WR:=2, RD_WR:=1, RD_EMPTY:=1, WR_FULL:=1};}) begin
                //$fatal(0,"Gen:: write_trans randomization failed.");
                
endclass

Interfaces code:


interface write_if();
    logic winc;
    logic [7:0] wdata;
    
endinterface

interface read_if();
    
    logic rinc;
    logic [7:0] rdata;
    
endinterface

interface clk_if (input wclk, input rclk);
    
    logic wrst_n;
    logic rrst_n;
    
endinterface

interface flag_if();
    
    logic wfull;
    logic rempty;
    
endinterface

Driver, going to post only the write side:

class write_driver;
    mailbox wgen2wdriv;
    full_transaction trans;
    virtual interface write_if w_vif;
    virtual interface clk_if c_vif;
    
    //constructor function
    function new (virtual interface write_if w_vif, mailbox wgen2wdriv, virtual interface clk_if c_vif);
        this.w_vif = w_vif;
        this.wgen2wdriv = wgen2wdriv;
        this.c_vif = c_vif;
    endfunction
    
   /* 
    task reset();
        $display("Reset every interface signal");
        wait (!c_vif.wrst_n);
        w_vif.winc <=1'b0;
        w_vif.wdata <=8'b0;
        @(posedge c_vif.wclk);
        wait (c_vif.wrst_n);
        $display("Reset phase is over");
    
    endtask */
    
    
    //task to drive the signals from the driver to the interface
    
    task drive();
        $display("This task is used to drive the signals from the transaction to the involved interface");
            @(posedge c_vif.wclk);
            wait(wgen2wdriv.num()!=0); 
            
            trans = new();
            wgen2wdriv.get(trans);
            w_vif.wdata <= trans.wdata;
            w_vif.winc <= trans.winc;
        
          /* if (trans.winc && !w_vif.wfull) begin
                $display ("The generator randomized winc equal to 1 so a new write operation can be passed");
                w_vif.winc <= trans.winc;
                w_vif.wdata<= trans.wdata;
            end else begin
                $display("Winc is equal to 0 so a write operation cannot be executed");
                w_vif.winc<=1'b0;
            end */
    endtask
    
    task write_full();
        $display("This task is created to check what happens when the FIFO is FULL and the write enable is 1");
        repeat(16) begin
            @(posedge c_vif.wclk);
            w_vif.winc<=1'b1;
            w_vif.wdata <=$urandom(1);
            
        end
        
    endtask
    
    task main();
        while(!c_vif.wrst_n) begin
            w_vif.winc <=1'b0;
            w_vif.wdata<='0;
            @(posedge c_vif.wclk);
        end
        
        forever begin
            fork
                begin
                    $display("Thread for the drive function");
                    drive();
                end
                
                begin
                    $display("Thread to check what happens when the FIFO is full");
                    write_full();
                end
                
            join_none
        end
    endtask
    
                   
            
            
endclass

The idea was to generate a random transaction with some constraints to cover 3 different situations: RD, WR and RD/WR at the same time by using that typedef, while for the Write_FULL and the Read_EMPTY scenarios, since I know the depth of the FIFO, I have implemented the task Write_full which repeats itself 16 times so I know it will happen. Then in the task main I did fork/join_none with those two tasks. Hope it makes sense my plan…still thank you for your help