VMM Central Verification Martial Arts

You get real hierarchy with VMM1.2

Posted by Wei-Hua Han on February 9th, 2010

If you look at VMM1.2 classes, you may find that almost all new() functions have an argument, vmm_object parent. The purpose of this argument is to build a parent-child hierarchy within a VMM1.2 based environment, so that VMM1.2 can provide an infrastructure where users can access the components inside the environment through hierarchical path and name. And this parent-child hierarchy also contributes to the implicit phasing implementation.

Here is a small example to illustrate how a hierarchy can be built with VMM1.2:

  1. class mike_c extends vmm_object;
  2.    function new(vmm_object parent=null, string name="");
  3.       super.new(parent,name);
  4.    endfunction
  5. endclass
  6. class ben_c extends vmm_object;
  7.    function new(vmm_object parent=null, string name="");
  8.       super.new(parent,name);
  9.    endfunction
  10. endclass
  11. class jason_c extends vmm_object;
  12.    mike_c Mike;
  13.    ben_c Ben;
  14.    int weight;
  15.    function new(vmm_object parent=null, string name="");
  16.       bit is_set;
  17.       super.new(parent,name);
  18.       weight=vmm_opts::get_object_int(is_set,this, "weight",0, "set weight");
  19.    endfunction
  20.    function void build();
  21.       Mike = new(this,"Mike");
  22.       Ben = new(this,"Ben");
  23.    endfunction
  24. endclass
  25. program p1;
  26.    jason_c Jason;
  27.    initial begin
  28.       vmm_opts::set_int("Jason:weight",10);
  29.       Jason=new(null,"Jason");
  30.       Jason.build();
  31.       vmm_object::print_hierarchy(Jason);
  32.       $display("Jason has %0d children",Jason.get_num_children());
  33.       $display(Jason.Mike.get_object_name());
  34.       $display(Jason.Ben.get_object_hiername());
  35.       $display(Jason.weight);
  36.    end
  37. endprogram

In this small example, line 30 creates an object (Jason) for jason_c and its parent is "null", so Jason is a root component in the hierarchy. When Jason.build() is called in line 31, object Mike and Ben are created and their parent is set to Jason. So in this small system we build the following hierarchy:

[Jason]

|–[Mike]

|–[Ben]

Jason has 2 children

Mike

Jason:Ben

This hierarchy can be printed by vmm_object method print_hierarchy().

Please note that unlike Verilog modules and instances where the hierarchy is defined as per the Verilog LRM, the VMM1.2 parent-child hierarchy is really user defined. It depends on how "parent" argument is specified when the object is created, and not on where the object variable is declared or created.

As for the component name, although you may choose to specify a different name as the variable name, it is a good practice to keep it consistent, which makes the code more readable and avoids confusion.

From the above example, you can find that the hierarchical name for object Jason.Ben is "Jason:Ben". VMM1.2 uses ":" as the hierarchical separator instead of ".". The reason is that this hierarchical name is actually a made-up name, and we want to differentiate it from the semantic hierarchical reference name specified in Verilog/SystemVerilog which uses "." as the separator.

There are many methods provided in VMM1.2 which help users to work with the parent-child hierarchy. Some of these methods are:

  • find_child_by_name(): finds the named object relative to this object
  • get_num_children(): gets the total number of children for this object
  • get_nth_child(): returns the nth child of this object
  • get_object_hiername(): gets the complete hierarchical name of this object
  • get_parent_object():returns the parent of this object
  • get_root_object(): gets the root parent of this object
  • get_typename(): returns the name of the actual type of this object
  • is_parent_of(): returns true, if the specified object is a parent of this object
  • print_hierarchy(): prints the object hierarchy
  • Set_parent_object(): sets or replaces the parent of this object

Dr. Ambar Sarkar has explained how users can traverse the hierarchy in his blog post.

This parent-child hierarchical infrastructure is one of the most important mechanisms in VMM1.2. Many other VMM1.2 features rely on this infrastructure:

1.   Implicit phasing

Implicit phasing is new in VMM1.2. In implicit phasing, structural components (transactors) are aligned with each other automatically. The phase specific methods are called automatically throughout the whole hierarchy in a top-down (for functions) or forked (for tasks) mode. Thus implicit phasing makes integration of Verification IPs into the simulation environment or other structural components a lot easier. Other VMM1.2 users also benefit from implicit phasing when building complicated verification environments.

2.   Factory replacement

Factory is an important feature that enables flexibility and reuse inside a verification environment. Because of the parent-child hierarchy, users can replace components, generated transactions or scenarios with their extension type or other objects by specifying hierarchy path and names. Support for regular expression for specifying hierarchies and names make this utility very powerful.

For example, in the following code segment, we override the type mike_c for Mike with mike_ext :

      mike_c::override_with_new("@%Jason:Mike",mike_ext::this_type,log);

3.   Hierarchical configuration

In addition to supporting runtime configuration through command-line options or files, using the parent-child hierarchy VMM1.2 also supports configuration of components by specifying their hierarchical path and name. All these configuration utilities are provided through vmm_opts.

For example, in the following code segment, we set the property weight of object Jason to 10 using hierarchical configuration:

   vmm_opts::set_int("Jason:weight",10);

Like factory, users can also use regular expression with hierarchical configuration.

If you have watched "Growing Pains", you know that I am not quite accurate when I say

   Jason has 2 children

He indeed has three…

Have fun with VMM1.2. J

Posted in Tutorial, VMM 1.2 | No Comments »

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

Leverage on the built-in callback inside vmm_atomic_gen and be productive with DVE features for VMM debug

Posted by srini on February 7th, 2010

Srinivasan Venkataramanan, CVC Pvt. Ltd.

Rashmi Talanki, Sasken

John Paul Hirudayasamy, Synopsys

During a recent Verification environment creation for a customer we had to tap an additional copy/reference of the generated transaction to another component in the environment without affecting the flow. So one producer gets more than one consumer (here 2 consumers). As a first time VMM coder the customer tried using “vmm_channel::peek” on the channel that was connecting GEN to BFM. Initially it seemed to work, but with some more complex code being added across the 2 consumers for the channel, things started getting funny – one of the consumers received the transactions more than once for instance.

The log file looked like:

@ (N-1) ns the transaction was peeked by Master_BFM  0.0.0

@ (N-1) ns the transaction was peeked by Slave_BFM 0.0.0

.

.(perform the task)

.

@N ns the Master_BFM  get the transaction 0.0.0

@N ns the transaction was peeked by Slave_BFM 0.0.0

@N ns the transaction was peeked by Master_BFM 0.0.1

@N ns the transaction was peeked by Slave_BFM 0.0.1

With little reasoning from CVC team, the customer understood the issue quickly to be classical race condition of 2 consumers waiting for same transaction. What are the options, well several indeed:

1. Use vmm_channel::tee() (See our VMM Adoption book http://systemverilog.us/vmm_info.html for an example)

2. Use callbacks – a flexible, robust means to provide extensions for any such future requirements

3. Use vmm_broadcaster

4. Use the new VMM 1.2 Analysis Ports (See a good thread on this: http://www.vmmcentral.org/vmartialarts/?p=860 )

The customer liked the callbacks route but was hesitant to move towards the lengthy route of callbacks – for few reasons (valid for first timers).

1. Coding callbacks takes more time than simple chan.peek(), especially the facade class & inserting at the right place

2. She was using the built-in `vmm_atomic_gen macro to create the generator and didn’t know exactly how to add the callbacks there as it is pre-coded!

Up for review, we discussed the pros and cons of the approaches and when I mentioned about the built-in post_inst_gen callback inside the vmm_atomic_gen she got a pleasant surprise – that takes care of 2 of the 4 steps in the typical callbacks addition step as being recommended by CVC’s popular DR-VMM course (http://www.cvcblr.com/trng_profiles/CVC_DR_VMM_profile.pdf).

Step-1: Declaring a facade class with needed tasks/methods

Step-2: Inserting the callback at “strategic” location inside the component (in this case generator)

This leaves only the Steps 3 & 4 for the end user – not bad for a robust solution (especially given that the Step-4 is more of formality of registration). Now that the customer is convinced, it is time to move to coding desk to get it working. She opened up vmm.sv and got trapped in the multitude of `define vmm_atomic_gen_* macros with all those nice looking “ \ “ at the end – thanks to SV’s style of creating macros with arguments. Though powerful, it is not the easiest one to read and decipher – again for a first time SV/VMM user.

Now comes the rescue in terms of well proven DVE – the VCS’s robust GUI front end. Its macro expansion feature that works as cleanly as it can get is at times hard to locate. But with our toolsmiths ready for assistance at CVC, it took hardly a few clicks to reveal the magic behind the `vmm_atomic_gen(icu_xfer). Here is a first look at the atomic gen code inside DVE.

clip_image002

Once the desired text macro is selected, DVE has a “CSM – Context Sensitive Menu” to expand the macro with arguments. It is “Show à Macro”, as seen below in the screenshot.

clip_image004

With a quick bang go on DVE – the Macros expander popped up revealing the nicely expanded, with all class name argument substituted source code for the actual atomic_generator that gets created by the one liner macro. Along with clearly visible were the facade class name and the actual callback task with clear argument list (something that’s not obvious by looking at standard vmm.sv).

clip_image006

Now, what’s more – in DVE, you can bind such “nice feature” to a convenient hot-key if you like (say if you intend to use this feature often). Here is the trick:

Add the following to your $HOME/.synopsys_dve_usersetup.tcl

gui_set_hotkey -menu “Scope->Show->Macro” -hot_key “F6″

Now when you select a macro and type “F6” – the macro expands, no rocket science, but a cool convenient feature indeed!

Voila – learnt 2 things today – the built-in callback inside the vmm_atomic_gen can save more than 50% of coding and can match up to the effort (or the lack of) of using simple chan.peek(). The second one being DVE’s macro expansion feature that makes debugging a real fun!

Kudos to VMM and the ever improving DVE!

Posted in Debug, VMM, vmm_channel, vmm_xactor | No Comments »

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

Verification in the trenches: Implementing Complex Synchronization Between Components Using VMM1.2

Posted by Ambar Sarkar on February 5th, 2010

ambar Dr. Ambar Sarkar, Chief Verification Technologist, Paradigm Works Inc.

Why is it tricky to get transactors and other verification components to work in sync with each other, especially  if they come from different projects?  It is likely that they worked well within their source projects,  but their phases (build, configure, reset, start, shutdown etc) were implemented quite differently compared to other components. These differences are usually driven by the inherent protocol requirements or team preferences. For example, consider the verification of an SOC with an AXI  host interface and a PCIe Root Complex. You will likely get your host interface transactor out of reset and execute a configuration sequence before you let your PCIe end point transactor send in requests. So you would not want to run the phases of these two transactors in lock step.

While there are countless ways to implement the phases and their sequencing, one can broadly classify a component  as being either explicitly or implicitly driven, depending on how its phases are invoked.

Implicit phasing: In my earlier post, we discussed how one can often easily coordinate the execution of various verification components. Simply put, as long as one is able to distribute the execution of the component between predetermined methods (called phases), the components can execute in lock-step with one another without requiring any additional coding by the verification engineer. This is called implicit phasing. Implicit phasing may suffice in many cases, but the challenge is to agree on the same set of phases and their sequencing. You basically will need a way to define additional  phases and potentially even rearrange their implicit calling sequence.

Explicit phasing: In contrast, explicit phasing requires the environment writer to explicitly call and synchronize the phases of the components. Typically, it takes some work to get such components to play well with one another.  This happens more often for legacy or externally developed components. In such cases, the  developers may not have known about the predetermined phases so they could not have broken down the implementation quite the way the target environment expects. Explicit phasing is often unavoidable in environments with components from multiple sources, since you may need to carefully control and coordinate the phases by hand to accommodate their differing implementation assumptions.

So the challenge we are discussing today is really about making these explicit and implicit phased components get their phases to match and cooperate during their phase transitions.

This is where vmm_timeline helps. Simply put, vmm_timeline object encapsulates your implicitly phased object and allows it to be called as an explicitly phased object.  It lets you define your own phases and the sequence in which you want to execute them. The ability to customize phases is critical, as you may need to define additional phases to fit in with the way the explicitly phased target  environment expects its phases to execute.

Here is an example that shows how an implicitly-phased component(my_implicit_comp) is being executed within an explicitly-phased my_env. Notice how the my_tl(derived from vmm_timeline) is used.

Step a. Create a vmm_timeline object and instantiate the components

// Implicitly phased comp
class my_implicit_comp extends vmm_group;
  `vmm_typename(my_implicit_comp
  …
  function new(string name = "",
                         vmm_object parent = null);
    super.new("my_implicit_comp", name, null);
    super.set_parent_object(parent);
  endfunction

  virtual function void build_ph();
    super.build_ph(); 
    …

  endfunction
  …

endclass

// Create a vmm_timeline class to wrap this implicitly phased component

class my_tl extends vmm_timeline
  `vmm_typename(my_tl) 
   my_implicit_comp comp1; 
 

  function new(string name = "", 
    vmm_object parent = null);
    super.new("my_tl", name, parent);
  endfunction

  virtual function void build_ph();  
    super.build_ph();

    // Create an instance
   
this.comp1 = my_implicit_comp::create_instance(this, “comp1”); 
  endfunction

endclass

Step b. Instantiate in top-level vmm_env and call out the implicit methods

// Instantiate the vmm_timeline object in the top environment and call its phases explicitly.

class my_env extends vmm_env;
  `vmm_typename(my_env)
  my_tl tl;

  function new();
    super.new("env");
  endfunction

  virtual function void build();
    super.build();
    this.tl = new("tl", this);
  endfunction

  virtual task start();
    super.start();
    tl.run_phase("start");
   `vmm_note(log, "Started…");
  endtask

  virtual task wait_for_end();
     super.wait_for_end();
     fork
       // run_test phase corresponds best here
       tl.run_phase("run_test");
       begin
         `vmm_note(log, "Running…");
         #100;
       end
    join
  endtask

  virtual task stop();
    super.stop();

    // shutdown phase corresponds best here
    tl.run_phase("shutdown");
    `vmm_note(log, "Stopped…");
  endtask

Note that the converse is also true. Explicitly phased components can be incorporated into implicitly driven environments. You need to encapsulate them in a parent class derived from the vmm_subenv class and define how each implicit phase of the parent class can be mapped to the proper explicit phase(s) of the original component. Then you can simply instantiate this parent class in the target environment. For further details, search the string “Mixed Phasing” in the VMM 1.2 User Guide.

In summary, vmm_timeline helps you manage different phasing and sequencing needs of verification components by making it easier for explicitly and implicitly phased components to interact. No wonder that under the hood of VMM1.2, vmm_timeline is used to implement advanced features such as multi-test concatenation.

This article is the 4th in the Verification in the trenches series. Hope you found this article useful. If you would like to hear about any other related topic, please comment or drop me a line at ambar.sarkar@paradigm-works.com. Also, if you are starting out fresh, please check out the free VMM1.2 environment generator at http://resourceworks.paradigm-works.com/svftg/vmm .

Posted in Uncategorized | No Comments »

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

The Curious World of Ports and Exports in VMM 1.2

Posted by John Aynsley on February 4th, 2010

JohnAynsley

John Aynsley, CTO, Doulos

 

 

In a previous post I discussed the new blocking transport interface of VMM 1.2, and described how a producer can call the b_transport method implemented by a consumer. In this post, I will describe how to connect the producer to the consumer using the new features of VMM 1.2.

The new features discussed here were inspired by the SystemC TLM-2.0 standard. In SystemC, two modules wishing to communicate get connected using so-called “ports” and “exports”. Although the underlying concept is both elegant and powerful, the terminology “port” and “export” often seems to cause confusion. Since this is now part of VMM 1.2, I will explain. Let us consider a simple example of a producer calling the b_transport method implemented in a consumer.

class my_tx extends vmm_data; // User-defined transaction class
    …

class producer extends vmm_xactor;
    vmm_tlm_b_transport_port #(producer, my_tx) m_port;
    my_tx tx;
    …

    m_port.b_transport(tx, delay);
    …

class consumer extends vmm_xactor;
    vmm_tlm_b_transport_export #(consumer, my_tx) m_export;
    task b_transport(int id = -1, my_tx trans, ref int delay);
    …

class my_env extends vmm_group;
    producer m_producer;
    consumer m_consumer;

    virtual function void connect_ph;
        m_producer.m_port.tlm_bind( m_consumer.m_export );
    endfunction
    …

What is happening here is that the producer is calling b_transport through a port, the consumer is providing an implementation of b_transport using an export, and the top-level environment is connecting (or “binding”) the port to the export. The tlm_bind method is creating the link between the port and the export such that when the producer calls b_transport, it is the implementation of b_transport within the consumer that actually gets called.

Both the port and the export declarations are parameterized with the type of the transactor (producer/consumer) and the type of the transaction (my_tx). You may notice that the implementation of b_transport has an extra int id argument. This can be used to distinguish between transactions arriving from different producers. I will discuss this in my next blog post.

The purpose of ports and exports is to provide a structured way of making method calls between VMM transactors (or SystemC modules) such that the dependencies between each transactor and its environment can be minimized. To call b_transport the code within the producer only need refer to the port and has no direct dependencies on any code outside that transactor. Similarly, to call the b_transport method implemented within the consumer, the environment only need refer to the export. It is only when the port and export are connected within the connect_ph method of the environment that a specific dependency is established between the producer and consumer transactors.

What about those terms “port” and “export”? The term port was originally borrowed from VHDL and Verilog. In SystemC, a port allows an interface method call to be made up-and-out-of a module. When SystemC was enhanced to add the mirror image construct that allows an interface method call to be made down-and-into a module the term “export” was chosen because an export provides or “exports” an interface, whereas a port “imports” an interface.

If we could re-write history, I guess we might have chosen the term “import” instead of “port”. Perhaps that would have caused less confusion. Or maybe not!

Posted in Uncategorized | 1 Comment »

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

Shorthand macros with user defined implementation

Posted by Vidyashankar Ramaswamy on February 3rd, 2010

 

The transaction class objects are created by extending the base class vmm_data. Vmm_data class has many virtual methods which need to be implemented by the extended class. This can become a laborious process as this is done for each extended class object. However, a set of shorthand macros available to help minimize the amount of code required to create these data class extensions. These shorthand macros can be used on per data member basis which provides a default implementation of all the require methods. Following is an example.

      . . . 
1    class apb_trans extends vmm_data;
2       `vmm_typename(apb_trans)

3       rand enum {READ, WRITE} kind;
4       rand bit [31:0] addr;
5       rand logic [31:0] data;

6       `vmm_data_member_begin(apb_trans)
7            `vmm_data_member_scalar(addr, DO_ALL)
8            `vmm_data_member_scalar(data, DO_ALL)
9            `vmm_data_member_enum(kind, DO_ALL)
10     `vmm_data_member_end(apb_trans)
11     …
12  endclass: apb_trans 
      . . .

 

The class properties are declared as shown in Line number 3 to 5. Line number 6 and 10 marks the start and end of the shorthand macro section. Based on the variable type , the appropriate macros are called (line number 7 to 9). As the name says “DO_ALL” means use this variable in all the virtual method implementations. Say if you want to exclude the “kind” property from printing, then you can use “DO_ALL – DO_PRINT”. Please refer to the VMM user guide for more details on this.

User defined implementation

Shorthand macros provide the default implementation for all the vmm_data virtual methods. If you want to override the default implementation of a method, then you have to implement the do_* method. For example say you want to change the implementation for byte_size, You can still use shorthand macros but need to explicitly implement the apb_trans::do_byte_size() method and force VMM not to provide the default implementation. The example code is shown below.

1   virtual function int unsigned do_byte_size (int kind = –1) ;
2       . . .
3       . . .
4   endfunction

 

Constructor replacement

In some cases a transaction class might need a custom constructor with different arguments. Please note that the explicit constructor implementation is done using the shorthand macro `vmm_data_new() as shown below (line 1). The new implementation should follow the macro definition (Line number 2 to 5). It is also important to provide default values for the arguments to make the transaction class factory-enabled (Line number 2).

1   `vmm_data_new(apb_trans)
2   function new (vmm_log log=null, vmm_object parent=null, string name=””);
3       super.new(. . ., . . .) ;
4       . . . 
5   endfunction

 

The shorthand macros are also available for messaging service (vmm_log), vmm_unit configuration, RTL configuration (vmm_rtl_config) and TLM ports. For the complete list, please refer to the VMM user guide.

Posted in Uncategorized | No Comments »

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

Moving from Block levels tests to a Chip level in a reusable world…

Posted by Srivatsa Vasudevan on January 29th, 2010

In modern designs, one observes a fairly high degree of reuse in designs and verification environments. Cores and blocks are regularly reused from one design to another and increasing demands are being placed on quickly getting a functional chip out of the door to meet a customer need.

In such a world, schedule and functional chips are key to success.

At the block level, the design and verification engineers use a white box testing methodology to write tests that will completely test out the block under test. Usually in these environments, various blocks that interact with the design under test will usually be stubbed out or replaced with transactors as the case may be.

In an ideal world, the tests which were written once for the block level verification if reused 100% at the chip level would mean that the reuse was at 100%. That never usually happens.

Let’s look at the illustration between a verification engineer who’s busy building cores at the block level, comes in to talk to the verification lead of a chip he’s delivering to.

 

method_2

 

 

 

 

 

 

 

It is obvious that the requirements and goals for any tests at chip level are quite different. Most chip leads would first like to ensure that the core in question is indeed integrated at the chip level properly and the core does indeed talk to other cores properly.   The core ideally is a black box that would perform the functionality expected of it. The perspective on design verification maintained by the chip level verification engineer is usually vastly different from the block level perspective

Usually interconnect, clocking, reset, power-up/down, programming sequences, power domains, timing etc would be some examples  of things that come to mind when writing tests at the chip level. Once these major goals are met, one looks at other optimizations while attempting to verify the device.

That said, how does one now take the tests that are written for the block and re-use them at the top level especially if the tests are intended for different reasons? Is it even possible to minimize the effort in doing so? Don’t we need a mechanism to ensure that things that weren’t completely exercised at the block/subsystem level are tested at core level? What do we need for the handoff? How do the various verification class libraries now play a part in this picture?

The series herein explores how this is all done. A proper top/down methodology Coupled to a bottom’s up methodology can yield excellent results with minimal overlap.

Stay Tuned….

Posted in Uncategorized | No Comments »

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

Should you use VMM Callbacks or TLM Analysis ports?

Posted by Prashanth S. on January 27th, 2010

With the addition of OSCI TLM2.0 features in VMM1.2, it is now becoming possible to use analysis ports for broadcasting transaction information from a transactor to any components such as scoreboard, functional coverage models, etc…

But, does it mean that VMM callbacks which are traditionally used for this purpose are not required anymore?

In short, the answer is no: both analysis port and callback have their own advantages. Before getting into more details, let me show you two simple examples I’ve created to encompass the analysis port and callback usage.

Communication through analysis port

Step1: In my transactor, I’ve simply instantiate a vmm_tlm_analysis_port instance [line 2]and invoke the analysis_port.write() method [line 8] to post the tr object to multiple subscribers.

1. class cpu_driver extends vmm_xactor;

2.    vmm_tlm_analysis_port#(cpu_driver, cpu_trans) analysis_port;

3.    virtual function void build_ph();

         analysis_port = new(this, “cpu_analysys_port”);

4.    endfunction

5.    virtual task main();

6.       cpu_trans tr;

7.       …

8.       analysis_port.write(tr);

9.     endtask

10. endclass

Step2: Let’s now see how to model a subscriber such as a coverage model. In this model, I’ve simply instantiated a vmm_tlm_analysis_export instance [Line 3] and provided the implementation of the write method() [Line 4]. Once the transactor posts a transaction to the write method, the coverage model write_CPU gets called as well and receives this transaction, which can be sampled and covered.

1. class cntrlr_cov extends vmm_object;

2.    vmm_tlm_analysis_export #(cntrlr_cov, cpu_trans)

3.    cpu_export = new(this, "CpuAnExPort");

4.    virtual function void write(int id=-1, cpu_trans tr);

5.        this.cpu_tr = tr;

6.        CG_CPU.sample();

7.   endfunction

8. endclass

Step3: The last important part is to bind my transactor and my subscribers. This is simply done by using the transactor tlm_bind() method. Of course we should be invoked for all subscribers.

1. class tb_env extends vmm_group;

2.    cpu_driver drv;

3.    cntrlr_cov cov;

4.    function void connect_ph();

5.       drv.analysis_port.tlm_bind(cov.cpu_export);

6.    endfunction

7. endclass

As you can see in these examples, analysis ports are easy to use. But they are not meant to allow subscribers to modify the transaction and are very much restricted to only one method with only one argument, i.e. write().

Communication through callback

Step1: I’ve created a generic callback that is nothing but a container that extends the vmm_xactor_callbacks base class with empty virtual methods [Line 1-3]. I have deliberately left these methods empty so that they can overridden for any particular usage such as a coverage model, scoreboard, etc.Next step is have my transactor calling this callback once the transaction is available [Line 10]

1. class cpu_driver_callbacks extends vmm_xactor_callbacks;

2.    virtual function void write(cpu_trans tr); endtask

3. endclass

4.

5. class cpu_driver extends vmm_xactor;

6.

7.    virtual task main();

8.       cpu_trans tr;

9.       …

10.      `vmm_callback(cpu_driver_callbacks, write(tr));

11.   endtask

12. endclass

Step2: Now, I can extend the previous class and provide the implementation the coverage model directly in the callback

1. cpu2cov_callback extends cpu_driver_callbacks;

2.    cntrlr_cov cov;

3.    virtual function void write(cpu_trans tr);

4.       cov.cpu_tr = tr;

5.       cov.CG_CPU.sample()

6.    endfunction

7. endclass

Step3: Once both transactor and subscribers are implemented, I can simply instantiate them in my implicitly phased environment [Line2-3]. Then, I can register the extended callback instance to the transactor by using append_callback() method Line[6]. Note that this registration happens in the connect phase.

1. class tb_env extends vmm_group;

2.    cpu_driver drv;

3.    cntrlr_cov cov;

4.    function void connect_ph();

5.       cpu2cov_callback cbk = new(cov);

6.       drv.append_callback(cbk);

7.    endfunction

8. endclass

As you can see in above examples, callbacks are also easy to use. As opposed to analysis port, their subscribers can possibly modify the transaction and can contain multiple methods with any kind of arguments.

Comparison

1. Analysis ports follow OSCI TLM2.0 standard.

2. Unlike callbacks, user do not need to create class with empty virtual methods, instead pre-defined method write() is used

3. Analysis ports can only broadcast one transaction as opposed to callbacks where you can define the method arguments.

4. Analysis port method write() is a function whereas callback class can model its empty virtual methods as tasks or void functions which gives you flexibility to control the transactor as well (like inserting delays, injecting error mechanism, etc)

5. The `vmm_tlm_analysis_export() macro must be used to create a new analysis façade when a class need to have more than one analysis export

6. Analysis ports can only be used by classes based on vmm_object. Callbacks can be used by any class.

In summary

- If a transactor needs to broadcast only one transaction, then analysis port can be used. If a transactor needs to send different kinds of information at different points, and provide some hooks for modeling variant functionality (like inserting delays, error injections, etc), then callbacks is the way to go. Both analysis port and callback can also be provided, publishing analysis port after callbacks.

Posted in Uncategorized | 2 Comments »

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

Using factories in an implicitly-phased environment

Posted by Kiran Maiya on January 25th, 2010

 

You might have read the neat article posted by Wei-Hua about the class factory in this forum. You may wonder what could be the best way to code this in an implicitly-phased environment. In this article I will be discussing how transactions based on vmm_data can be made factory enabled using VMM-1.2 based factory utility in an implicitly-phased environment. We will see how transaction objects can be replaced using these factory utilities. I’ve deliberately attempted not to introduce too many concepts such as macros to implement default methods. I will post an article on replacing transactors shortly.

As a test bed let us take a simple concept of generating raw pixels using generators. These raw pixels are modeled as vmm_data objects. This pixel object contains RGB color information in the variable clr which is a rand property. A discriminant member called type_e determines the color that this pixel object can take.


/* 1 */ `include “vmm.sv”
/* 2 */ program main ;
/* 3 */
/* 4 */ //// Class: Pixel ////
/* 5 */
/* 6 */ class Pixel extends vmm_data ;
/* 7 */ `vmm_typename(Pixel)
/* 8 */ static vmm_log log = new(”Pixel”, “class”) ;
/* 9 */ typedef enum {Red, Green, Blue, ANY} clr_type_e ;
/* 10 */
/* 11 */ typedef enum {
/* 12 */         R1, R2, R3, R4, R5,
/* 13 */         G1, G2, G3, G4, G5,
/* 14 */         B1, B2, B3, B4, B5
/* 15 */         } clrcode_e ;
/* 16 */ rand clrcode_e       clr ;
/* 17 */ clr_type_e type_e = ANY ;
/* 18 */
/* 19 */ constraint con
/* 20 */ {
/* 21 */ if(type_e == Red)
/* 22 */    clr inside {R1, R2, R3} ;
/* 23 */ if(type_e == Green)
/* 24 */    clr inside {G1, G2, G3} ;
/* 25 */ if(type_e == Blue)
/* 26 */    clr inside {B1, B2, B3} ;
/* 27 */ }

 

In addition to the constructor, the factory override methods needs to implement two predefined methods of vmm_data class, copy() and allocate(). These methods return the right type of the object that was enabled as factory. This can also be implemented using vmm_data short-hand macros, but for the purpose of clarity the full implementation is shown.

/* 28 */

/* 29 */ /// Function: new()

/* 30 */ function new(string name = “”, vmm_object parent = null) ;

/* 31 */ super.new(log) ;

/* 32 */ endfunction: new

/* 33 */

/* 34 */ /// Function: copy()

/* 35 */ virtual function vmm_data copy(vmm_data to = null) ;

/* 36 */ Pixel me ;

/* 37 */ if(to == null)

/* 38 */ me = new (this.get_object_name(), this.get_parent_object()) ;

/* 39 */ else if(!$cast(me, to))

/* 40 */ `vmm_fatal(log, “Can’t copy to non-Pixel instance”) ;

/* 41 */

/* 42 */ /// Copy members here

/* 43 */ me.clr = this.clr ;

/* 44 */ me.type_e = this.type_e ;

/* 45 */ return me ;

/* 46 */ endfunction: copy

/* 47 */

/* 48 */ /// Function: allocate()

/* 49 */ virtual function vmm_data allocate() ;

/* 50 */ Pixel me = new () ;

/* 51 */ return me ;

/* 52 */ endfunction: allocate

 

It is a good practice to have the psdisplay() method implemented. This will be handy for debugging the simulation and/or for a better report log.

/* 53 */

/* 54 */ /// psdisplay()

/* 55 */ virtual function string psdisplay(string prefix = “”);

/* 56 */ $sformat(psdisplay, “%s clr = %s, type_e = %s”,

/* 57 */ prefix, clr.name(),type_e.name()) ;

/* 58 */ endfunction: psdisplay

 

Finally make this Pixel class to be factory enabled by using the factory utility macro as shown below.

/* 59 */

/* 60 */ `vmm_class_factory(Pixel) // Introduce factory API

/* 61 */

/* 62 */ endclass: Pixel

 

This base pixel class can be customized to different variants by forcing the discriminant type as shown below. Objects of these types can be later used to override the factory. These class extensions should also be enabled as factories using the factory utilities as shown below.

/* 63 */

/* 64 */ //// GreenPixel ////

/* 65 */

/* 66 */ class GreenPixel extends Pixel ;

/* 67 */ /// new()

/* 68 */ function new(string name = “”, vmm_object parent = null) ;

/* 69 */ super.new(name, parent) ;

/* 70 */ type_e = Green ;

/* 71 */ endfunction: new

/* 72 */

/* 73 */ constraint con_GreenPixel

/* 74 */ {

/* 75 */ type_e == Green ;

/* 76 */ }

/* 77 */

/* 78 */ /// copy()

/* 79 */ virtual function vmm_data copy(vmm_data to = null) ;

/* 80 */ GreenPixel me ;

/* 81 */

/* 82 */ if(to == null)

/* 83 */ me = new ;

/* 84 */ else if(!$cast(me, to))

/* 85 */ `vmm_fatal(log, “Can’t copy to non-Green Pixel instance”) ;

/* 86 */

/* 87 */ /// Copy members here

/* 88 */ return super.copy(me) ;

/* 89 */ endfunction: copy

/* 90 */

/* 91 */ /// allocate()

/* 92 */ virtual function vmm_data allocate() ;

/* 93 */ GreenPixel me = new() ;

/* 94 */ return me ;

/* 95 */ endfunction: allocate

/* 96 */

/* 97 */ `vmm_class_factory(GreenPixel)

/* 98 */

/* 99 */ endclass: GreenPixel

 

In real environment, pixels streams are generated using a custom pixel generators based of vmm_xactors or use one of the pre-defined generators. I will not discuss the generators in this article for the sake of simplicity. However a simplistic approach is taken here to show the generation in an implicit environment by utilizing the run_ph() of a timeline class. The driver logic is mimicked in the run_ph() method of PixelTimeline which is an extension of vmm_timeline. This method it is implicitly triggered during the run phase of the timeline. Transaction object tr which is a Pixel object, is instanced at run-time using the create_instance() method rather than the traditional constructor new(). Although the object instantiation via the factory can also be carried out in the constructor of the transactor or the build phase of the generator (timeline here), this has a disadvantage. With the create_instance() called inside pre-run phase of the generator, it allows only one opportunity for effectively replacing the factory instance before it is created. By calling the create_instance() factory method repeatedly at run-time, it allows the factory to override dynamically as required by testcases.

/* 100 */

/* 101 */ //// PixelTimeline ////

/* 102 */

/* 103 */ class PixelTimeline extends vmm_timeline ;

/* 104 */ `vmm_typename(PixelTimeline)

/* 105 */ Pixel tr ;

/* 106 */ bit done = 0 ;

/* 107 */

/* 108 */ /// Function: new()

/* 109 */

/* 110 */ function new(string name = “”, string inst = “”, vmm_object parent = null) ;

/* 111 */ super.new(get_typename(), name) ;

/* 112 */ //tr = Pixel::create_instance(this, “Fact”) ; // Not advisable

/* 113 */ endfunction : new

/* 114 */

/* 115 */ virtual function void build_ph() ;

/* 116 */ // Not recommended to call create_instace

/* 117 */ endfunction: build_ph

/* 118 */

/* 119 */ /// Task: myrun

/* 120 */

/* 121 */ task run_ph() ;

/* 122 */ tr = Pixel::create_instance(this, “Fact”) ; // This is recommended

/* 123 */ repeat(5)

/* 124 */ begin

/* 125 */ tr.randomize() ;

/* 126 */ tr.display(”::”) ;

/* 127 */ end

/* 128 */ $display(”");

/* 129 */ endtask: run_ph

/* 130 */

/* 131 */ endclass: PixelTimeline

Having the environment ready, let us see how the objects can be replaced using the factory utility. A typical environment will be created by extending vmm_group. Environment creation is out of scope of this topic and will not be discussed here; instead a simple program block is shown creating the relevant blocks.

/* 132 */

/* 133 */ //// Program

/* 134 */ initial

/* 135 */ begin

/* 136 */ PixelTimeline tl ;

/* 137 */ Pixel tr = new ;

/* 138 */ GreenPixel gpxl ;

/* 139 */ vmm_log log = new(”A”, “B”) ;

/* 140 */

/* 141 */ tl = new (”tl”, “Main”, null) ;

 

Following code shows a free running generator with no customization on the pixels.

/* 142 */

/* 143 */ /// Free running simulation, any color

/* 144 */ tl.run_phase() ;

 

The above code will generate pixels of all colors and the output will be as follows

:: clr = R3 :: type_e = ANY

:: clr = G3 :: type_e = ANY

:: clr = B3 :: type_e = ANY

:: clr = G3 :: type_e = ANY

:: clr = B5 :: type_e = ANY

 

Factory replacement can be done in two ways,

  1. override by transaction copy using a reference object or
  2. override by transaction type

While overriding by transaction copy the factory method override_with_copy() should be used. This will call the copy() method to return the right type to be overridden. Here one needs to have a reference copy that will be passed as an argument while replacing factory. In the following code snippet it is shown that a local transaction copy is pixilated to Red using the type discriminant. This copy is used to override the transaction object as shown below.

/* 145 */

/* 146 */ /// Override with a transaction object copy of type Red

/* 147 */ tr.type_e = Pixel::Red ;

/* 148 */ Pixel::override_with_copy(”@%*”, tr, tr.log, `__FILE__, `__LINE__) ;

/* 149 */ tl.reset_to_phase(”start_of_sim”) ;

/* 150 */ tl.run_phase() ;

/* 151 */

This part of the code will generate the red pixels and the output will be as follows

:: clr = R1 :: type_e = Red

:: clr = R3 :: type_e = Red

:: clr = R2 :: type_e = Red

:: clr = R3 :: type_e = Red

:: clr = R1 :: type_e = Red

 

While overriding by transaction type the factory method override_with_new() should be used. This will call the allocate() method to return the right type. To specify the type of object to be manufactured one can use the predefined method this_type() which returns the type of the specified object. Here in the following example GreenPixel type is used to override the factory.

/* 152 */

/* 153 */ /// Override with new type-Green

/* 154 */ Pixel::override_with_new(”@%*”, GreenPixel::this_type,

/* 155 */ log, `__FILE__, `__LINE__) ; // Strongly typed

/* 156 */ tl.reset_to_phase(”configure”) ;

/* 157 */ tl.run_phase() ;

/* 158 */

/* 159 */ #100 $finish ;

/* 160 */ end

/* 161 */ endprogram: main

/* 162 */

The above code will generate the green pixels and the output will be as follows

:: clr = G2 :: type_e = Green

:: clr = G2 :: type_e = Green

:: clr = G1 :: type_e = Green

:: clr = G3 :: type_e = Green

:: clr = G3 :: type_e = Green

 

It is worthwhile to notice that the factory override methods are strongly typed. There isn’t any room for accidental type mismatch. Tool will automatically error out during compilation. For example, the following code is illegal and the tool throws a compilation error.

Data::override_with_new(”@%*”, GreenPixel::this_type,

log, `__FILE__, `__LINE__) ; // Strongly typed

 

    Where Data is not an extension of type Pixel.

Hope you all will have a colorful experience with VMM-1.2 factory utility (no pun intended).

Posted in Uncategorized | No Comments »

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

Proxy Coverage

Posted by Andrew Piziali on January 22nd, 2010

Andy's Picture_1

Andrew Piziali, Independent Consultant

I was recently talking with a friend about a subject near and dear to my heart: functional coverage. Our topic was block level or subsystem coverage models that are not ultimately satisfied until paired with corresponding system level context. The question at hand was “Is it possible to use conditional block level coverage as a proxy for system level coverage by recording coverage points at the block level, conditioned upon subsequent observation of system level context?”

For example, let’s say I have an SoC DUV (design under verification) that contains an MPEG-4 AVC (H.264) video encoder with a variety of features enabled by configuration registers. I need to observe the encoder operating in a subset of modes—referred to as “profiles” in H.264 parlance—defined by permutations of the features. In addition, each profile is only stressed when the encoder is used by an application for which the profile was designed. For example, the “high profile” is intended for high definition television while the “scalable baseline profile” was designed for mobile and surveillance use. The profiles may be simulated at the block level but the applications are only simulated at the system level. How do I design, implement and fill this H.264 coverage model so that the model is populated using primarily block level simulations, with their inherent performance advantage, while depending upon system level application context? May I even use the block level coverage results as a proxy for the corresponding system level coverage?

I think this nut is tougher to crack than the black walnuts that recently fell from my tree. The last time I tried to crack those nuts I resorted to vise grips from my tool chest, leading to a number of unintended, but spectacular, results, but I digress. My friend and I were able to define a partial solution but it wasn’t at all clear whether or not a viable solution exists that leverages the speed of block level simulations. After you finish reading this post, I’d like to hear your thoughts on the matter. This is how far we got.

As with the top level design of any coverage model, we started with its semantic description (sometimes called a “story line”):

Record the encoder operating in each of its profiles while in use by the corresponding applications.

Next, we identified the attributes required by the model and their values:

Attribute Values
Profile BP, CBP, Hi10P, Hi422P, Hi444PP, HiP, HiSP, MP, XP
Application broadcast, disc_storage, interlaced_video, low_cost, mobile, stereo_video, streaming_video, video_conferencing
Feature 8×8_v_4×4_transform_adaptivity, Arbitrary_slice_ordering, B_slices, CABAC_entropy_coding, Chroma_formats, Data_partitioning, Flexible_macroblock_ordering, Interlaced_coding, Largest_sample_depth, Monochrome, Predictive_lossless_coding, Quantization_scaling_matrices, Redundant_slices, Separate_Cb_and_Cr_QP_control, Separate_color_plane_coding, SI_and_SP_slices

Then, we finished the top level design using a second order model (simplified somewhat for clarity):

clip_image001
The left column of the table serves as row headings: Attribute, Value, Sampling Time and Correlation Time. The remaining cells of each row contain values for the corresponding heading. For example, the “Attribute” names are “Profile,” “Application” and “Feature.” The values of attribute “Profile” are BP, CBP, Hi10P, etc. The time at which each attribute is to be sampled is recorded in the “Sampling Time” row. The time that the most recently sampled attribute values are to be recorded as a set is when the “application [is] started,” the correlation time. Finally, the magenta cells define the value tuples that compose the coverage points of the model.

The model is implemented in SystemVerilog, along with the rest of the verification environment and we begin running block level simulations. Since no actual H.264 application is run at the block level, we need to invent an expected application value whenever we record a coverage point defined by an attribute value set, lacking only an actual application. Why not substitute a proxy application value, to be replaced by an actual application value when a similar system level simulation is run? For example, if in a block level simulation we observe profile BP and feature ASO ({BP, *, ASO}), we could substitute the value “need_low_cost” for “low_cost” for the application value, recording the tuple {BP, need_low_cost, ASO}. This becomes a tentative proxy coverage point. Later on, when we are running a system level simulation with an H.264 application, whenever we observe a {BP, low_cost, ASO} event, we would substitute the much larger set of {BP, need_low_cost, ASO} block level events for this single system level event, replacing “need_low_cost” with “low_cost.” This would allow us to “observe” the larger set of system level {BP, low_cost, ASO} events from the higher performance block level simulations. How can we justify this substitution?

We could argue that a particular system level coverage point is an element of a set of block level coverage points because (1) it shares the same subset of attribute values with the system level coverage point and (2) the block level simulation is, in a sense, a superset of the system level simulation because it implicitly abstracts away the additional detail available in the system level simulation. Is there any argument that the profile value BP and feature value ASO are the same in the two environments? The second reason is clearly open to discussion.

This brings us back to the opening question, can we use conditional block level coverage as a proxy for system level coverage by recording coverage points at the block level, conditioned upon subsequent observation of system level context? If so, is this design approach feasible and reasonable? If not, why not? Have at it!

Posted in Coverage | 1 Comment »

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

Transactor Interfaces [VMM 1.2 style]

Posted by Vidyashankar Ramaswamy on January 18th, 2010

In my earlier blog post  I have shown how to write a configurable physical interface. This time I shall look into the slave transactor and its interfaces. A slave transactor can have all or some of the interfaces shown in the following Figure. It solely depends on the nature of the protocol and its surrounding environment. If you are developing verification IP which is used across multiple projects or groups , then you must consider the entire interface requirement and develop the VIP accordingly. Please note that I will be discussing only the analysis port [No 5] and the transport port [No 6] interfaces which are developed using VMM 1.2 features.

  image

 

Analysis Port

Analysis ports are used to share the received transaction among the other testbench components. These components can also be referred to as listeners, subscribers, observers, target or sometimes passive component. As the name says , this port is used to distribute the transaction to a single or multiple listeners for analysis. The important feature of the analysis port is that a single port can be connected to multiple subscribers. The component which is broadcasting the transaction uses “analysis port” and the observers implement the analysis export port. Each subscriber must implement the “write”  method of the vmm_tlm_analysis_export class. Here the listeners can’t modify the transaction and can only copy it’s content for analysis. Most common testbench components which can use this port are scoreboard, functional coverage, debug and reference model.

Analysis port in the producer (Initiator) model

The analysis port is implemented as follows. The port object is constructed in the transactors’s build phase. As this is a slave model, the write method is called after responding to the observed request on the bus.

File: vip_slave.sv 

. . .
//////////// SLAVE MODEL ///////////////

class vip_slave extends vmm_xactor;
   `vmm_typename(vip_slave)
   // Variables declaration
   . . .
  // Analysis port
   vmm_tlm_analysis_port#(vip_slave, vip_trans) analysis_port;
   // TLM Blocking port
   vmm_tlm_b_transport_port#(vip_slave, vip_trans) b_trans_resp_port;
   
   // Component Phases
   . . .
   extern virtual function void build_ph();
   extern virtual protected task main();
   . . .
endclass: vip_slave
. . .
/////////////// Build Phase /////////////////
function void vip_slave::build_ph();
   . . .
   // Construct the analysis port for Observers
   analysis_port = new(this, "analysis_port");
   // Construct the transport port for response modification
   b_trans_resp_port = new(this, "b_trans_resp_port");
   . . .
endfunction: build_ph
. . .

////////////// Main Method////////////////
task vip_slave::main();
   super.main();
  forever begin 
      vip_trans tr;
      int dly = 0;
      . . . 
      if (tr.kind == vip_trans::READ) begin
         // Retrieve the data
        . . .  
         // Provide the handle to the modifying transactor
         b_trans_resp_port.b_transport(tr, dly);
         . . .
         // Finally drive the data onto the bus
         . . .
      end
      else begin
// WRITE
         // Assemble the trans properties
         . . .
         // Provide the handle to the modifying transactor
         b_trans_resp_port.b_transport(tr, dly);
         . . .
         // Store the data
        . . .
      end
      . . .
      // notify the observers about this transaction
      this.analysis_port.write(tr);
      . . . 
   end
endtask:
main

 

 Analysis export port in the target (consumer) model

Following is a very simple implementation of an observer. This observer class has an instance of the tlm analysis export class and overrides the virtual function  “write”  to operate on the received transaction. Here the write method displays the received transaction from the initiator.

File: observer.sv

class observer extends vmm_object;
   `vmm_typename(observer)
   vmm_tlm_analysis_export#(observer, vip_trans) obsrv ;
   vmm_log log = new("log", this.get_object_hiername());
  
   virtual function void write (int id = -1, vip_trans tr);
      `vmm_note(log, " … From Observer: Rcvd Transaction … ");
      tr.display("");
   endfunction: write

  function new(vmm_object parent = null, string inst=””);
      super.new(parent, get_typename());
   endfunction

  /////////////// Build Phase /////////////////
  function void build_ph();
     . . .
     // Construct the analysis export port 

     obsrv = new(this, “obsrv”); 
     . . . 
   endfunction

endclass: observer

 

 Transport Interface

The transactions received by a slave can be processed by a higher layer transactor before responding to the request. In these situations TLM transport ports are used for passing transactions in a blocking/non-blocking way. This slave model uses a blocking transport port to pass the transaction to another transactor for further processing. Blocking transport, completes the transaction within a single method call and uses the forward path from initiator to target. To use this interface, the slave model should implement vmm_tlm_b_transport_port for issuing transactions and the higher layer transactor implements vmm_tlm_b_transport_export for receiving transactions. Please refer to the code shown above (vip_slave.sv).  Also note that the transaction data modification (call to b_transport method) happens before storing the WRITE data  or responding to a READ request.

In a transactor implementation, It is ok to construct and call the analysis port’s “write” method without binding it in the environment. This is not true with the transport port’s “b_transport” method and should be bound in the environment. So it is a good practice to make this port configurable (enable/disable) using vmm options. 

 

Response modifier Transactor

This transactor shows how to instantiate the tlm export class object and the implementation of the b_transport method. Following is a very simple implementation of the transport method, where the received transaction is displayed. 

File: resp_modifier.sv

class resp_modifier extends vmm_xactor;
   `vmm_typename(resp_modifier)
   vmm_tlm_b_transport_export#(resp_modifier, vip_trans) b_trans_resp_export ;   

   virtual task b_transport (int id = -1, vip_trans tr, ref int dly);
      `vmm_note(log, " …. Resp Trans Modifier …. ");
       tr.display("");
   endtask: b_transport 
 

   function new(vmm_unit parent = null, string inst=””);
      super.new(get_typename(), inst, 0, parent);
   endfunction

   /////////////// Build Phase /////////////////
   function void build_ph();
      . . .
      // Construct the transport export port 

      b_trans_resp_export = new(this, “b_trans_resp_export”); 
      . . . 
   endfunction

endclass: resp_modifier

 

Connecting it all together

The last step is to create the environment. The required components are constructed in the build phase. Connect phase is used to bind the ports appropriately as shown in the code below.  Please refer to the VMM user guide for more information on TLM 2.0 interfaces.

File: tb_env.sv
. . .
`include "vip_slave.sv"
`include "observer.sv"
`include "resp_modifier.sv"
. . . 
////////// TB ENVIRONMENT //////////////// 
class tb_env extends vmm_group; 
   `vmm_typename(tb_env)

   // VIP Instantiation 
   vip_slave slv; 
   . . .
 
   // TLM Port Declaration 
   observer        obsrv_vip_trans;
   resp_modifier trans_mod_xactor; 
   . . .
 
   // Component Phases  
   extern virtual function void build_ph(); 
   extern virtual function void connect_ph();
   . . .
endclass: tb_env
. . .

////////// Build Phase //////////////// 
function void tb_env::build_ph();
   . . .
   this.slv = vip_slave::create_instance(this, "slv", `__FILE__, `__LINE__);
 
   //Create observer component
   obsrv_vip_trans = new(this, "TRANS_OBSVR");
   // Create the response modifier transactor
   trans_mod_xactor = new(this, "RESP_MODIFIER"); 
   . . . 
endfunction: build_ph

/////////// Connect Phase //////////// 
function void tb_env::connect_ph(); 
   . . .   
   // Bind the analysis Port 
   this.slv.analysis_port.tlm_bind(obsrv_vip_trans.obsrv);
   // Bind the transport port
   this.slv.b_trans_resp_port.tlm_bind

                            (trans_mod_xactor.b_trans_resp_export); 
   . . . 
endfunction: connect_ph 
. . .

 

I hope you find this article useful. Please feel free to send me your opinion on this.

 

 

 

 

Posted in Uncategorized | No Comments »

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

VMM 1.2 – The Movie

Posted by John Aynsley on January 14th, 2010

JohnAynsley

John Aynsley, CTO, Doulos

To celebrate the release of VMM 1.2 on VMM Central, I thought I would do something a little different and share with you a video giving a brief overview of the new features, including the implicit phasing and TLM-2 communication. So grab some popcorn, sit back, and enjoy…

Posted in VMM, VMM 1.2 | No Comments »

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

Transactors and Virtual Interface

Posted by Vidyashankar Ramaswamy on January 8th, 2010

 

In my previous blog post I have shown high level view of a transactor and its properties. In this article I look into more details about developing a reusable transactor with a physical interface. There are many ways to connect a transactor to the physical interface. Everybody is aware of how we used do this in the object constructor. This is similar to hardwiring the connection in the environment. Another way is to make the interface configurable by the environment thus removing any dependency between the test, env and the DUT interface. This can be accomplished in two steps. The first step is to create an object wrapper for the virtual interface and make it as one of the properties of the transactor. The second step is to set this object using VMM configuration options either from the enclosing environment or from top level.

 

image

Step1. Developing the port object

VMM set/get options cannot be used directly to set a virtual interface. To make this work, we have to implement an object wrapper for the virtual interface. Sample code is shown below. Please note that the class name (master_port) and the interface name (vip_if) should be changed appropriately. EX – axi_master_port, axi_if… etc

File: master_port.sv
class master_port extends vmm_object;

   virtual vip_if.master mstr_if;

   function new (string name, virtual vip_if.master mstr_if);
      super.new(null, name);
      this.mstr_if = mstr_if;
      if (mstr_if != null)
         `vmm_note(log, "\n**** Master I/F created ****\n");
      else
         `vmm_error(log, "\n**** Master Interface is NULL ****\n");
   endfunction

endclass: master_port

Step2. Configuring the Virtual Interface

In the transactor’s connect phase, get a handle to the virtual interface using the vmm_opts get method. This establishes the connection between the transactor and the DUT pin interface. Please do not forget to check the object handles and print debug messages using VMM messaging service. This will greatly reduce the debug time if things are not connected properly in the environment.

File: master.sv

//////////////////// Master Model //////////////
class master extends vmm_xactor;
   `vmm_typename(master)
  
// Variables declaration
  virtual vip_if.master mstr_if;
   …
   ////////////// Connect_vitf method ////////////////
   function void connect_vitf(vip_if.master mstr_if);
   begin
      if (mstr_if != null)
         this.mstr_if = mstr_if;
      else
         `vmm_fatal(log, "Virtual port [Master] is not available"); 
   end
   endfunction: connect_vitf

   ////////////// Connect Phase ////////////////
   function void connect_ph();
   begin
      master_port mstr_port_obj;
      bit is_set;
      // Interface connection
      if ($cast(this.mstr_port_obj, vmm_opts::get_object_obj(is_set, this, "vip_mstr_port"))) begin
         if (mstr_port_obj != null)
            this.connect_vitf(mstr_port_obj.mstr_if);
         else
            `vmm_fatal(log, "Virtual port [Master] wrapper not initialized");
      end 
      …
   end
   endfunction: connect_ph
   …
endclass: master

Finally, the interface is set using vmm_opts set method in the environment. In VMM 1.2, the verification environment is created by extending the vmm_group base class object. VMM 1.2 supports both explicit and implicit phasing mechanism. In the implicit phasing mechanism, the connect phase is used to configure the virtual interface. Use the connect_vitf() method directly to have similar support in the explicit phasing mechanism. Sample code is shown below. Please note that the interface (master_if_p0) is instantiated in the top level test bench (tb_top).

File: vip_env.sv

//////////////////// Environment  //////////////

`include "vmm.sv”

class vip_env extends vmm_groups;
   `vmm_typename(vip_env)
   …
   // Variables declaration
   master_port mstr_p0;
   …
   ////////////// Build Phase ////////////////
   function void build_ph();
   begin
      …
      mstr_p0 = new("master_port", tb_top.master_if_p0);
      …
      end
   endfunction: build_ph

   ////////////// Connect Phase ////////////////
   function void connect_ph();
   begin
      bit is_set;
      …
      // Set the master port interface
      vmm_opts::set_object("VIP_MSTR:vip_mstr_port", mstr_p0, env);
      … 
   end
   endfunction
   …
endclass: vip_env

Explicit phasing environment:

Extend vmm_env to create the explicit phasing environment. The connect_vitf() method is called in the build phase of the environment. Sample code is shown below. Please note that one can use transactor iterater instead of using the object hierarchy to call the connect_vitf() method.

File: vip_env.sv

//////////////////// Environment  ////////////// 
`include "vmm.sv”

class vip_env extends vmm_env;
  
`vmm_typename(vip_env)
   …
   // VIP’s used …
   master mstr_drvr;
   …
   ////////////// Build Phase ////////////////
   function
void build();
   begin
      super.build();
      …
      // Set the master port interface
      this.mstr_drvr.connect_vitf(tb_top.master_if_p0);
      …
   end
   endfunction:
build
   …
endclass: vip_env

Please feel free to comment and share your opinion on this. In my next article I shall discuss more about the transactor’s interface and how to develop them using VMM1.2 features.

Posted in Uncategorized | 2 Comments »

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

Verification in the trenches: Creating your verification components using VMM1.2

Posted by Ambar Sarkar on December 25th, 2009

ambar Dr. Ambar Sarkar, Chief Verification Technologist, Paradigm Works Inc.

Ever wonder why it is hard to mix and match verification components from different sources and have them play nicely with the one you created? You want all of these components to execute in sync with each other through the phases of their construction, configuration, shutdown, etc. For example, if the AXI slave transactor is executing its reset phase while the PCIe stimulus generator is sending in DMA read requests to the AXI interface, you have a problem. Often, you end up adding dedicated code or using explicit synchronization objects such as events to get the right coordination. Wouldn’t it be nice if this synchronization came automatically?

This is where the vmm_unit base class introduced in VMM1.2 comes in. The basic idea is to derive your verification component from this predefined class and you are guaranteed that the verification environment will automatically synchronize its execution with the others. While there is much offered by the vmm_unit class, the following statement summarizes its real benefit:

vmm_unit class comes with a rich set of built in synchronization points.

These synchronization points are represented as predefined tasks or functions called phases. The verification engineer provides the actual implementation of these phases. The environment makes sure that all the objects derived from the vmm_unit class get their phases called in a well defined order, so that once an object moves into a phase, it is guaranteed that all its siblings have completed the previous phase. For example, once a component enters reset, you know every other associated component is either being reset or about to enter the reset state.

Couple of things to note, however. First, you do not have to provide implementation for each and every phase. If you do not define them, the default action is that this object will wait for the others to finish this phase before moving to the next one. Second, you can override, replace, or even add your own phases to introduce a finer or different synchronization scheme altogether.

So how does the implementation end up looking like? Here is a snippet from something that I coded recently for a reusable module-level verification environment. Note that I only defined a few of the phases of my own and used the default implementation for the others.

Predefined phase

Sample Code Snippet

build_ph()

  …
  // Create various functional components of this environment
  pwr_hi = new(“subenv”, "PWR_HI", this);
  pwr_pi = new("subenv”,"_PWR_PI", this);
  // Instantiate a consensus manager
  cm = new(this, {this.get_object_name(), "_CM"}, pwr_port);
  ….

configure_ph()

  // If someone built me as a sub-environmnet, take appropriate action
  if (is_subenv) begin
    // Disable the host interface driver
    …

connect_ph()

  // Connect the components as needed
  pwr_hi.chk.ana_port.tlm_bind(sb.pwr_hi_sb_chk_ap);
  pwr_pi.has_generator) pwr_pi.gen.ana_port.tlm_bind(sb.pwr_pi_sb_post_ap);

start_of_sim_ph()

// Put a diagnostic message, otherwise leave empty
`vmm_verbose(log, "Starting simulation");

reset_ph()

  // Power_cycle
  pwr_port.dck.reset <= 0;
  @(pwr_port.dck);
  pwr_port.dck.reset <= 1;
  repeat (10) @(pwr_port.dck);
  pwr_port.dck.reset <= 0;
  repeat (2) @(pwr_port.dck);

training_ph()

  // Leave as default

config_dut_ph()

  // Sw initialization sequence
  // …

start_ph()

  // Leave as default

start_of_test_ph()

  // Leave as default

run()

  // All you need is to wait for the consensus manager to agree to shut down
  cm.wait_for_end_t();

shutdown_ph()

  // Leave as default

cleanup()

  // Leave as default

report()

  // Dump the final scoreboard status
  sb.report();

destruct()

  // Leave as default

The key is to make sure that your code is partitioned into the appropriate phases, as shown above. Also note that a bunch of the phases were left alone to their default implementation.

Okay, one minor detail. You do not directly derive from the vmm_unit base class. Instead, two classes, vmm_xactor and vmm_group have been provided. Both are derived from vmm_unit, so you have all the support for synchronization. vmm_xactor should be used as the base class for defining your individual transactors, whereas vmm_group should be used as the base class for components that put together several others into a single entity such as the top-level environment or a top-level interface vip.

Of course, the correctness of your synchronization  will still depend on what you end up implementing in the body of the pre-defined phases.  The names of the predefined phases give a hint. So you are still subject to what the other components implemented in their corresponding phases. Do follow the spirit of what each phase is supposed to do. Do not connect your components in build_ph()  phase even if the test passes. Do so in connect_ph(). A small price to pay for most cases, IMHO.

Do note that there often are scenarios where some components need to be synchronized separately from others. For example, in a PCIe based SOC, what if you need the OCP interface to be up and running before you bring your PCIe interface out of reset? In this case, you definitely do not want the OCP and the PCIe VIPs to run their configure and reset phases in lock-step with each other. This is where advanced synchronization features such as vmm_timelines come to play, but that’s a topic for the next post. Stay tuned.

This article is the 3rd in the Verification in the trenches series. Hope you found this article useful. If you would like to hear about any other related topic, please comment or drop me a line at ambar.sarkar@paradigm-works.com. Also, if you are starting out fresh, please check out the free VMM1.2 environment generator at http://resourceworks.paradigm-works.com/svftg/vmm .

Posted in Uncategorized | No Comments »

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

Viewing VMM log details in waveforms

Posted by Avinash Agrawal on December 17th, 2009


Avinash Agrawal, Corporate Applications, Synopsys

Often engineers need a combination of logfile outputs and waveforms, to look at their simulations. And they wonder if it is possible to look at waveforms and get information on the number of simulation errors that might have occurred in a simulation upto any particular point of simulation time.

The good news is that the VMM log service helps a user track the source of different messages to different verification components.

When VMM macros such as `vmm_error, `vmm_note etc are used at different places in the verification environment, the user is able to view the corresponding information in the simulation output. This information includes the time at which the message was logged, the verification component and the specified instance of which the message was issued from. However, it can be very useful if the timing of the errors or warnings in the simulation output can be correlated with waveforms in simulation. For example, if there is a protocol violation message issued from one of the testbench monitors, the user can map the time when the message was issued to the actual signals in the waveform. That way user can quickly uncover the relevant problem in the DUT.

The VMM message service vmm_log consists of the vmm_log_format object to control the format of the messages. The vmm_log_format object also gets the information of the type/severity of the messsages. The vmm_log class for each component uses the default implementation of all these methods. The user can easily extend the vmm_log_format class and add in his modifications. Modifications can be either to trigger an assertion or incrememt a variable which can be dumped into the waveform window. This way, the  engineer can correlate the errors with the change in the variable or an assertion in the waveform window.

The following code shows how this can be done:

module top();
  int error_count;

initial begin
 $vcdpluson();
end

endmodule

program P;

class env_format extends vmm_log_format;  //extending vmm_log_format
                                          // for adding in user modifications
      virtual function string format_msg(string name,
                                         string inst,
                                         string msg_typ,
                                         string severity,
                                         ref string lines[$]);
         if (msg_typ == "FAILURE" && severity == "!ERROR!") begin
            top.error_count++;  //incrementing error count for warnings and errors
         end
//or trigger an assertion which can also be seen in the waveform
        assert (~(msg_typ == "FAILURE" && severity == "!ERROR!"));
          format_msg = super.format_msg(name, inst, msg_typ, severity, lines);
      endfunction
endclass

class xactor extends vmm_xactor;

  int id;

  function new(int id, string instance);
    super.new("xactor", instance);
    this.id = id;
  endfunction

  virtual task main();
    super.main();
    `vmm_note(log, "This is a note message");
    #5;
    `vmm_error(log, "This is an error message");
    if (id == 0)
      #10 `vmm_error(log, "This is an error message");
    else
      #30 `vmm_error(log, "This is an error message");
  endtask
endclass

class env extends vmm_env;
  xactor x1;
  xactor x2;
  vmm_log log;

  function new();
    env_format fmt;
    log = new("env", "class");
    fmt = new();
    log.set_format(fmt);
  endfunction

  virtual function void build();
    super.build();
    x1 = new(0, "x1");
    x2 = new(1, "x2");
  endfunction

  virtual task start();
    super.start();
    x1.start_xactor();
    x2.start_xactor();
  endtask

  virtual task wait_for_end();
    super.wait_for_end();
    #1000;
  endtask

  virtual task stop();
    super.stop();
    x1.stop_xactor();
    x2.stop_xactor();
  endtask

endclass

initial begin
  env e = new;
  e.run();
end

endprogram

Posted in Uncategorized | No Comments »

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

Just in time for the holidays: VMM 1.2!

Posted by Janick Bergeron on December 15th, 2009

Janick Bergeron
Synopsys Fellow

I am pleased to see that the OpenSource version of VMM 1.2 is finally released. It is the culmination of six months of hard work by the entire VMM teams and the hundreds of customers who have provided inputs on its requirements and the dozens of teams who have contributed their feedback during the beta period.

What is new in VMM 1.2 is a “secret de Polichinelle“. Ever since the start of the beta period, several VMM users and Synopsys engineers have published tutorials, seminar presentations and blog articles on many of its powerful aspects. Nonetheless, I would like to take this opportunity to give you the highlights and pointers to where you can find more information.

A new User’s Guide

One of the most important aspect of this release—and one that has not been mentioned so far—is the completely revamped and expanded User’s Guide. We have integrated the content of the VMM for SystemVerilog book, the book’s errata, and the previous User’s Guide into a single User Guide that completely documents all of the features of the class library. Furthermore, the body of this new User’s Guide has been expanded to present the methodology in a style that will be easier to learn, with many examples. Speaking of examples, this latest distribution contains a lot more examples (in $VMM_HOME/sv/examples), illustrating the many applications domains of the VMM and all of its new features.

Implicit Hierarchical Phasing

The original VMM used explicit phasing exclusively. With 1.2, VMM now supports implicit hierarchical phasing. With implicit phasing, transactors and environments need not be responsible for the phasing of the components they instantiate: that is taken care of automatically by the new vmm_timeline object. The implicit phasing is also hierarchical, meaning that an environment may contain more than one vmm_timeline instances. Sub-timelines limit the scope and interaction of user-defined phases when block-level environments are reused in a system context. Sub-timelines may also be rolled back if their portion of verification environment needs to be stalled or restarted, for example because its corresponding functionality in the DUT has been powered down. Furthermore, VMM allows implicit and explicit phasing to be arbitrarily mixed: instead of insisting that it be in control of every aspect of a verification environment, it can import portions of an environment described using an alternative phasing methodology and have it be explicitly phased using a different mechanism by encapsulating in a vmm_subenv instance. Similarly, any VMM environment can be subjugated to another phasing methodology by allowing vmm_timeline instances to be explicitly phased.

TLM 2.0

In addition to the vmm_channel, VMM 1.2 now offers an alternative transaction-level interface mechanism inspired by OSCI’s Transaction-Level Modeling standard version 2.0. I say “inspired” because it is not a direct translation of the SystemC TLM standard, as the SystemVerilog language does not support multi-inheritance used in the SystemC implementation. The TLM2 standard is radically different from TLM1 because the latter did not live up to its promises of model interoperability and simulation performance. In addition to specifying an interface and transport mechanism, TLM2 specifies clear transaction progress and completion models through phases and the Base Protocol. VMM has always provided similarly well-defined transport mechanism (vmm_channel) and completion models (see pp176-195 of the original VMM book). With the addition of TLM2 sockets, VMM can also be used to implement high-performance virtual prototyping models in SystemVerilog. Of course, we’ve made sure that you can attach a vmm_channel to an initiator or target blocking or nonblocking socket interface for maximum flexibility.

Object Hierarchy

Whereas modules form a strict hierarchy in SystemVerilog, class instances (also known as objects) do not – at least from a language standpoint. However, it is a common mental model even though it is not enforced by the language. VMM 1.2 has the ability to define parent-child relationships between any instances of the vmm_object class. And because that class is the base class for all other VMM classes, any instance of a VMM class or user-defined extensions thereof can have a parent and any number of children. This creates a user-defined hierarchy of objects. And because each object has a name, it implicitly creates a hierarchical naming structure. Furthermore, because this hierarchical and the name of its component is entirely user-defined, VMM 1.2 provides the concept of namespaces to create alternative object hierarchies and names, making it easy to create hierarchical registries or to map an object hierarchy to another one. Objects can easily be found by name or by traversing the hierarchy from parent to child or vice-versa.

Factory API

VMM always had the concept of class factories (see p217 in the original VMM book). It used the factory pattern in all of its pre-defined generators and recommended that it be used whenever transaction objects were created or randomized (see Rules 4-115 and 5-6 in the original VMM book). It simply did not provide any pre-defined utility to ease the implementation or overriding of class factory instances. VMM 1.2 remedies this situation by introducing a class factory API that makes it easier to replace class factory instances, as well as to build class factories. Furthermore, it provides two factory override mechanism: a fast one that creates class instances with default values, and a slower one that creates exact copies. And, being strongly typed, the new factory API will detect at compile time if you are attempting to replace a factory instance with an incompatible type.

And many more!

VMM 1.2 provides many more additional features, like hierarchical options, RTL configuration support, and test concatenation.

Learning more

You can download the OpenSource distribution here. You will also find VMM 1.2 in your VCS 2009.12-1 distribution (use the +define+VMM_12 compile-time command-line option to enable it!).

Visit this blog often, as many industry leaders and Synopsys engineers will continue to provide insights on the new features included in VMM 1.2

Also, stay tuned for a series of one-day VMM 1.2 seminars and workshops that will be touring the major semiconductor centers around the globe.

Posted in VMM | 2 Comments »

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

Transaction Level Modeling – Value add in different languages.

Posted by Nasib Naser on December 14th, 2009

Nasib_NaserNasib Naser, PhD

Sr. Staff Corporate Applications Engineer – Synopsys

One of the driving factors of creating SystemVerilog is to raise the design verification abstraction level. The reason for such a move is described in the SV LRM Abstract. It says: “A set of extensions to the IEEE 1364-2001 Verilog Hardware Description Language to aid in the creation and verification of abstract architectural level models.” So why and how? The “Why” is obvious. A number of reasons come to mind:

  1. Faster development and simulation to enable reaching design and verification goals sooner than later.
  2. Achieve early closure on architectural decisions without early commitment to implementation details.
  3. Enable running “some” software within the complete system context.
  4. Create an environment in which verification methodologies could be developed that creates true verification IP re-usability and models interoperability.

This blog will discuss the methodology behind the “How.” The technology will be explained in subsequent blogs. Up until vmm 1.2 was released Transaction based models were created by utilizing VMM constructs such as vmm_channel(), vmm_xactor(), and vmm_data() . With all VMM strengths this use model succeeded only on in-house designs and didn’t gain wide modeling adoption. The TLM methodology built into VMM lacked common practices to enable interoperability – emphasis on common practice.

Meanwhile, driven by users, the Open SystemC Initiative aka OSCI TLM community managed to create a standard on which models could be developed for re-use and interoperability. Without dwelling in the past I’d like to give a brief history on the evolution that led to the TLM standard. In the beginning there was C, then C++. For obvious reasons these widely known software languages were used to develop in-house system level models for doing high level performance and architectural analysis. The use model was very limited as the simulation behavior was very far from the actual hardware. It lacks concurrency and timing. C++ extensions augmented with a “proof of concept” simulator was created and called SystemC that enable modeling these hardware behaviors in C++, and more. That worked very well. Failing to replace Verilog and vhdl for design and verification SystemC found its niche use model in the architectural modeling domain. At that point architects using SystemC started to demand a standard that enables interoperability for fast platform composition, ease of use, and re-usability. Hence, the OSCI SystemC Transaction Level Modeling 1.0 and later 2.0 standards were created.

So why re-invent the wheel when it comes to SystemVerilog TLM? And why not adopt a powerful and robust verification methodology such as VMM standard to enable seamless integration between SystemC and SystemVerilog? That’s why features described in the OSCI TLM standard found its way into the SystemVerilog/VMM world. These features are available in the newly released VMM 1.2. Subsequent blogs I will explain these features and the value add they bring into a true IP re-use and interoperable verification methodologies.

Posted in Uncategorized | No Comments »

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

Hitting the “Playback” button on VMM transactions

Posted by Avinash Agrawal on December 11th, 2009

Avinash Agrawal

Avinash Agrawal, Corporate Applications, Synopsys




Often verification engineers face the challenge of recording transactions in one simulation, and wanting to replay the same set of transactions in the same sequence of transactions in a different simulation, and try different ways to make this happen.

Well, there is some good news !!!

VMM provides a facility where you can record the transactions going through a VMM channel and save it into a file. This can be done through the record method of the VMM channel. Later, for replay, disconnect the producer of the channel (may be generator/transactor, etc which sends transactions to the channel) and use playback method to load the channel with the transactions from the file in the same order.

Here the saved transaction file acts as a virtual producer. This way random stability is guaranteed. Note that byte_pack() or save() method of transaction (vmm_data) class must be implemented to use record mechanism and byte_unpack() or load() method of transaction (vmm_data) class must be implemented to use playback mechanism. Since playback avoids randomization of the transaction/corresponding scenarios, performance can be improved in case of complex transaction/scenario constraints. Also generation is not scheduling-dependent and will work with different versions of the simulator as well as with different simulators.

This record/replay mechanism can also be used to go through known states at one interface while stressing another interface with random scenarios within the same simulation itself.

The code below snippet shows how to use VMM record/playback.

task start();

`ifdef RECORD_MODE
    fork
      chan.record("Chan.dat"); //call record method of the channel
                               //with a filename
    join_none
    gen.start_xactor();       //start the generator (producer) of the  channel
                              // if it is record mode.
 `endif
 `ifdef PLAYBACK_MODE    // In playback mode, make sure that the
                         // generator (producer) is not started.
     fork begin
         bit success;
         trans tr = new;
         chan.playback(success, "Chan.dat", tr);  // call playback method of
                                               // the channel with the same file
         if (!success) begin
            `vmm_error(log, "Playback mode failed for channel");
         end
     end join_none
`endif

endtask

Posted in Uncategorized | No Comments »

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

Using Explicitly-Phased Components in an Implicitly-Phased Testbench

Posted by JL Gray on December 11th, 2009

In my last post, I described the new VMM 1.2 implicit phasing capabilities.  I also recommended developing any new code based off of implicit phasing.  Obviously, though, companies that have been using the VMM for quite some time will have developed all of their existing testbench components using explicit phasing.  It is relatively straightforward (and in some sense almost trivial) to use an explicitly phased component in an implicitly phased testbench.

Remember that the whole point of explicit phasing is that users cycle components through the desired phases by manually calling functions and tasks within the component itself. vmm_env contains the following methods:

  • gen_cfg
  • build
  • reset
  • config_dut
  • start
  • wait_for_end
  • stop
  • cleanup
  • report

vmm_subenv contains the following relevant methods:

  • new
  • configure
  • start
  • stop
  • cleanup
  • report

In an explicitly-phased environment, subenv methods are called manually by integrators, usually from the equivalent method in vmm_env. There are two approaches for instantiating a vmm_subenv-based component in an implicitly-phased testbench. The default approach is to simply allow the implicit phasing mechanism to call these explicit phases for you. Explicitly phased components are identified by the implicit phasing mechanism, and methods are called using a standard (and not entirely unexpected) mapping:

Implicit Phase Explicit Phase Called
build_ph vmm_subenv::new[1]
configure_ph vmm_subenv::configure
start_ph vmm_subenv::start
stop_ph vmm_subenv::stop
cleanup_ph vmm_subenv::cleanup
report_ph vmm_subenv::report

[1] Users must call vmm_subenv::new manually.

Now, you might want to phase your vmm_subenv in a non-standard way. If that’s the case, the first thing you’ll need to do is disable the automatic phasing. Here’s how. First, instantiate a null phase:

vmm_null_phase_def null_ph = new();

Next, override the phases you don’t want to start automatically. For example:

my_group.override_phase(“start”, null_ph);
my_group.override_phase(“stop”, null_ph);

Finally, call the explicit phases from the parent object’s implicit phases.  A complete example is shown below.

class testbench_top extends vmm_group;
bus_master_subenv bus_master;
vmm_null_phase_def null_ph = new();

function void build_ph();
bus_master = bus_master_subenv::create_instance(this, “bus_master”);
bus_master.override_phase(“start”, null_ph);
bus_master.override_phase(“stop”, null_ph);
endfunction: build_ph

task reset_ph();
bus_master.start();
// wait 1000 clocks…
bus_master.stop();
endtask: reset_ph

endclass: testbench_top

Posted in VMM | No Comments »

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

Blocking Transport Communication in VMM 1.2

Posted by John Aynsley on December 10th, 2009

image John Aynsley, CTO, Doulos

 

One of the new features in VMM 1.2 is the blocking transport interface, borrowed from the SystemC TLM-2.0 standard. This interface provides an alternative to the using the put and get methods of the vmm_channel class when communicating between a producer and consumer. There are a couple of reasons why you might consider using the blocking transport interface in VMM: perhaps you are trying to interface to a SystemC reference model, or perhaps you want to write a transactor that has very clean semantics when it comes to determining the start and end of a transaction. In either case, the new blocking transport interface can help.

A blocking transport call is a method call that carries with it a transaction object and is expected not to return until the transaction is complete. The status of the transaction, that is, whether the transaction succeeded or failed, is expected to be carried within the transaction object itself. You call blocking transport as follows:

class my_tx extends vmm_data; // A user-defined transaction class
   …
   enum {OKAY, FAIL} status; // Status flag embedded in the transaction itself
endclass

my_tx tx;
int delay;

$cast(tx, randomized_tx.copy()); // Create and randomize a transaction object

m_port.b_transport(tx, delay);

if( tx.m_status != OKAY ) // Check the response status
   `vmm_warning(log, “Transaction failed”);

Since this is SystemVerilog, the transaction object itself is passed by reference. The delay argument instructs the recipient of the transaction to process the transaction after the given delay has elapsed, and can be set by both the caller of and the implementation of the b_transport method.

The blocking transport method needs to be implemented by the transactor that will receive or consume the transaction:

task b_transport(int id = -1, my_tx tx, ref int delay);

   // Process transaction
   …

   // Pause for given delay and then reset the delay argument
   #(delay);
   delay = 0;

   // Set the response status in the transaction object
   tx.m_status = OKAY;
endtask : b_transport

By definition, blocking transport “blocks” until the transaction has been fully processed. It is coded as a SystemVerilog task so that it is allowed to execute timing controls such as the #(delay) in this example. The task is not obliged to suspend for the given delay in this manner; it could have simply returned leaving the delay without modification, or even have increased the value of the delay, leaving it for the caller to realize the delay later. The task is obliged to set the response status flag within the transaction object before returning, and the caller must check the response status on return.

One significant thing that is happening here is that communication between a producer and a consumer (or in TLM-2.0 jargon, an initiator and a target) is being accomplished without having any intervening channel to buffer the transactions. The b_transport method is called by the producer, provided by the consumer, and only returns when the transaction is complete. The advantage of not having an intervening channel is that it can help increase execution speed in the context of a very fast simulation model. The disadvantage is that it is not possible to have multiple transactions in progress given only a single execution thread in the initiator; therefore, it is a good idea to add a semaphore in the implementation of the b_transport method in the target transactor

class target_xactor extends vmm_xactor;
   …
   local semaphore m_sem = new(1);

   task b_transport(int id = -1, my_tx tx, ref int delay);
   m_sem.get(1);
   // Process transaction
   …
   m_sem.put(1);
endtask : b_transport

This is not the whole story, because we also need to explain how to connect the producer to the consumer. Look out for future blog posts.

Posted in Uncategorized | No Comments »

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

Developing transactors using VMM 1.2

Posted by Vidyashankar Ramaswamy on December 8th, 2009

 

There are many ways to design and develop a transcator. The following is the way I visualize it. Typical transactor components are shown in the following figure. Based on the functionality, transactor can be grouped into up-stream, down-stream, pass through or a passive monitor types. I shall explain in brief what I mean by these. Up-stream can be a stimulus generator and down-stream can be a bus function master/slave model. A pass through can exists in the test bench to connect a master and the bus function model. A passive monitor simply monitors the bus interface on to which it is connected and broadcasts the packet information whenever it is available. The dotted line in the figure partitions the transactor based on functionality and shows different port connections.

 

 

 

The right side of the dotted line in the above figure represents the upstream. This can be a producer (master or stimulus generator), in which case it can have only output port. This output port is designed as a TLM transport port.

The left side of the dotted line represents the downstream: this can be a slave transactor in which case the receiving port will be a VMM channel. If the downstream transactor is connected to the DUT, then you need to declare a virtual interface and bind it through a port object to the physical interface. This is done from the enclosing environment. This makes the BFM component reusable across test benches. In my next article I shall show you an example about the port object.

If you are designing a pass through transactor, then you need to have both VMM channel for receiving the transaction from the producer and the TLM transport port for sending the transaction to the consumer. Analysis port can be used if any observers are hooked up to this transactor. Also note that you need not have any virtual port connection for a pass through transactor.

A monitor component will have only analysis port along with the physical interface connection.

You might be wondering why the analysis port and the callback interface are centered between up-stream and the down-stream. If you have guessed it, yes you’re right. Both master and slave need to broadcast the information/packet which is passing through them to the observers. The observer can be a scoreboard, coverage collector or a simple file write for debug purposes.

To make the master/slave transactor re-usable, callback methods are used. A callback method allows the user to extend the behavior of a transactor without having to modify the transactor itself. VMM 1.2 supports factory service to replace a transactor. I favor callbacks for transactor extensions. So which one should you use ? I shall leave that up to you to decide and this could be a topic on its own.

Please feel free to comment and share your opinion. For more information please refer to the VMM 1.2 user guide.

Posted in VMM | No Comments »

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

Paved With Good Intentions: Examining Lost Design Intent

Posted by Adiel Khan on December 7th, 2009

image-thumb.pngandys-picture_2

Adiel Khan, Synopsys CAE

Andrew Piziali, Independant Consultant

Remember the kick-off of your last new project, when the road to tape-out was paved with good intentions? Architects were brainstorming novel solutions to customer requirements? Designers were kicking around better implementation solutions? Software engineers were building fresh new SCM repositories? And you, the verification engineer, were excitedly studying the new design and planning its verification? Throughout all of this early excitement, all sorts of good intentions were revealed. Addressing the life story of each intention would make a good short story, or even a novel! Since we don’t have room for that, let’s just focus on the design intentions.

Design intent, how the architect intended the DUV (design under verification) to behave, originates in the mind’s eye of the architect. It is the planned behavior of the final implementation of the DUV. Between original intent and implementation the DUV progresses through a number of representations, typically referred to as models, wherein intent is unfortunately lost. However, intent’s first physical representation, following conception, is its natural language specification.

We may represent the space of design behaviors as the following Venn diagram:1

Each circle—Design Intent (AEGH), Specification (BEFH) and Implementation (CFGH)—represents a set of behaviors. AEGH represents the set of design requirements, as conveyed by the customer. BEFH represents the intent captured in the specification(s). CFGH represents intent implemented in the final design. The region outside the three sets (D) represents unintended, unspecified and unimplemented behavior. The design team’s objective is to bring the three circular sets into coincidence, leaving just two regions: H (intended, specified and implemented) and D. By following a single design intention from set Design Intent to Specification to Implementation, we learn a great deal about how design intent is lost.

An idea originally conceived appears in set AEGH (Design Intent) and, if successfully captured in the specification, is recorded in set EH. However, if the intent is miscommunicated or not even recorded and lost, it remains in set A. There is the possibility that a designer learns of this intent, even though it not recorded in the specification, and recaptures it in the design. In that case we find it in set G: intended, implemented, but unspecified.

Specified intent is recorded in set BEFH. Once the intent is captured in the specification it must be read, comprehended and implemented by the designer. If successful, it makes it to the center of our diagram, set H: intended, specified and implemented. Success! Unfortunately some specified requirements are missed or misinterpreted and remain unimplemented, absent from the design as represented by set E: intended, specified but unimplemented. Sometimes intent is introduced into the specification that was never desired by the customer and (fortunately) never implemented, such as set B: unintended, unimplemented, yet specified. Unfortunately, there is also unintended behavior that is specified and implemented as in set F. This is often the result of gratuitous embellishment or feature creep.

Finally, implemented intent is represented by set CFGH, all behaviors exhibited by the design. Those in set G arrived as originally intended but were never specified. Those in set H arrived as intended and specified. Those in set F were introduced into the specification, although unintended, and implemented. Behaviors in set C were implemented, although never intended nor specified! In order to illustrate the utility of this diagram, let’s consider a specific example of lost design intent.

We can think of each part of the development process as building a model. Teams write documentation as a specification model, such as a Microsoft Word document. System architects build an abstract algorithmic system model that captures the specification model requirements, using SystemC or C++. Designers build a synthesizable RTL model in Verilog or VHDL. Verification engineers build an abstract problem space functional model in SystemVerilog, SVA and/or e.

If any member of the team fails to implement an element of the upstream, more abstract model correctly (or at all), design intent is lost. The verification engineer can recover this lost design intent by working with all members of the team and giving the team observability into all models.

Consider an example where the system model (ex. C++) uses a finite state machine (FSM) to control the data path of the CPU whereas the specification model (ex. MS Word) implies how the data path should be controlled. This could be a specification ambiguity that the designer ignores, implementing the data path controller in an alternate manner, which he considers quite efficient.

Some time later the system architect may tell the software engineers that they do not need to implement exclusive locking because the data path FSM will handle concurrent writes to same address (WRITE0, WRITE1). However, the designer’s implementation is not based on the system model FSM but rather the specification model. Therefore, exclusive locking is required to prevent data corruption during concurrent writes. We need to ask: How can the verification engineer recover this lost design intent by observing all models?

Synopsys illustrates a complete solution to the problem in a free verification planning seminar that dives deep into this topic. However, for the purposes of this blog we offer a simplified example, using the design and implementation of a coverage model:

  1. Analyze the specification model along with the system model
  2. Identify the particular feature (ex. mutex FSM) and write its semantic description
  3. Determine what attributes contribute to the feature behavior
  4. Identify the attribute values required for the feature
  5. Determine when the feature is active and when the attributes need to be sampled and correlated

This top-level design leads to:

Feature CPU_datapath_Ctrl
Description Record the state transitions of the CPU data path controller
Attribute Data path controller state variable
Attribute Values IDLE, START, WRITE0, WRITE1
Sample Whenever the state variable is written

The verification engineer can now implement a very simple coverage model to explicitly observe the system model, ensuring entry to all states:

enum logic [1:0] {IDLE, START, WRITE0, WRITE1} st;

covergroup cntlr_cov (string m_name) with function sample (st m_state);

option.per_instance = 1;

option.name = m_name;

model_state: coverpoint m_state {

bins t0 = (IDLE   => IDLE);

bins t1 = (IDLE   => START);

bins t2 = (START  => IDLE);

bins t3 = (START  => WRITE0);

bins t4 = (WRITE0 => WRITE1);

bins t5 = (WRITE1 => IDLE);

bins bad_trans = default;

}

endgroup

planner

The verification engineer can link the feature “CPU_datapath_Ctrl” in his verification plan to the cntlr_cov covergroup. Running the system model with the verification environment and RTL implementation will reveal that bin “t4″ is never visited, hence state transition WRITE0 to WRITE1 is never observed. The team can review the verification plan to determine if the intended FSM controller should be improved in the design to conform to all design intent.

Although there are many other subsets of the design intent diagram we could examine, it is clear that a design intention may be lost through many recording and translation processes. By understanding this diagram and its application, we become aware of where intent may be lost or corrupted and ensure that our good intentions are ultimately realized.


1The design intent diagram is more fully examined in the context of a coverage-driven verification flow in chapter two of the book Functional Verification Coverage Measurement and Analysis (Piziali, 2004, Springer, ISBN 978-0-387-73992-2).

Posted in Coverage, VMM | No Comments »

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

VMM scenario generators and dependent scenarios

Posted by Avinash Agrawal on December 4th, 2009

Avinash Agrawal

Avinash Agrawal, Corporate Applications, Synopsys

Often folks wonder if it possible to have a VMM scenario generator, where one scenario is dependent on another scenario.

The answer is “Yes.”

Consider the testcase below. You can define two scenarios, scn_a and scn_b, both of which have their own set of constraints. The variables generated in scn_b are a multiple of the values that were set when scn_a was generated previously, in this case by variable “ratio”. For more details on how VMM scenario generators work, refer to the VMM user guide.

Systemverilog testcase:

——————————————————————————–

class packet extends vmm_data;

rand int sa;

rand int da;

`vmm_data_member_begin(packet)

`vmm_data_member_scalar(sa, DO_ALL)

`vmm_data_member_scalar(da, DO_ALL)

`vmm_data_member_end(packet)

endclass

`vmm_channel(packet)

`vmm_scenario_gen(packet, “packet”)

class a_scenario extends packet_scenario;

int unsigned scn_a;

rand int ratio;

function new();

scn_a = define_scenario(”scn_a”, 5);

endfunction

constraint cst_a {

$void(scenario_kind) == scn_a ->  {

foreach(items[i]) {

this.items[i].sa inside {[0:100]};

this.items[i].da inside {[0:100]};

}

ratio inside {[1:5]};

}

}

endclass

class b_scenario extends packet_scenario;

int unsigned scn_b;

function new();

scn_b = define_scenario(”scn_b”, 10);

endfunction

constraint cst_b {

$void(scenario_kind) == scn_b -> {

foreach(items[i]) {

this.items[i].sa inside {[100:300]};

this.items[i].da inside {[100:300]};

}

}

}

endclass

class hier_scenario extends packet_scenario;

rand a_scenario scn_a;

rand b_scenario scn_b;

function new();

this.scn_a = new();

this.scn_b = new();

this.scn_a.set_parent_scenario(this);

this.scn_b.set_parent_scenario(this);

endfunction

virtual task apply(packet_channel channel, ref int unsigned n_insts);

this.scn_a.apply(channel, n_insts);

this.scn_b.apply(channel, n_insts);

endtask

constraint cst_hier {

foreach(scn_b.items[i]) {

// Create a scenario ’scn_b’ depending upon ’scn_a’

scn_b.items[i].sa inside {[scn_a.ratio*100:scn_a.ratio*800]};

scn_b.items[i].da inside {[scn_a.ratio*100:scn_a.ratio*800]};

}

}

endclass

program automatic test;

packet_scenario_gen scn_gen;

packet_channel      pkt_chan;

packet pkt;

hier_scenario scn_hier;

initial begin

scn_hier = new();

scn_gen = new(”scn_gen”, -1, pkt_chan);

scn_gen.register_scenario(”scn_hier”, scn_hier);

scn_gen.unregister_scenario_by_name(”Atomic”);

$display(”Size of the scn is %0d”, scn_gen.scenario_set.size());

scn_gen.stop_after_n_insts = 15;

scn_gen.start_xactor();

while(1) begin

#10 scn_gen.out_chan.get(pkt);

$display(”id is %0d , %0d %0d”, pkt.data_id, pkt.sa, pkt.da);

end

end

endprogram

——————————————————————————–

Posted in Support, Uncategorized, VMM | No Comments »

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

Verification in the trenches: Traversing object hierarchies using VMM1.2

Posted by Shankar Hemmady on November 25th, 2009

AmbarSarkar Dr. Ambar Sarkar, Chief Verification Technologist, Paradigm Works Inc.

 

Ever get stuck trying to configure an object deep inside a verification environment? More likely than not, someone else created the environment, changed it drastically over time, and did not leave a description of the object hierarchy. It is quite time-consuming to unravel the whole hierarchical path from the root to that object. The phrase “needle in a haystack” comes to mind.

I will share some tips on how the vmm_object class, recently added to VMM1.2, can help.

As you are most likely aware, a typical verification environment today contains hundreds of classes defined by the user. Many of these are related to each other in parent/child relationships, creating several hierarchies.

As you are most likely aware, a typical verification environment today contains hundreds of classes defined by the user. Many of these are related to each other in parent/child relationships, creating several hierarchies.

 Ambar_Sarkar_blog2_fig1

 

 

So how does the vmm_object class help in traversing these hierarchies? In a nutshell, it is the base class for all classes defined in the VMM library. This means that for every object derived from a class in the VMM library, you can use the same API for:

  • Finding its type
  • Finding its ancestors and children
  • Avoiding name clashes ( a new feature called namespace and we will save this topic for another post)
  • Displaying the hierarchy
  • Iterate over the objects
  • Find object by a regular expression search

The last two bullets above are what I feel help me most, because:
a. I do not have to write separate code for traversing the hierarchy of different types of objects.
b. I can locate an object easily without having to remember or figure out the exact path to it.

Here is an example. I wanted to access all the atomic generators in my environment and they were all named with the suffix GEN.

There are at least two ways to accomplish this:

1. Use the `foreach_vmm_object macro:

// Configuration phasefor test

virtual function void configure_ph();

string pattern = "@%*GEN"; // regular expression used to search for generator name

// Just iterate over all `VMM_ATOMIC_GEN objects that end with "GEN"
`foreach_vmm_object(`VMM_ATOMIC_GEN, pattern, env) 
   begin
      // Do something with this generator
     `vmm_note(log, $psprintf("Found %s\n", obj.get_object_name())); 
   end

When applicable, this approach seems like a quite concise way to get things done. Do note that the implementation of `foreach_vmm_object macro requires it to be the first statement following the declarations in a method or following a begin keyword due to the way it is implemented.

Of course, you can avoid the macro if you are so inclined as shown below. Sometimes I do this to get better visibility while debugging.

2. Use iteration class vmm_object_iter provided with this release:

   … 
   vmm_object_iter iter; // iterator object
  
vmm_object obj;
  
`VMM_ATOMIC_GEN at_gen;
  
string pattern = "@%*GEN"; // regular expression used to search for generator name

   // Just iterate over all `VMM_ATOMIC_GEN objects that end with "GEN"
   iter = new(env, pattern); // Get a pointer to the iterator

   obj = iter.first(); // Get the first in the list
  
while (obj != null) begin
        
// Always check type!
        
if (!$cast(at_gen, obj))
        
`vmm_error(log, $psprintf("Unexpected type for %s. \n", obj.get_object_name()));

         // Do something with this generator
        
`vmm_note(log, $psprintf("Found %s\n", obj.get_object_name()));

         obj = iter.next(); // Get the next one

   end

Pretty much similar to what I expected as a use model for an iterator class.

Of course, your mileage/opinion may differ from mine. Please share how you plan on using the common vmm_object base class in your daily verification tasks.

This article is the 2nd in the Verification in the trenches series. Hope you found this article useful. If you would like to hear about any other related topic, please comment or drop me a line at ambar.sarkar@paradigm-works.com. Also, if you are starting out fresh, please check out the free VMM1.2 environment generator at http://resourceworks.paradigm-works.com/svftg/vmm .

Posted in Uncategorized | No Comments »

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

Implicit and Explicit Phasing

Posted by JL Gray on November 20th, 2009

JLGray JL Gray, Verilab, Inc.

In my last post I discussed the difference between phases and threads in the VMM. Phases and threads are conventions used to simplify testbench development. The classic VMM approach has been to use the vmm_env and vmm_subenv classes to manage phases of testbench components, leaving vmm_xactor to deal with thread management. Phases were managed “explicitly”, that is to say, users had complete control over when to step the environment and its subcomponents through individual phases via function and task calls.

The VMM 1.2 introduces the concept of “implicit” phasing. Both implicit and explicit phasing can be used to accomplish many of the same goals, albeit in different ways. In an implicitly-phased testbench, functions and tasks representing phases are called automatically at the appropriate times. A global controller (vmm_simulation) works in conjunction with specially configured schedulers (vmm_timeline) to walk all testbench components through relevant phases. vmm_simulation acts like a conductor, keeping all of the various testbench components in sync during pre-test, test, and post-test portions of a typical simulation.

A natural question to ask is, “Are there any benefits to implicit phasing over and above the explicit phasing techniques I’m using today?” When testbench components are walked through phases automatically, there are a few interesting possibilities that arise. For starters, it becomes possible to add and remove phases. Let’s imagine a simulation that has the following phases1:

  • reset_ph
  • training_ph
  • config_dut_ph
  • start_ph
  • shutdown_ph

What happens if I need to use a piece of verification IP that has implemented a training phase that is not applicable in my test environment? In an explicitly phased environment, I’d need to have control of the code that called the sub-component’s training phase in order to ensure the task in question was not called. In an implicitly-phased environment, I can simply delete the phase from being executed on that component:

my_enet_component.override_phase(“training”, vmm_null_phase_def);

In addition to adding and removing phases, you can also alias one phase to another so that the two phases overlap. For example, let’s say I need the reset phase of one portion of my design to overlap with the training phase of another. I could reconfigure the phasing of the components so they occurred in parallel instead of serially. Here are the steps:

  • Disable the reset phase for the group 1

    group1.override_phase(“reset”, vmm_null_phase_def);

  • Create a new user-defined phase for group 1 called “reset_during_training”

    class reset_during_training_phase_def extends
    vmm_fork_task_phase_def #(group1);
    `vmm_typename(reset_during_training_phase_def)

    virtual task do_task_phase(group1 obj);
    if(obj.is_unit_enabled())
    obj.reset_ph();
    endtask:do_task_phase

    endclass:reset_during_training_phase_def

  • Alias the new user-defined phase to the “training” phase

    vmm_timeline tl = vmm_simulation::get_top_timeline();
    tl.add_phase(”training”, reset_during_training_phase_def);

Another benefit of implicit phasing is that vmm_xactor, which normally manages threads, is also phased. Because of this, your transactors are now aware of the stage of the simulation we are in at any given moment. They can then change their behavior based on this knowledge. Because of this, VIP developer could configure a transactor to automatically start during the reset phase, stop during training, and continue during the start phase. Users could easily reconfigure the transactor to start and stop at other times as needed.

One thing to note is that implicitly phased components can be phased explicitly if desired by the user. This means that implicit phasing provides a superset of the explicit functionality provided by vmm_env and vmm_subenv.

The addition of implicit phasing means developers now have a choice to make when building a verification environment. Should you build it based on implicitly or explicitly-phased components? Luckily, it is possible to use implicitly-phased components in an explicitly-phased environment, and vice versa. My recommendation is for users to create all new testbench code using implicit phasing. If you are used to explicit phasing, the new development style can seem perplexing. However, as I mentioned, implicit phasing is effectively a superset of explicit phasing in its capabilities. Adopting the new methodology across your entire team will ensure the additional capabilities discussed above are available in your testbench in the future without users having to make changes down the road.

1 The listed phases are a subset of the actual pre-defined implicit phases available

Posted in Uncategorized | No Comments »

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

What Has TLM-2.0 Got To Do With It?

Posted by John Aynsley on November 17th, 2009

JohnAynsley

John Aynsley, CTO, Doulos

 

You may have noticed that the public release of VMM 1.2 is just around the corner, and with this new version of VMM comes the introduction of features inspired by the SystemC TLM-2.0 standard.

Excuse me! TLM-2.0? What? Why do we need features from SystemC in VMM?

I will set out to answer that question fully in a series of blog posts over the coming months. But first off I will remark that the idea is not so strange. After all, VMM has always been transaction-level (with a small ‘t’ and ‘I’). Communication within a VMM verification environment exploits transaction-level modeling for speed and productivity, because “TLM” is about abstracting the model of communication used in a simulation. If we can adopt a common standard for transaction-level modeling across both SystemC and SystemVerilog, that has to be a good thing for everyone. It is evident that the design and verification community demands more than one language standard (witness VHDL, SystemVerilog, C/C++, and SystemC). Each individual language standards has progressed over time by borrowing the best features from the others. Having VMM borrow features from SystemC makes it easier to learn and work with both standards.

The other natural link between VMM and SystemC is that mixed-language simulation environments and C/C++ reference models are not unusual. Virtual platform models, as used for software development and architectural exploration, are growing in importance, and the SystemC TLM-2.0 standard is used to achieve interoperability between the components of a virtual platform model. If a constrained random VMM environment is to be used with a reference model that consists of a virtual platform adhering to the SystemC TLM-2.0 standard, then having TLM-2.0 support within VMM promises to make life easier for the VMM programmer.

Besides interoperability, the other main objective of the SystemC TLM-2.0 standard is simulation speed. The combination of speed and interoperability is achieved by the technical details of the ways in which transactions are passed between components. Fortunately, those technical details are a good fit with the way communication has always worked in VMM. In particular, both VMM and TLM-2.0 support the idea that each transaction has a finite lifetime with a well-defined time-of-birth and time-of-death.

The SystemC TLM-2.0 standard is based on C++. Unfortunately, not all C++ coding idioms translate naturally into SystemVerilog, so the transaction-level communication in VMM 1.2 is “inspired by” the TLM-2.0 standard rather than being a literal rendition of it.

So what are these new features? If you are already a SystemC user you may recognize ports and exports, borrowed directly from the SystemC standard, and analysis ports, transport interfaces, sockets and the generic payload, borrowed from the TLM-2.0 standard. I will explain how VMM is able to exploit each of these features in future blog posts, so watch this space…

Posted in Uncategorized | No Comments »

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