Skip to Content

Changing Functionality: The Factory Service #2

Posted on  by  from the site Verification Martial Arts
by Jason Sprott Jason Sprott is CTO at Verilab In a previous post we took a look at what the VMM Factory is and what it could do for us. In this post, we take a look at the problem the Factory solves. In order to understand when we should use a Factory, we really need to understand a bit more about the problem it solves, so we can spot situations where a Factory might be appropriate. Let’s start by looking at how we might do things in SystemVerilog without using VMM Factories. Typically we might instantiate a class of type Foo like this: Foo my_foo; … my_foo = new(…); The problem with that is my_foo will always create an object of type Foo, no matter what.  We can’t change that. We might use this class Foo in lots of places around our testbench code. If we want to replace Foo with a new class that maybe adds more variables, or replaces a method, we could have a problem. We’d have to go around our testbench looking for everywhere Foo is used, to see if there was a way it could be replaced. Anywhere Foo is constructed using new(), it’s is highly likely to involve modifying the original code. This is very undesirable, especially if the code may be part of a tested IP library. In general, modifying the original source is a Bad Thing. In VMM we can avoid this problem using a different way to create an instance of the class with the factory. It would look something like this: Foo my foo; … my_foo = Foo::create_instance(…); Although this looks similar to an instantiation with new(), something much more sophisticated  is at play. If we factory enable the class Foo, we never call new()to instantiate it again. We now call the Factory’s static method for generating instances of the object create_instance(). Since the method is static, it can be called without having an instance of Foo, therefore it can be used to create itself. What’s the point? Now that we’ve encapsulated the task of creating an instance in a method, we can change what that method does. We can tell create_instance() to return something different. This might be a new type (with some modifications), derived from Foo, or the same type populated with different values for the variables. What’s more, we can do this easily anywhere Foo is used in the code. We can pick specific instances to be replaced, or replace multiple instances globally. The two methods used to reprogram the factory are: •    create_with_new() – tells the factory, when creating new instances, to return a brand new instance of the type specified. •    create_with_copy() – tells the factory, when creating new instances,  to return a copy of instance specified. This replacement can be done anywhere in the code, as long as it happens before the instances you want to replace are created. Let’s say a test needs to replace our type Foo, with a new class, FooWithAttitude, derived from Foo. Here’s what that might look like: class my_factory_test extends vmm_test;    …    virtual function void configure_test_ph();       Foo::override_with_new(         // Foo’s factory is being overridden         "@%*",                        // instances matching this pattern will be replaced         FooWithAttitude::this_type(), // they will be replaced with this type of class         log, `__FILE__, `__LINE__);   // some generic log and debug stuff    endfunction:configure_test_ph    … endclass:my_factory_test As can be seen, the Factory override uses regular expression pattern matching to specify which instances will be targeted. The syntax is expressive enough to indentify single instances, multiple instances (e.g. by hierarchy), or all instances (as in this example). More information on the pattern matching syntax can be found in the “Common Infrastructure and Services > Simple Match Patterns” section of the VMM User Guide.  This next example shows how the factory can be programmed to return a copy of the class we’ve modified with some values. class test_read_back2back extends vmm_test;    …    virtual function void configure_test_ph();       FooBusTrans tr = new();        // create a template for the override copy       tr.address = ‘habcd_1234;      // special value we want to set up for override       tr.address.rand_mode(0);       // you might want to protect value during randomization       FooBusTrans::override_with_copy(          "@top:foobus0:*",           // instances matching this pattern will be replaced          tr,                         // they will be replaced by a copy of this instance          log, `__FILE__, `__LINE__); // some generic log and debug stuff    endfunction    … endclass The above example  shows a Factory override in the configure test phase of the simulation timeline. Exactly when a Factory replacement is done is quite important. As previously mentioned, the replacement has to be done before any instances of the class have been created. This depends on the type of class being replaced. For example, dynamic objects (such as transactions), created many times during normal operation of the testbench, are likely to be created after the test has started. However, structural objects (such as instances of a VIP), are likely to be created once when the testbench is built. The details of where to put Factory overrides is covered in another post. ShareThis
JL Gray
by Jason Sprott Jason Sprott is CTO at Verilab In a previous post we took a look at what the VMM Factory is and what it could do for us. In this post, we take a look at the problem the Factory solves. In order to understand when we should use a Factory, we really need to understand a bit more about the problem it solves, so we can spot situations where a Factory might be appropriate. Let’s start by looking at how we might do things in SystemVerilog without using VMM Factories. Typically we might instantiate a class of type Foo like this: Foo my_foo; … my_foo = new(…); The problem with that is my_foo will always create an object of type Foo, no matter what.  We can’t change that. We might use this class Foo in lots of places around our testbench code. If we want to replace Foo with a new class that maybe adds more variables, or replaces a method, we could have a problem. We’d have to go around our testbench looking for everywhere Foo is used, to see if there was a way it could be replaced. Anywhere Foo is constructed using new(), it’s is highly likely to involve modifying the original code. This is very undesirable, especially if the code may be part of a tested IP library. In general, modifying the original source is a Bad Thing. In VMM we can avoid this problem using a different way to create an instance of the class with the factory. It would look something like this: Foo my foo; … my_foo = Foo::create_instance(…); Although this looks similar to an instantiation with new(), something much more sophisticated  is at play. If we factory enable the class Foo, we never call new()to instantiate it again. We now call the Factory’s static method for generating instances of the object create_instance(). Since the method is static, it can be called without having an instance of Foo, therefore it can be used to create itself. What’s the point? Now that we’ve encapsulated the task of creating an instance in a method, we can change what that method does. We can tell create_instance() to return something different. This might be a new type (with some modifications), derived from Foo, or the same type populated with different values for the variables. What’s more, we can do this easily anywhere Foo is used in the code. We can pick specific instances to be replaced, or replace multiple instances globally. The two methods used to reprogram the factory are: •    create_with_new() – tells the factory, when creating new instances, to return a brand new instance of the type specified. •    create_with_copy() – tells the factory, when creating new instances,  to return a copy of instance specified. This replacement can be done anywhere in the code, as long as it happens before the instances you want to replace are created. Let’s say a test needs to replace our type Foo, with a new class, FooWithAttitude, derived from Foo. Here’s what that might look like: class my_factory_test extends vmm_test;    …    virtual function void configure_test_ph();       Foo::override_with_new(         // Foo’s factory is being overridden         "@%*",                        // instances matching this pattern will be replaced         FooWithAttitude::this_type(), // they will be replaced with this type of class         log, `__FILE__, `__LINE__);   // some generic log and debug stuff    endfunction:configure_test_ph    … endclass:my_factory_test As can be seen, the Factory override uses regular expression pattern matching to specify which instances will be targeted. The syntax is expressive enough to indentify single instances, multiple instances (e.g. by hierarchy), or all instances (as in this example). More information on the pattern matching syntax can be found in the “Common Infrastructure and Services > Simple Match Patterns” section of the VMM User Guide.  This next example shows how the factory can be programmed to return a copy of the class we’ve modified with some values. class test_read_back2back extends vmm_test;    …    virtual function void configure_test_ph();       FooBusTrans tr = new();        // create a template for the override copy       tr.address = ‘habcd_1234;      // special value we want to set up for override       tr.address.rand_mode(0);       // you might want to protect value during randomization       FooBusTrans::override_with_copy(          "@top:foobus0:*",           // instances matching this pattern will be replaced          tr,                         // they will be replaced by a copy of this instance          log, `__FILE__, `__LINE__); // some generic log and debug stuff    endfunction    … endclass The above example  shows a Factory override in the configure test phase of the simulation timeline. Exactly when a Factory replacement is done is quite important. As previously mentioned, the replacement has to be done before any instances of the class have been created. This depends on the type of class being replaced. For example, dynamic objects (such as transactions), created many times during normal operation of the testbench, are likely to be created after the test has started. However, structural objects (such as instances of a VIP), are likely to be created once when the testbench is built. The details of where to put Factory overrides is covered in another post. ShareThis
Posted in
Syndicate content