Feed on

Reuseless Code

Here’s a new English term I’ve just coined: reuseless code. It refers to code that was written in such a reusable way that it can’t be used in any way. Writing reusable code is a noble cause, but before you start it is better to clarify where, why and how you think your code will ever be reused, if at all. Skip this step and you can be sure that, despite your good intentions, someone else will have to rewrite the whole thing later on. You can also be sure that your code will be unnecessarily and overwhelmingly complex.

In a testbench different parts are likely to be reused in different ways. Standard interfaces are the number one candidates for reuse in the pure sense of the term; it is quite probable that they will be plugged in as is into an altogether different project later on. Data generators (i.e. an Ethernet packet generator), base class libraries and generic packages (register package) follow close. In fact, if you’re lucky enough, you will probably be reusing someone else’s code yourself.

As a rule of thumb, any part in your testbench that is tied to more than one DUT interface will probably remain attached to the same DUT for the rest of its life. Scoreboards, multi-interface sequences (”virtual sequences”) and some coverage collectors are good examples. Such code should be written with system level reuse in mind, but trying to write it for plug and play reuse with another DUT is, in most cases, a waste of time. Other parts of your testbench that are likely to be reused only on system level are coverage of DUT configuration or state machines, signal maps (connection of DUT signals to testbench signals) and the verification environment top file that normally does all the internal interconnect (i.e. connect a scoreboard to the BFMs ports). Since these parts will go together everywhere, they can, for example, rely on the fact that other parts exist. This means, that decoupling issues can be relaxed a little bit: For example, if a scoreboard refers to some public DUT specific configuration member by name, I wouldn’t consider it a crime. If a code for standard interface does something similar, that’s another thing.

Finally, in most testbenches there exist some limited parts that are not going to be reused at all. For example, in a module level testbench you usually have a part that instantiates the DUT and the corresponding verification environment and connects the two together. Obviously, this will not be required anywhere else.

As an example consider the following, over simplified and partly abridged, testbench for a serial to parallel DUT. The serial to parallel can convert words of up to 32 serial bits into one parallel word. The size configuration parameter can be used to determine how many serial bits are grouped into one parallel word.

// *** reused in another project

// monitors know nobody else
class serial_monitor;
   mailbox#(logic) data_bits;
   // implementation

class parallel_monitor;
   mailbox#(int) data_words;
   // implementation

// *** reused on system level only

class dut_config;
   logic[4:0] size;

// scoreboard can know about env
// object and everything that's
// underneath it. No point in
// passing info through mailboxes
// between config object and scoreboard
// because they always go together...

typedef class env;

class s2p_scoreboard;
   env env_p;

   mailbox#(logic) serial_in_p;
   mailbox#(int) parallel_out_p;

   task check();
      forever begin
         int data_in, data_out;

         for (int i = 0; i<=env_p.config_i.size; i++) serial_in_p.get(data_in[i]);

         assert (data_in != data_out);
endclass // s2p_scoreboard

class env;
   serial_monitor serial_monitor_i;
   parallel_monitor parallel_monitor_i;
   s2p_scoreboard s2p_scoreboard_i;
   dut_config config_i;

   function new();
      // instantiate and connect mailboxes
endclass // env

// *** not reused at all

module dut;

module tb;
   env env_i;
   dut dut_i();

   // connect the two;

In such a testbench, it is expected that the intricate interfaces will be later dug out to be used in another project. Therefore, the interfaces don’t know the DUT, its configuration or the environment. In case they need to know anything, you should (a) ask yourself if they really need to know it (b) if yes, pass that info in some way that will maintain the interfaces decoupled.

On the other hand, the DUT config object, which is some representation of register values, the scoreboard and the env object are always expected to remain together and attached to the same DUT. Therefore, they can reference each other if this makes things more convenient. For example, there is no point in adding some sort of a port/mailbox/buffer between the scoreboard and the config object. The scoreboard can just have a look at the config, or at any other public parts under env, because it will always be used together with the env. Also, the env can connect the mailboxes together because it knows the interfaces and the scoreboard. This might not seem like a big saving, but when you have hundreds of configuration parameters and you insist on passing each of them to the scoreboard via some kind of a buffer you will note the difference.

So before you go crazy about reuse, it is always good to think ahead. Writing reusable code often comes with a high price tag attached. It demands more effort, and it might make your code more complex and verbose, and hence, harder to maintain.

2 Responses to “Reuseless Code”

  1. Sandeep Gor Says:

    Nice explaination to my dilemma while developing verification environment. Lately, I started realizing what you explained as “reuseless code”. In my last project, my verification environment implementation made me think “whether that perticular reusable code was really reusable? or was it required to implement it as resuable?”

    Thanks for this post.

    Best Regards,
    Sandeep Gor.

  2. Pandithurai Sangaiyah Says:

    Really Good explanation which clear my basic doubt.

Work For Verilab