VMM Central Verification Martial Arts

Garbage collection

Posted by janick on July 18th, 2008

In SystemVerilog, unlike C, you don’t have to explictly free dynamically allocated class instances. Like most modern programming languages, SystemVerilog includes a garbage collector that frees memory that is no longer needed.

Although there are a few garbage collection processes out there, the lack of a root object (like the sys object in e) almost garantees that Reference Counting is used1.

To illustrate how reference counting works, consider the following code segment:

fork
   while (...) begin
      eth_frame pkt = new;         // Line 1
      scoreboard.push_back(pkt);   // Line 2
      ...
   end                             // Line 3
   forever begin
      eth_frame pkt;
      pkt = rx();
      if (!scoreboard[0].compare(pkt)) …
      scoreboard.pop_front();      // Line 4
   end
join

When the eth_frame instance gets first created on line 1, its reference count is set to “1″. When it is later added to the scoreboard on line 2, its reference count is incremented to “2″ (one for the “pkt” variable, one for the “scoreboard” queue). When the while loop iterates on line 3, the dynamic “pkt” variable disappears, reducing the reference count to 1. When the class instance is finally popped out of the scoreboard on line 4, its reference count is decremented to “0″ and its memory is garbage collected.

This usually works well until you create circular references, i.e. objects that refer to each other.

Circular references are always created whenever you introduce the concept of a “parent” object. Take a RAL model for example, an instance of the vmm_ral_reg class has a reference to its parent vmm_ral_block class instance that gets returned by the vmm_ral_reg::get_block() method. But the vmm_ral_block class instance has a reference to all of the vmm_ral_reg instances it contains, which get returned by the vmm_ral_block::get_registers() method.

That create a circular reference for every register in a block.

That means that a RAL model cannot be garbage collected. Ever.

In the case of a RAL model, that’s not a problem because it corresponds to the DUT and that DUT, being modelled using modules and interfaces, is static in nature and can never be dynamically modified.

But in the case of objects that get created in large numbers but should live only for as long as needed (such as packets or transactions who only need to live while they are processed by the DUT), then you have to be very careful to break that circular reference to enable garbage collection. Otherwise you will end up consuming an ever-increase amount of memory.

1Turns out not to be accurate. See follow-up comment.

Posted in Uncategorized | 3 Comments »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Size does matter

Posted by janick on July 6th, 2008

The VMM Register Abstract Abstraction layer is documented with a 64-bit data value system. For example, the write() method in the vmm_ral_field class is documented as:

task vmm_ral_field::write(output vmm_rw::status_e status,
                          input  bit [63:0]       value,
                          …);

However, to be able to handle fields (and registers and virtual registers) that are wider than 64 bits, the implementation uses the VMM_RAL_DATA_WIDTH macro to define the maximum size of a field (and register and virtual register):

class vmm_ral_field;
   local bit [`VMM_RAL_DATA_WIDTH-1:0] value;
   …
   task vmm_ral_field::write(output vmm_rw::status_e              status,
                             input  bit [`VMM_RAL_DATA_WIDTH-1:0] value,
                             …);
   …
endclass

Of course, the macro is defined by default to “64″, unless you define it otherwise:

`ifndef VMM_RAL_DATA_WIDTH
   `define VMM_RAL_DATA_WIDTH 64
`endif

Normally, you don’t really need to worry about the value of this macro, as SystemVerilog will silently extend or trim the value assigned to the value argument (or returned via that same argument in the read() method). That works fine if you know a priori the size of the field and use an appropriately sized expression or variable for it.

However, when writing generic code that needs to work with unknown field sizes, you must be careful to declare any temporary or working variable using:

bit [`VMM_RAL_DATA_WIDTH-1:0] tmp;

to avoid inadvertently losing data. You can get the actual size of the field by using the vmm_ral_field::get_n_bits() method. See the pre-defined register tests in $VMM_HOME/sv/RAL/tests for examples.

All of the above is documented in Chapter 16 “Maximum Data Size” of the RAL User’s Guide.

But why not make the size of the field a parameter and do away with the macro altogether?

You mean beside the fact that VCS did not support parameterized class at the time RAL was created? :-)

There are a few reasons…

One could be the potential memory saving by making the data members holding the mirrored field value match the actual size of the field. However, experiments on a customer RAL model with hundreds of thousands of fields (with a VMM_RAL_DATA_WIDTH defined to 512) did not show a significant memory saving (at least with VCS).

The other is that the notion of an absolute maximum field size would still be required to be able to write generic code, independent of their respective sizes. It is impossible to operate on a field through a parameterized class unless its size is known a priori because you must specialize1 a parameterized class whenever you use it. It would thus still be necessary to create a size-generic field base class that can deal with any size of fields (so a function such as vmm_ral_reg::get_fields() can be provided). As a user, you would still need to know about and use the VMM_RAL_DATA_WIDTH macro.

Finally, the automatic value resizing only works for input, output and inout arguments. A ref argument (such as the ones use in the field pre/post_read/write callback methods) require that a variable of the exact same size be used. Again, this would require knowing, a priori, how large a field is before you could write code for it.

So my current line of thinking is that class parameters are fine for generic types, but not that useful for generic sizes. Or am I all wet?

1and that specialization must be defined at compile-time because you can’t use a run-time expression to specialize a class. For example, you could not call get_n_bits() to obtain the size of the field then use the result to specialize a parameterized class to access it.

Posted in Coding Style, RAL, SV | No Comments »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Message, message on the wall!

Posted by janick on June 19th, 2008

Why does the VMM message interface (the vmm_log class) have a start_msg() and a text() method that must be used in this convoluted way:

if (log.start_msg(vmm_log::DEBUG_TYP, vmm_log::TRACE_SEV)) begin
   log.text("This is a trace message");
   log.end_msg();
end

Why not the much simpler one-liner:

log.message(vmm_log::DEBUG_TYP, vmm_log::TRACE_SEV,
            "This is a trace message");

which would then eliminate the need for the macro:

`vmm_trace(log, "This is a trace message");

Given the examples above, there is absolutely no reason. However, this example illustrates why:

`vmm_trace(log, $psprintf("Read 'h%h from 'h%h with status %s",
                           data, addr, status.name()));

which expands to:

if (log.start_msg(vmm_log::DEBUG_TYP, vmm_log::TRACE_SEV)) begin
   log.text($psprintf("Read 'h%h from 'h%h with status %s",
                      data, addr, status.name()));
   log.end_msg();
end

The $psprintf() (and all other formatting system tasks $sformat(), $format(), $write(), $display(), etc…) may be simple to use but they are very run-time expensive. And if you are not going to display a message, why incur the cost of composing its image?

When using a single procedure call, the value of all of its arguments must be determined before it is called. Thus, using this approach:

log.trace($psprintf("Read 'h%h from 'h%h with status %s",
                    data, addr, status.name());

incurs the cost of creating the message image every single time. And most of the time, this debug message will simply be filtered out (think about the thousands and thousands of regression runs where debug is not enabled!).

On the other hand, checking first if messages of a certain type or severity are going to be filtered out or not and only then composing the image of the message improves your run-time performance.

By how much? Of course, it depends on the number of messages that will eventually get filtered out. But just to give you an idea, I ran this experiment using VCS:

program p;

initial
begin
   int i;
   string msg;
   i = $urandom() % 2; // See footnote1
   if (i == 1) i–;
   repeat (100000) begin
`ifdef GUARD
      if (i)
`endif
      msg = $psprintf(”%s, line %0d: Message #%0d at %t”,
                      `__FILE__, `__LINE__, 0, $time());
   end
end

endprogram

With `GUARD defined, which causes the $psprintf() call to be skipped, I get run-times of approximately 0.025 seconds. With `GUARD undefined, which causes the $psprintf() call to be executed, I get run-times of approximately 0.230 second or 10x slower simulation performance.

Personally, I think the performance gain is worth the little extra bit of code to write. Remember to always optimize the right thing: you’ll write that code once but you’ll run it thousands and thousands of times. So saving a few lines of codes is not always the right decision.

1 I use a convoluted way to set i to 0 to prevent an optimizing compiler from optimizing the entire if statement away.

Posted in Uncategorized | 1 Comment »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Be careful what you wish for!

Posted by janick on June 10th, 2008

Now that VMM is available under an Open Source license, how is it going to be supported?

First of all, any further bug fixes and enhancements we will be making to VMM (such as the newly announced VMM-for-Low-Power functionality) will be added to the Open Source distribution once the usage model and basic functionality will have been proven with one or two lead customers who work with us in specifying and developing the enhancements.

The Apache licensing means that you are free to modify the VMM code in any way you wish. But should you? If you make a modification, that modification exists only in your version of the VMM code.

For bug fixes, that is obviously not a problem (and I’d really like to know about those so I can have them fixed in our distribution (such as the recent non-compliance issues)).

But what about functional changes? If you add functionality that you rely on for implementing your verification IP, those will no longer be portable to other VMM environments.

If you require some new capability or have an idea for some cool new functionality, I suggest talking with Synopsys first about it. If we can implement that new functionality for you, it will automatically be included in the next VMM release and everyone else will get it, ensuring portability once more. It will also ensure that the new functionality is implemented while taking into account other developments concurrently happening or planned. And we will be responsible for its on-going support.

But should you decide to go ahead on your own (for whatever reason), I’m still OK with it.

If you want to share your modifications with the VMM community, you can publish your patch in the VMM MODS forums. Such user-contributed modifications will use the same support model as the one used by phpbb. As the MOD author, you are responsible for all support. The VMM MODS forums on VMM Central can be used as a support and distribution meeting point. Simply request that a MOD-specific forum be created in the VMM MODS forum group. You can then announce new versions and users can request support on that forum.

We only ask (but cannot require) that you publish your MOD under the same Apacahe 2.0 license that VMM is published under. MODs that prove to be popular and stable (and backward compatible!) will eventually be merged into the Synopsys VMM Open Source distribution (with proper credit given to the original author(s) of course).

Posted in MODs, Support | 1 Comment »

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Do they diss this “this”?

Posted by janick on June 9th, 2008

One of the issues with open-sourcing, is that everyone gets to see the code you write.

So now that you have had the chance to look at the VMM source code, you probably have noticed what a former co-worker once described as “a compulsive use of “this” in method implementations“. Why is it that I indeed compulsively use “this” whenever I refer to a data member in a class? As with every coding guideline, there are pros and cons. This one is no exception.

First of all, a bit of background, for those of you who may not be familiar with “this”… Those of you who are, can skip forward…

When referring to a variable or function/task that is declared as a member of a class from within that same class, you can prefix the reference with “this.”. For example:

class packet;
   bit [47:0] da;
   function new(bit [47:0] da);
      this.da = da;
   endfunction
   function void display();
      $write(”DA = %h\n”, this.da);
   endfunction
endclass

“this” is an implicitly defined handle to the object instance that is executing the procedural code. In the constructor of the above example, “this.da” is an explicit reference to the “packet::da” variable contained in the class. Using the “this.” prefix makes it clear that I am not referring to the “da” argument. In this case, the use of “this.” is not optional as it is needed to differentiate between the constructor argument and the data member. It also avoids having to come up with different names for the constructor argument and data member when in fact they both represent the same thing.

I choose to use the “this.” prefix even in cases where it is clearly optional. For example, in the “packet::display()” method, because there are no other variables named “da” in the scope of the function, it would be clear that a simple reference of “da” would refer to the packet::da variable.

I always use the “this.” prefix to document that the variable or method referred to is a member of the class and not some variable/task/function inherited from some larger scope. In the simple example above, it is easy to see that the “da” variable is a class data member. But what if the procedural code making use of the “packet::da” variable and the declaration of that variable where separated by several dozens — or hundreds — of lines? What if they were in different files? Consider the code below. Using “this.”, despite being option, clearly document the nature of the “stream_id” and “fcs” variables.

class bad_frames_in_stream_0 extends eth_frame;
   rand bit is_bad;

   constraint some_bad_frames {
      if (this.stream_id == 0 && this.is_bad) this.fcs != 0;
   }
   ...
endclass

Posted in Coding Style, VMM | 4 Comments »

1 Star2 Stars3 Stars4 Stars5 Stars (2 votes, average: 5 out of 5)
Loading ... Loading ...