How To Protect FIFOs Against Overflow – Part 2

This post is a follow-up of the How To Protect FIFOs Against Overflow – Part 1 and it details the second proposed solution.

The complete implementation presented in this post can be downloaded from GitHub.

That being said, let’s go through the implementation, step-by-step.


Step 1. Define enumeration items that identify the two FIFOs

typedef enum {FIFO_MSGS, FIFO_RESP} fifo_t;


Step 2. Implement the singleton using a class that inherits uvm_object

class fifo_protection extends uvm_object;
   // singleton instance
   protected static fifo_protection m_inst;
   typedef uvm_object_registry#(fifo_protection, "fifo_protection") type_id;

   // constructor
   function new(string name="fifo_protection");
      super.new(name);
   endfunction

   // Creates/returns the singleton instance
   static function fifo_protection get();
      if(m_inst == null)
         m_inst = fifo_protection::type_id::create("fifo_protection");
      return m_inst;
   endfunction

   virtual function string get_type_name ();
      return "fifo_protection";
   endfunction

   // override of the create function to create the fifo_table instance
   function uvm_object create (string name="");
      fifo_protection tmp = new(name);
      return tmp;
   endfunction
endclass


Step 3. Add the objection-related fields

Add the objection list which contains one objection for each FIFO. Add other utility fields which enable the objections or specify their resource limit.

// list of objections: one for each FIFO
local uvm_objection objs[fifo_t];

// list of fifo limits
local int objs_limit[fifo_t];

// enable flag that allows us to enable/disable fifo protections
local bit objs_en[fifo_t];


Step 4. Add objection initialization function

function void init_protection(fifo_t kind, int limit, bit en);
   objs[kind] = new($sformatf("obj_%s", kind.name()));
   objs_limit[kind] = limit;
   objs_en[kind] = en;
endfunction


Step 5. Add resource locking/freeing API

task lock(fifo_t kind, int count=1);
    if (!objs_en[kind])
       `uvm_warning("FIFO_PROTECTION_LOCK_WRN", 
                    $sformatf("The objection %s is not enabled.", kind.name()))
    wait_for_resources(kind, count);
    objs[kind].raise_objection(this, "", count);
endtask

function void free(fifo_t kind, int count=1);
   if (!objs_en[kind])
      `uvm_warning("FIFO_PROTECTION_FREE_WRN", 
                   $sformatf("The objection %s is not enabled.", kind.name()))
   objs[kind].drop_objection(this, "", count);
endfunction

function int get_resource_count(fifo_t kind);
   if (!objs_en[kind])
      `uvm_warning("FIFO_PROTECTION_GET_RESOURCE_COUNT_WRN", 
                   $sformatf("The objection %s is not enabled.", kind.name()))
   return objs[kind].get_objection_count(this);
endfunction


Step 6. Add objection status API

function bit are_all_free();
    fifo_t ft = ft.first();
    are_all_free = 1;
    forever begin
       are_all_free &= (get_resource_count(ft) == 0);
       if ( ft == ft.last )
          break;
       ft = ft.next;
    end
endfunction


Step 7. Add debug API

function string dump();
   fifo_t ft = ft.first();
   dump = "";
   forever begin
      dump=$sformatf("%s%s=%d/%d, ", dump, ft.name(), 
            (objs_limit[ft]-get_resource_count(ft)), objs_limit[ft]);
      if ( ft == ft.last )
         break;
      ft = ft.next;
   end
   dump=$sformatf("fifo_protection available resources: %s", dump);
endfunction


Step 8. Create a global variable for the singleton

fifo_protection fifo_prot_ston = fifo_protection::get();


Step 9. Initialize protections

One can initialize protections for various FIFOs in the build_phase() of one’s verification environment:

fifo_prot_ston.init_protection(FIFO_MSGS, 16, 1);
fifo_prot_ston.init_protection(FIFO_RESP, 4096, 1);

One can read more about the limit argument here.

Step 10. Lock and free resources

Locking and freeing of resources is explained here.
For MESSAGE data flow one needs to:

  • lock 1 resource just before sending a MESSAGE (in MESSAGE sequence, just before `uvm_do)
  • release 1 resource as soon as the RAM write access is verified against the original MESSAGE packet
// lock resources in MESSAGE sequence, just before `uvm_do
fifo_prot_ston.lock(FIFO_MSGS, 1); 
`uvm_do(msg_item)
……………………
// free resources in scoreboard, right after the RAM-write access is verified
fifo_prot_ston.free(FIFO_MSGS, 1);

Similarly for READ data flow one needs to:

  • lock “number of READ bytes” resources as soon as the input monitor detects a READ request. Given my particular example the resource locking could be done later than MESSAGE resource lock.
  • Free “number of READ bytes” resources after scoreboard checks the READ response going out on the input interface
// lock resources in scoreboard as soon as a READ request is detected on the input interface by the input monitor
fifo_prot_ston.lock(FIFO_RESP, read_req.size);
……………………
// free resources in scoreboard, right-after the READ-response on input interface is verified
fifo_prot_ston.free(FIFO_RESP, read_resp.size);


How to disable the protection

There might be scenarios that require to overfill one or more of the FIFOs. In that case it is sufficient to disable the objections for the given FIFO using the dedicated API:

fifo_prot_ston.set_enable(FIFO_RESP, 0);

It is recommended to disable one FIFO at a time and have different tests for each FIFO overflow scenario.



That’s all! Enjoy!


Comments

Leave a Comment:

Your comment will be visible after approval.

(will not be published)

This site uses Akismet to reduce spam. Learn how your comment data is processed.