Example Usage of Systemverilog Interface Class

Interface class is a very common concept in many object-oriented programming languages. In Systemverilog, it was introduced since 2012 version, but still rarely seen in verification environment.


Interface Class vs Interface

The first thing we need to address here is the confusion between interface and interface class in Systemverilog. These are two different concepts and serve different purposes.

  • The interface in Systemverilog is a group of signals, or methods, and is used for connecting the signals between the blocks (hardware module and software obj).
  • The interface class in the other hand, is a very common term in Object Oriented Programming. It is used to define a set of methods that are used for a specific purpose. Think of it as a template, when a class implements an interface class, that class must follow this template and construct all the required methods.

To understand the interface class concept, let’s look at an example below, where interface class is not used at first and how it helps the issue when coming into use in the later implementation.


A Problem Needed to Be Solved

Let’s consider this case:

  • We have memory manager component mem_mgr in our verification env.
  • This mem_mgr will have a function backdoor_obj_data to write data to the memory backdoor.
  • In the the test we will call this function to write a data of our objs to SoC memory.
  • Assuming in current env, we need to support 2 type of data packets: aes_pkt and uart_pkt.
class test_a extends uvm_test;
...
   mem_mgr m_mem;
   ...
   m_mem.backdoor_obj_data(aes_obj);
   m_mem.backdoor_obj_data(uart_obj);
...
endclass

So when designing the mem_mgr class, not using interface class, we might implement it as below:

class mem_mgr extends uvm_component;
...
   function void backdoor_obj_data(uvm_object m_obj);
      bit[31:0] m_addr;
      bit[31:0] m_data[$];

      aes_pkt  m_aes_obj;
      uart_pkt m_uart_obj;

      if($cast(m_aes_obj, m_obj)) begin
         m_addr = m_aes_obj.m_addr;
         m_data = m_aes_obj.m_data;
         //...
         //some statements to backdoor data
      end

      if ($cast(m_uart_pkt, m_obj)) begin
         m_addr = m_uart_obj.m_addr;
         m_data = m_uart_obj.m_data;
         //...
         //some statements to backdoor data
      end
   endfunction
...
endclass

In the above implementation of the backdoor_obj_data() function, there are some rules that must be followed:

  • Firstly, the input argument of the function backdoor_obj_data need to be uvm_object. Since all transaction objects in the uvm env will be extends from this base class. We cannot let the argument as backdoor_obj_data(aes_pkt m_obj) because we also need to run this function with uart_pkt obj.
  • Secondly, each class uart_pkt and aes_pkt must have the m_addr variable and m_data queue.
  • Thirdly, we need to cast the m_obj from type uvm_object to either uart_pkt or aes_pkt. Otherwise we will meet an error since uvm_object type does not have m_addr and m_data in it.

This implementation has several issues:

  • The mem_mgr job is to backdoor data to memory, it should not need to be aware of which class it supports, which is aes_pkt and uart_pkt.
  • If we need to support more type of data, we must modify the function backdoor_obj_data() as what we do for aes_pkt and uart_pkt.
  • The aes_pkt and uart_pkt are usually mantained by different programmers working in the same verification environment. Basically, letting all verification engineers modifying 1 file is not a good idea. If just one of the programmers does not follow the rules above, such as using different variable name instead of m_addr, the whole verification environment will be break. And other engineers will not be able to run simulation.
  • This implementation makes the mem_mgr become very messy later when more and more class type need to be suppported to backdoor to memory.

Using an Interface Class

Now, let’s implement the mem_mgr using interface class and see how it helps in this case.

Define a Interface Class

Firstly, we need to define an interface class. It’s a good practice to use an adjective to name an interface class, and it’s also should be named after what it’s capable of.

In this example, let’s create an memory_backdoorable interface class. Since the object that implements this interface class can be backdoored by our memory manager (backdoorable).

interface class memory_backdoorable;
   pure virtual function get_data_info(output bit[31:0] addr, output bit[31:0] data[$]);
endclass

In this interface, we define a pure virtual function get_data_info. This function will be called by our memory manager to get the data, and its address to backdoor to the SoC memory.

Any object wishes to have data backdoored by the memory manager must implement this interface class and create this function.

Implement an Interface Class

Now, since the aes_pkt needs to be backdoored to SoC memory, we will implements the memory_backdoorable interface.

As this interface has a pure function get_data_info, it is required to be defined in the aes_pkt.

This function will output the addr, and the data queue which are later used by mem_mgr to write data to SoC memory.

class aes_pkt extends uvm_object implements memory_backdoorable;
...
   virtual function get_data_info(output bit[31:0] addr, output bit[31:0] data[$]);
      bit[31:0] addr;
      bit[31:0] data[$];

      addr = 32'hea00_0010;

      data.push_back(32'habab_cdcd);
      data.push_back(32'hffff_0000);
   endfunction
endclass

Using Interface Class as Input Arguments

Back to the implementation of mem_mgr, now we will use the memory_backdoorable as the input argument for backdoor_obj_data() function.

class mem_mgr extends uvm_component;
...
   function void backdoor_obj_data(memory_backdoorable m_obj);
      bit[31:0] m_addr;
      bit[31:0] m_data[$];

      m_obj.get_data_info(m_addr, m_data);

      // some backdoor statements using m_addr and m_data
   endfunction
...
endclass

We can clearly see that:

  • The mem_mgr will not need to know which class type it is getting backdoor data from. Therefore, no need to casting and checking if the type is supported as another implementation.
  • We can also avoid any modification if new type of class is needed to be backdoor by mem_mgr. If new class type needs to be backdoor by mem_mgr, it need to implements the memory_backdoorable and create the get_data_info function.
  • Also, by using the interface class, we can eliminate the implicit coding conventions such as backdoorable class must have m_addr and m_data variable. All the requirements are defined explicit in the interface class. So it is easier for any verification engineer to follow.

If we look at the test_a where the backdoor_obj_data action is triggered, it’s still the same as the requirements, but the code of mem_mgr is much more simplier, and more important, the mem_mgr is totally independent from the aes_pkt and uart_pkt.

class test_a extends uvm_test;
...
   mem_mgr m_mem;
   ...
   m_mem.backdoor_obj_data(aes_obj);
   m_mem.backdoor_obj_data(uart_obj);
...
endclass

Later, when another class, let’s say spi_pkt, needs to be backdoored by mem_mgr, we just need to make sure that the new class spi_pkt implements memory_backdoorable and its required method get_data_info. The mem_mgr will remain untouch.

Some side notes

Sometimes it seems a bit difficult to follow the flow of data, we just need to clarify these three places and the action of each one:

  • Where the related data for the targeted operation come from: In this example the object of aes_pkt and uart_pkt will provide data for backdoor operation.
  • Where the action is performed: The mem_mgr is where the data backdoor action is actually performed.
  • Where the action is triggered: The test is where the backdoor action is triggered, by calling the mem_mgr.backdoor_obj_data() function with the input argument is any object implementing the memory_backdoorable interface class.

Further reading

  1. Interface class is a basic concept in other oop languages, if you’re insterested, get to know the SOLID Principles to understand why we should use the interface class.
  2. There are several DVCon papers that share other application of interface class such as:
    • SystemVerilog Interface Classes - More Useful Than You Thought
    • Design Patterns by Example for SystemVerilog Verification Environments Enabled by SystemVerilog 1800-2012


[Tags systemverilog  ]