Verification Martial Arts: A Verification Methodology Blog

Archive for 2008

Negative Verification

Posted by Janick Bergeron on 20th December 2008

The objective of negative verification is to check that an error is detected when present. It is actually one of the most important aspect of verification because undetected errors do not “exists”. It is thus important that you make sure that your monitor properly issues an error message when the errors it is supposed to detect actually occur.

But how do you distinguish between an error that is supposed to occur (and is not an error) and one that is unexpected (and actially is an error)? How do you prevent the former from causing your test to fail but not the latter? And how do you make sure that the expected error message indeed came out? That’s where the message demotion and catching capabilities of the VMM message service come into play. And we use these capabilities in verifying the VMM library itself!

Take the VMM Data Stream Scoreboard, for example. It has three pre-defined methods for checking an observed response against the expected response. How would you know if your integration of it worked properly unless you injected an error in your testcase? Or if you decide to write your own “expect()” method, how would you verify that it detects errors?

Let me illustrates how that could be done using the APB bus examples located in $VMM_HOME/sv/examples/sv/apb_bus in the OpenSource distribution.

The example provides a simple positive test that does not expect any errors. If you invoke “make”, you should see the following message:

Simulation PASSED on /./ (/./) at 6010 (0 warnings, 0 demoted errors & 0 demoted warnings)

To check that the scoreboard would indeed identify a bad transaction, should it see one, we need to break the correct functionality of the environment. So let’s create a test that will corrupt a transaction in each of the streams. That can be easily accomplished by registering a callback after the scoreboard integration callback that will modify the transaction before it is executed. For example, let’s corrupt the address of the Nth transaction targetting slave #N, for each of the three slaves:

class apb_master_to_sb_inj extends apb_master_cbs;
   int n[3] = '{0, 0, 0}; 

   virtual task pre_cycle(apb_master xactor,
                          apb_rw     cycle,
                          ref bit    drop);
      // Corrupt the address on the Nth transaction on slave #N
      if (this.n[cycle.addr[9:8]]++ == cycle.addr[9:8]) begin
         cycle.addr[7:0] = ~cycle.addr[7:0];
      end
   endtask: pre_cycle 

endclass: apb_master_to_sb_inj

We then inject the error in a new tests:

initial
begin
   tb_env env = new;
   env.build();
   begin
      apb_master_to_sb_inj inj = new;
      env.mst.append_callback(inj);
   end
   env.run();
   $finish();
end

and voila! Three streams, three failures:

Simulation *FAILED* on /./ (/./) at 6010: 3 errors, 0 warnings

It is a good idea to include this test in your regression suite. But how to make it pass when it really needs to fail? The new VMM message catcher to the rescue! Simply define a handler that will trap and mask the following error messages:

!ERROR![FAILURE] on Data Stream Scoreboard(Master->Slave) at                   70:
    In-order packet was not expected:
       Actual: APB WRITE @ 0x000000db = 0xd612ef36
       Expect: APB WRITE @ 0x00000024 = 0xd612ef36.

First, let’s define a message catcher that will turn all messages into normal note messages. For safety reasons, it is a good practices to count the number of such demotions to ensure that only the expected messages where demoted:

class handler extends vmm_log_catcher; 

   int n = 0; 

   virtual function void caught(vmm_log_msg msg);
      msg.effective_typ = vmm_log::NOTE_TYP;
      msg.effective_severity = vmm_log::NORMAL_SEV;
      this.n++;
      this.throw(msg);
   endfunction 

endclass

then register this message catcher with the appropriate message service interface instance, specifying the appropriate message content. At the end of the test, simply check that only three messages were demoted:

initial
begin
   tb_env env = new;
   handler hdlr = new;
   env.build();
   env.m2s.log.catch(hdlr, .text("/In-order packet was not expected/"));
   begin
      apb_master_to_sb_drop inj = new;
      env.mst.append_callback(inj);
   end
   env.run();
   if (hdlr.n != 3) begin
      `vmm_error(log, "The scoreboard did not properly detect errors");
   end
   $finish();
end

And now the negative test will succesfully report that the scoreboard detects errors:

Normal[NOTE] on Data Stream Scoreboard(Master->Slave) at                   70:
    In-order packet was not expected:
       Actual: APB WRITE @ 0x000000db = 0xd612ef36
       Expect: APB WRITE @ 0x00000024 = 0xd612ef36.
Normal[NOTE] on Data Stream Scoreboard(Master->Slave) at                 1090:
    In-order packet was not expected:
       Actual: APB WRITE @ 0x000000ca = 0xe6aa5052
       Expect: APB WRITE @ 0x00000035 = 0xe6aa5052.
Normal[NOTE] on Data Stream Scoreboard(Master->Slave) at                 2030:
    In-order packet was not expected:
       Actual: APB READ @ 0x000000bf = 0xxxxxxxxx
       Expect: APB READ @ 0x00000040 = 0x57879754.
Simulation PASSED on /./ (/./) at 6010 (0 warnings, 0 demoted errors & 0 demoted warnings)

Posted in Callbacks, Scoreboarding | 4 Comments »

VMM 1.1 is finally out

Posted by Janick Bergeron on 18th December 2008

Even though VMM 1.1 is only the second Open Source release of the VMM library, it follows in a long series of customer-based productivity enhancements that have been made to VMM since the original specification was published back in 2005.

I would like to thank all of the customers who kindly contributed to the requirement specification, reviews and beta-testing. The feedback from late beta customers and early adoptees has been very positive.

So, what’s new in VMM 1.1?

First, we have fixed all of the non-compliant language usage that were reported in VMM 1.0.1.

It also adds two new functional coverage models to the generated Register Abstraction model. One measures that all addresses have been accessed, the other that all interesting field values have been used.

It adds a new Performance Analysis package to measure coverage metrics that are more statistical in nature, compared to the simple singular counts of traditional functional coverage.

It adds a Multi-Stream Scenario Generator (MSSG) that can generate and coordinate stimulus across multiple channels, anywhere in the verification environment. Multi-Stream Scenarios (MSS) can be built of individual transactions, single-stream scenarios (used by the VMM Scenario Generator) or other multi-stream scenarios.

It adds a transactor iterator that brings the simple named-based controllability of vmm_log to vmm_xactor. It makes it possible to control and configure transactors reguardless of where they are located in the verification environment.

It adds a message catching mechanism that combines, in a single handler, the capability of vmm_log::modify() and vmm_log::wait_for_msg(). It can catch any message, making it easy to react or mask exceptions and perform negative testing (i.e. making sure that your environment does catch errors when they are present).

It adds a command-line option manager that makes it easy to define, document and manage environment and test options. Options can be specified on the command line or in a series of option files. And the +vmm_help option will display the automatically-generated usage documentation of all the options available in an environment or test.

And finally, it adds a mechanisms for run-time test selection for those who prefer to compile all of your tests in one simulation binary then select, via the command line, which test to execute.

Examples are provided that demonstrate how to use each of these new productivity-enhancing capabilities.

There are other minor additions that are too numerous to mention here. Just refer to the file “RELEASE.txt” in the distribution for an exhaustive list.

VMM 1.1 requires the following versions of VCS: 2006.06-SP2-9(*), 2008.09-4 or 2008.12. As usual, we believe that it is implemented using IEEE-compliant SystemVerilog code — but should some non-compliant usage be identified, be assured that it is entirely unintentional and it will be fixed as soon as possible.

The Open Source distribution of VMM 1.1 can be downloaded immediately from vmmcentral.org but will also be included in VCS2009.06. If you wish to use this version of VMM with OpenVera and/or DesignWare VIPs that require OpenVera interoperability, you must download the “SvOv” version of the distribution and patch your VCS installation using the “patch_vcs” script. This latter version is identical except for some additional encrypted code that enables methodology-level interoperability between OpenVera and SystemVerilog.

Give it a try and let us know what you think. And keep those suggestions and requests coming! We have a healthy backlog of enhancement requests that are sure to keep VMM moving forward for years to come!

(*) See VCS2006.06-SP2.txt in the distribution. The `VCS2006_06 symbol MUST be defined to enable some work-arounds unsupported SystemVerilog features.

Posted in Announcements, SystemVerilog, VMM, VMM infrastructure | 1 Comment »

How to use VMM callbacks

Posted by Janick Bergeron on 19th November 2008

One of the major benefits of using VMM is that it helps create reusable and extensible transactors and verification environments. One of the key extensibility features of VMM is the “callback”.

The following code sample shows a transactor with its core functionality implemented in the main() task.

   class my_xactor extends vmm_xactor;
      ...
      task main();
         ...
         forever begin
            ...
            in_chan.get(in_tr);    // Get input transaction
            ...                    // Execute transaction
            out_chan.put(out_tr);  // Generate output transaction
            ...
         end
      endtask
   endclass

How would you modify this transactor to accommodate DUT or test-specific features such as saving the output transactions in a scoreboard, injecting errors or adding functional coverage? One option would be to modify the transactor itself and change the functionality in the main() task as shown in the code below:

   class my_xactor extends vmm_xactor;
      ...
      task main();
         ...
         forever begin
            ...
            in_chan.get(in_tr);    // Get input transaction
            if ($urandom % 10 < 2) in_tr.checksum = $random;
            ...                    // Execute transaction
            sb.expect(out_tr);
            out_chan.put(out_tr);  // Generate output transaction
            ...
         end
      endtask
   endclass

However, this makes the transactor not reusable for other DUTs or tests. You must avoid making DUT or test-specific modifications to reusable code. This is a problem that is generic to reusable software, not just verification, and the object-oriented world provides a solution to this problem: virtual methods. Virtual methods can be used to provide extension points at relevant point in the functionality of the transactor:

   class my_xactor extends vmm_xactor;
      virtual task pre_exec(in_trans tr);
      endtask
      virtual task post_exec(out_trans tr);
      endtask
      ...
      task main();
         ...
         forever begin
            ...
            in_chan.get(in_tr);    // Get input transaction
            pre_exec(in_tr);
            ...                    // Execute transaction
            post_exec(out_tr);
            out_chan.put(out_tr);  // Generate output transaction
            ...
         end
      endtask
   endclass

VMM guidelines 4-155 through 4-158 provide recommendations for useful extension points through virtual methods. Virtual methods should also have suitable argument allowing the behavior of the transactor to be observed or modified as necessary. It is now possible to provide DUT or test-specific extensions of the transactor without modifying the reusable code:

   class my_dut_xactor extends my_xactor;
      virtual task post_exec(out_trans tr);
         sb.expect(tr);
      endtask
   endclass

   class my_test_xactor extends my_dut_xactor;
      virtual task pre_exec(in_trans tr);
         if ($urandom % 10 < 2) in_tr.checksum = $random;
      endtask
   endclass

There are two problems though. First, each time you extend the transactor, you create a new class type but the verification environment needs to be written using a class type that is known a priori; if the class type changes for every test, the environment itself will need to be changed for every test. Second, to combine orthogonal extensions, you must extend the previously extended class type, creating a linear chain of new class types; combining N different orthogonal extensions leads to potentially having to create and maintain 2N classes.

Object-oriented programming provide several well-known approaches and solutions to common problem called “patterns“. Patterns are not base classes or libraries. They are techniques and examples on how to structure object-oriented code to solve a specific challenge. Different patterns can be used, alone or in combination.

The first problem can be solve using the “abstract factory” pattern. But it introduces several limitations. For example, it requires an argument-free constructor. This has the consequence of requiring separate methods for transactor configuration and connection. VMM was designed to use the simple and well-known pins-and-wire connectivity model–which has been used to connect Verilog modules for many, many years and is well understood by all design and verification engineers. Just like Verilog modules are instantiated with their configuration parameters and port connections specified at the same time, so are VMM transactors. And this requires that they be passed as arguments via the constructor. Second, the factory pattern is used only at the time the transactor is created. This creates a static lifetime for the transactor and its extensions. It is not possible to dynamically introduce or remove a transactor extension (such as error injection) during the execution of a test.

The better pattern to use for transactor extension is the “facade pattern”. It calls for the separation of the virtual methods into a separate class, called the “facade”. The transactor will then call the virtual methods through that facade class.

   class my_xactor_cbs;
      virtual task pre_exec(in_trans tr);
      endtask
      virtual task post_exec(out_trans tr);
      endtask
   endclass

   class my_xactor extends vmm_xactor;
      my_xactor_cbs cbs;
      ...
      task main();
         ...
         forever begin
            ...
            in_chan.get(in_tr);    // Get input transaction
            if (cbs != null) cbs.pre_exec(in_tr);
            ...                    // Execute transaction
            if (cbs != null) cbs.post_exec(out_tr);
            out_chan.put(out_tr);  // Generate output transaction
            ...
         end
      endtask
   endclass

Using the facade pattern, the environment can be written using the original “my_xactor” class and your extensions can be introduced in the existing transactor instance by assigning to the “cbs” facade instance. Extensions can also be dynamically introduced or removed as the facade instance can be modified at any time.

   class my_dut_cbs extends my_xactor_cbs;
      ...
      virtual task post_exec(out_trans tr);
         sb.expect(tr);
      endtask
   endclass

   class my_test_cbs extends my_dut_cbs;
      ...
      virtual task pre_exec(in_trans tr);
         if ($urandom % 10 < 2) in_tr.checksum = $random;
      endtask
   endclass

   class tb_env extends vmm_env;
      my_xactor xact;
      ...
      virtual function void build();
         ...
         xact = new(...);
         begin
            my_dut_cbs cb = new(...);
            xact.cbs = cb;
         end
         ...
      endfunction
      ...
   endclass

   program test;
   initial begin
      tb_env env = new();
      my_test_cbs err_ext = new(...);
      fork
         #1000 env.xact.cbs = err_ext;
      join_none
      env.run();
   end
   endprogram

The facade pattern solves the new class type issue, but not the exponential number of class extensions. If a “chain of responsibilityobserver” pattern is used in combination with the facade pattern, it is possible to register multiple facade instances with the same transactor, each providing an orthogonal extension.

   class my_dut_cbs extends my_xactor_cbs;
      ...
      virtual task post_exec(out_trans tr);
         sb.expect(tr);
      endtask
   endclass

   class my_test_cbs extends my_xactor_cbs;
      virtual task pre_exec(in_trans tr);
         if ($urandom % 10 < 2) in_tr.checksum = $random;
      endtask
   endclass

   class tb_env extends vmm_env;
      my_xactor xact;
      ...
      virtual function void build();
         ...
         xact = new(...);
         begin
            my_dut_cbs cb = new(...);
            xact.cbs.push_back(cb);
         end
         ...
      endfunction
      ...
   endclass

   program test;
   initial begin
      tb_env env = new();
      my_test_cbs err_ext = new(...);
      fork
         #1000 env.xact.cbs.push_back(err_ext);
      join_none
      env.run();
   end
   endprogram

The combination of the facade and chain-of-responsibilityobserver patterns creates what is known as VMM callbacks. VMM provides some pre-defined functionality to support the callbacks: the “vmm_xactor_callbacks” base class, the vmm_xactor::prepend_callback(), vmm_xactor::append_callback() and vmm_xactor::unregister_callback() and the `vmm_callback macro. Using the provided VMM functionality, a reusable transactor would have the following structure:

   class my_xactor_cbs extends vmm_xactor_callback;
      virtual task pre_exec(in_trans tr);
      endtask
      virtual task post_exec(out_trans tr);
      endtask
   endclass

   class my_xactor extends vmm_xactor;
      ...
      task main();
         ...
         forever begin
            ...
            in_chan.get(in_tr);    // Get input transaction
            `vmm_callback(my_xactor_cbs, pre_exec(in_tr));
            ...                    // Execute transaction
            `vmm_callback(my_xactor_cbs, post_exec(out_tr));
            out_chan.put(out_tr);  // Generate output transaction
            ...
         end
      endtask
   endclass

The environment can provide a callback extension to provide DUT-specific functionality that is shared by all tests:

   class my_dut_cbs extends my_xactor_cbs;
      ...
      virtual task post_exec(out_trans tr);
         sb.expect(tr);
      endtask
   endclass

   class tb_env extends vmm_env;
      my_xactor xact;
      ...
      virtual function void build();
         ...
         xact = new(...);
         begin
            my_dut_cbs cb = new(...);
            xact.append_callback(cb);
         end
         ...
      endfunction
      ...
   endclass

And a test can similarly provide additional callback extensions in addition to the DUT-specific extensions already registered in the environment. Each test can extend a transactor differently according to its need:

   class my_test_cbs extends my_xactor_cbs;
      virtual task pre_exec(in_trans tr);
         if ($urandom % 10 < 2) in_tr.checksum = $random;
      endtask
   endclass

   program test;
   initial begin
      tb_env env = new();
      my_test_cbs err_ext = new(...);
      fork
         #1000 env.xact.append_calback(err_ext);
      join_none
      env.run();
   end
   endprogram

You will find additional guidelines and examples on using and implementing callbacks in the VMM book pp. 198-201 and pp 221-225.

Posted in Callbacks, Communication, Structural Components | 5 Comments »

Where did this blog go?

Posted by Janick Bergeron on 6th November 2008

Darn. Haven’t posted in over three months!

My appologies to readers: these past few months have been crazy! Two trips to India, one to Japan and many to various parts of North America. Made my 1K status with United in mid-october this year. Lots of customer visits, working on lots of new VMM stuff.

I’ll be back on a regular schedule soon.

Posted in VMM | No Comments »

Garbage collection

Posted by Janick Bergeron on 18th July 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 Optimization/Performance, Register Abstraction Model with RAL | 5 Comments »

Size does matter

Posted by Janick Bergeron on 6th July 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, Register Abstraction Model with RAL, Reuse, SystemVerilog | 4 Comments »

Message, message on the wall!

Posted by Janick Bergeron on 19th June 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 Debug, Messaging, Optimization/Performance | 2 Comments »

Be careful what you wish for!

Posted by Janick Bergeron on 10th June 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 Announcements | 1 Comment »

Do they diss this “this”?

Posted by Janick Bergeron on 9th June 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, Modeling Transactions, VMM | 4 Comments »

How to contribute to VMM source

Posted by Janick Bergeron on 28th May 2008

Now that you can have full access to the source code for the VMM standard library and the VMM application packages, you are free to modify them at your leisure. Whether or not you should modify the VMM code and what kind of modifications you can safely make will be the object of another post. But let’s assume, for the sake of argument, that you have made a modification that you think should be folded in the VMM source code release. How should you go about it?

  1. Keep an unmodified reference copy of the VMM source code distribution. Let’s assume it is located in a directory whose path is specified in the $VMM_ORIG environment variable.
  2. Make your modifications in a coding style that is consistent with the coding style used in the original code.
  3. Write one or more self-checking testcases (using vmm_log::report() to declare pass or fail) that demonstrate that every aspects of your modifications are working. If your modification fixes a bug (what? a bug?), your testcase must fail in the original version but pass in your modified version.
  4. Write a makefile to run your testcases. The default target “all” should run all of the testcases.
  5. Write the user reference manual page for every user-accessible class method or data member your modification creates or modifies using this template.
  6. Update the file $VMM_HOME/sv/PATCHES.txt with a one-line description of your patch, including a version number. If the files does not exist, create it.
  7. Create a patch file describing your modifications using the following commands:

    % cd $VMM_HOME
    % diff -Naur sv $VMM_ORIG/sv >patchfile
  8. Send me a tarball containing the patchfile, the testcases and the documentation. Be sure to let me know the version number(s) in the original release you started from.

Following this process will not garantee that your modifications will make it into the VMM code base, but it will greatly increase the chances that it might. I will be the final arbiter of which contributions will be included in the VMM code base and when. My decision will be based on the backward compatibility of the modifications, their impact on on-going or future development activities and their potential usefulness to others. Should your modifications be included, proper credit will be given.

Posted in VMM | No Comments »

I guess it was only a matter of time.

Posted by Janick Bergeron on 22nd May 2008

I have now joined the millions of other bloggers out there, adding to a blogosphere of politicians and their opponents, technical enthousiasts, rabid fans, angsty teenagers and the just plain weird.

As many of you probably already know, I am the moderator of the Verification Guild, a website I created in 2003 to replace the electronic newsletter I was editing since 2000. So what is the difference between Janick-the-blogger and Janick-the-moderator?

The Verification Guild is a personal, part-time endeavor that is not owned nor controlled (but supported) by my employer. As the moderator, I simply wish to maintain the technical relevance of its content, not use it as my personal soapbox. I always prefer the contribution of others to my own. I never censor a contribution from a competitor and, thanks to an amazing group of contributors, I do not remember the last time I had to delete a post1. It appears my main task as a moderator has been to not approve weblinks for pills with very dubious claims.

On the other hand, this blog is part of my day job to promote and advance functional verification methodology. Still not a soapbox (I don’t intent to write editorials) but more like a blackboard. I intent for this blog to be highly technical, for each post to be a small tutorial on an aspect of functional verification or one of the languages used in executing a verification project.

For example, as one of the architects of VMM and its application packages, I will write about specific aspects of its implementation, why it was designed a certain way or how it was meant to be used. I will also write about SystemVerilog constructs that are useful for implementing testbenches. I will answer questions I get frequently asked, either through the regular VMM support channels, by email or while visiting customers.

I also welcome your comments and suggestions. If you wish to leave a comment about one of my posts, simply click on the “comments” link at the bottom. Or write to me. Or drop by the Synopsys booth at the DAC Conference in Anaheim June 9-12 and look for the tall bald guy who isn’t wearing a suit!

1 How ironic: A few days after writing this, I had to delete the first spam post!

Posted in Announcements, VMM | 4 Comments »