Skip to Content

Changing Functionality: The Factory Service #1

Posted on  by  from the site Verification Martial Arts
by Jason Sprott Jason Sprott is CTO at Verilab. Building a testbench using SystemVerilog, an object-oriented testbench language, does not automatically make the end solution reusable, or easily extensible. In SystemVerilog we can certainly implement the kind of object-oriented principles and design patterns that enable reuse, but this requires significant programming skills and experience in understanding the requirements of building substantial reusable software solutions. Also, when users inevitably need to change the original functionality, the mechanisms in place to allow those changes would have to be well documented and understood. Two common changes we might need to make are: •    Swap one type of class with another (which may replace or add new constraints, variables and methods). •    Add new functionality to an existing method without replacing it Fortunately the VMM provides some standard solutions for handling these types of changes. This post takes a look at how the Factory Service helps with the first of the two requirements, swapping classes. There are many cases where we might want to swap one class for another in a testbench. For example, a typical requirement in constrained random testbenches is to replace one randomizable class with derived version, adding or modifying constraints. We might also have various derived versions of VIP, supporting slightly different versions of a protocol, or injecting different types of errors. When we first develop some VIP, or a testbench, it’s almost impossible to predict what people will want and need from our solution in the future. We can however provide users with a standard way to replace our implementation with something slightly different. This could be a derived version (extension with additions or modification), of what we originally implemented, or a copy with modified values. Although this sounds quite trivial, testbenches that do not have this capability can be very hard to change without modifying the original source. Changing an original implementation to add new functionality is undesirable and sometimes impossible. Access to original source code is quite often restricted, or at least controlled. The original code may be well tested and proven, and changing the original source could affect other users of the code. Testbench components implementing the Factory are easily replaced at runtime without modifying the original source. Such modification can even be done on a per test basis if required.   However, we don’t get a Factory for free; there’s a bit of up-front thought required. We have to care enough about this capability to implement a Factory in the first place, obviously. This seems obvious, but in fact many times I come across a class that was in dire need of a factory that wasn’t there, the only reason was that the original developer didn’t think to put one in. I try not to judge too harshly, because we’ve all developed code that didn’t quite meet down-steam requirements at some point in time. Theres’s also a bit of effort (a small amount of additional coding) required. So why should we bother? The VMM Factory Service (Factory) is a standard mechanism for changing functionality by replacing classes. VMM has always recommended the use of Factories, but The Factory Service was introduced in VMM 1.2 to ease implementation. The Factory provides the following API and utilities: •    Short-hand macros to make implementing a Factory simple. The macros implement the Factory API methods for a given class. •    API method to create instances of the class – replacing the use of the new()constructor method. •    API method to change the instance generated by the Factory to a new derived type. •    API method to change the instance generated by the Factory to a copy of a class of the same type •    Specific and regular expression based selection of component factories to override As an example, imagine a Bus VIP. The VIP has quite a lot going on inside. It has a master, a slave, some monitors, coverage and checking. All use a particular transaction type. So if we wanted to replace that transaction with a derived type, to maybe inject some errors, it would affect multiple components in the VIP. If any of the components using the transaction create an instance using new(), there’s a pretty good chance we cannot replace the transaction type without modifying that source. This may not be an option, or at the very least not desirable. If we use the Factory, we can not only do the replacement, we can choose which instances of the VIP in a testbench we want to perform the replacement on. Maybe not all nodes on the bus have to inject errors. In our test, or new testbench environment, we might decide to say something like: •    When creating new classes of type Foo, instead instantiate my new class FooWithAttitude. Using the Factory in VMM that might look like:       Foo::override_with_new(         // Foo’s factory is being overridden         "@%*",                        // match all instances         FooWithAttitude::this_type(), // they will be replaced with this type of class         log, `__FILE__, `__LINE__);   // some generic log and debug stuff Or, maybe we want to make sure some variables in a configuration object are replaced with something specific. We can make sure a copy of a class is instantiated, with some specific values set. We might decide to say something like: •    When creating new classes of type Foo,  instead insatiate my copy of that class (in this case we have created our own instance, tr), with some values set and randomization turned off:       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 To understand when we should use a Factory, we really need to understand a bit more about the problem it solves. We’ll discuss this in another post. ShareThis
JL Gray
by Jason Sprott Jason Sprott is CTO at Verilab. Building a testbench using SystemVerilog, an object-oriented testbench language, does not automatically make the end solution reusable, or easily extensible. In SystemVerilog we can certainly implement the kind of object-oriented principles and design patterns that enable reuse, but this requires significant programming skills and experience in understanding the requirements of building substantial reusable software solutions. Also, when users inevitably need to change the original functionality, the mechanisms in place to allow those changes would have to be well documented and understood. Two common changes we might need to make are: •    Swap one type of class with another (which may replace or add new constraints, variables and methods). •    Add new functionality to an existing method without replacing it Fortunately the VMM provides some standard solutions for handling these types of changes. This post takes a look at how the Factory Service helps with the first of the two requirements, swapping classes. There are many cases where we might want to swap one class for another in a testbench. For example, a typical requirement in constrained random testbenches is to replace one randomizable class with derived version, adding or modifying constraints. We might also have various derived versions of VIP, supporting slightly different versions of a protocol, or injecting different types of errors. When we first develop some VIP, or a testbench, it’s almost impossible to predict what people will want and need from our solution in the future. We can however provide users with a standard way to replace our implementation with something slightly different. This could be a derived version (extension with additions or modification), of what we originally implemented, or a copy with modified values. Although this sounds quite trivial, testbenches that do not have this capability can be very hard to change without modifying the original source. Changing an original implementation to add new functionality is undesirable and sometimes impossible. Access to original source code is quite often restricted, or at least controlled. The original code may be well tested and proven, and changing the original source could affect other users of the code. Testbench components implementing the Factory are easily replaced at runtime without modifying the original source. Such modification can even be done on a per test basis if required.   However, we don’t get a Factory for free; there’s a bit of up-front thought required. We have to care enough about this capability to implement a Factory in the first place, obviously. This seems obvious, but in fact many times I come across a class that was in dire need of a factory that wasn’t there, the only reason was that the original developer didn’t think to put one in. I try not to judge too harshly, because we’ve all developed code that didn’t quite meet down-steam requirements at some point in time. Theres’s also a bit of effort (a small amount of additional coding) required. So why should we bother? The VMM Factory Service (Factory) is a standard mechanism for changing functionality by replacing classes. VMM has always recommended the use of Factories, but The Factory Service was introduced in VMM 1.2 to ease implementation. The Factory provides the following API and utilities: •    Short-hand macros to make implementing a Factory simple. The macros implement the Factory API methods for a given class. •    API method to create instances of the class – replacing the use of the new()constructor method. •    API method to change the instance generated by the Factory to a new derived type. •    API method to change the instance generated by the Factory to a copy of a class of the same type •    Specific and regular expression based selection of component factories to override As an example, imagine a Bus VIP. The VIP has quite a lot going on inside. It has a master, a slave, some monitors, coverage and checking. All use a particular transaction type. So if we wanted to replace that transaction with a derived type, to maybe inject some errors, it would affect multiple components in the VIP. If any of the components using the transaction create an instance using new(), there’s a pretty good chance we cannot replace the transaction type without modifying that source. This may not be an option, or at the very least not desirable. If we use the Factory, we can not only do the replacement, we can choose which instances of the VIP in a testbench we want to perform the replacement on. Maybe not all nodes on the bus have to inject errors. In our test, or new testbench environment, we might decide to say something like: •    When creating new classes of type Foo, instead instantiate my new class FooWithAttitude. Using the Factory in VMM that might look like:       Foo::override_with_new(         // Foo’s factory is being overridden         "@%*",                        // match all instances         FooWithAttitude::this_type(), // they will be replaced with this type of class         log, `__FILE__, `__LINE__);   // some generic log and debug stuff Or, maybe we want to make sure some variables in a configuration object are replaced with something specific. We can make sure a copy of a class is instantiated, with some specific values set. We might decide to say something like: •    When creating new classes of type Foo,  instead insatiate my copy of that class (in this case we have created our own instance, tr), with some values set and randomization turned off:       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 To understand when we should use a Factory, we really need to understand a bit more about the problem it solves. We’ll discuss this in another post. ShareThis
Posted in
Syndicate content