Cadence.com will be under maintenance from Friday, Oct. 3rd at 6pm (PST) thru Sunday, Oct 5th at 11pm (PST).
Cadence.com login, registration, community posting and commenting functionalities will be disabled.
Home > Community > Blogs > Functional Verification > generic dynamic run time operations with e reflection part ii
 
Login with a Cadence account.
Not a member yet?
Create a permanent login account to make interactions with Cadence more convenient.

Register | Membership benefits
Get email delivery of the Functional Verification blog (individual posts).
 

Email

* Required Fields

Recipients email * (separate multiple addresses with commas)

Your name *

Your email *

Message *

Contact Us

* Required Fields
First Name *

Last Name *

Email *

Company / Institution *

Comments: *

Generic dynamic run-time operations with e reflection Part II

Comments(0)Filed under: coverage driven verification (CDV), Aspect Oriented Programming, Funcional Verification, e language, AF, reflection, Functiional Verification

Field access and method invocations

In the previous blog, we explained what are untyped variables and value holders in e, and how to assign and retrieve values to/from them. In this and the next blogs, we will see how they can be used in conjunction with the Reflection API, to perform operations at run time.

Normally, when you declare fields in your e structs and units, you then procedurally assign values to those fields at some points and retrieve their values at others. When you declare a method, you call it with certain parameters and retrieve its return value for later use. All of this is fine when you deal with a specific field or method, and that is what you need most of the time.

But what if you want to perform some generic operation? For example, you may want--given any e object (of any struct or unit type, which is unknown upfront)--to go over all its numeric fields and print their values. Or, you may want to traverse the whole unit tree, and on every unit whose type has a specific method (given by name), call that method and print its result.

The Reflection API allows us to perform tasks like that in a fairly easy manner. Here are some reflection methods which are helpful for those tasks. Given an instance object, the following two methods allow you to get the reflection representation of the struct or unit type of the object.

  • rf_manager.get_struct_of_instance(instance: base_struct): rf_struct

This method returns the like struct of the object, disregarding when subtypes.

  • rf_manager.get_exact_subtype_of_instance(instance: base_struct): rf_struct

This method returns the most specific type, including when subtypes, of the object.

For example, for a red packet instance, get_struct_of_instance() will return the reflection representation of type packet, and get_exact_subtype_of_instance() will return the representation of type red packet.

The following methods of rf_field allow, given an instance object of some struct, to set or get the value of the specific field of that object.

  • rf_field.set_value(instance: base_struct, value: rf_value_holder);
  • rf_field.set_value_unsafe(instance: base_struct, value: unsafe);
  • rf_field.get_value(instance: base_struct): rf_value_holder;
  • rf_field.get_value_unsafe(instance: base_struct): unsafe;

The set_value methods take the value passed as parameter, and assign it to the given field of the specified object. The get_value methods retrieve the value of the given field of the specified object and return it. There is a safe and an unsafe version of each method. The safe version uses a value holder, which already contains the type information for the value (as was explained in the previous blog), performs additional checks, and throws a run-time error in case of an inconsistency (for example, if the field does not belong to the struct type of the given instance). The unsafe version (the one with the _unsafe suffix) does not use a value holder and does not perform such checks; in case of an inconsistency, its behavior is undefined and might even cause a crash. Thus, you need to use it with a care. However, the unsafe version is more efficient, and I recommend using it when possible.

Similar to the above rf_field methods, the following methods of rf_method, given an instance object of some struct, allow you to invoke a specific method of that object or to start a TCM.

  • rf_method.invoke(instance: base_struct, params: list of rf_value_holder): rf_value_holder;
  • rf_method.invoke_unsafe(instance: base_struct, params: list of unsafe): unsafe;
  • rf_method.start_tcm(instance: base_struct, params: list of rf_value_holder);
  • rf_method.start_tcm_unsafe(instance: base_struct, params: list of unsafe);

The invoke methods call the given method on the specified object and return the value returned from that method. If the given method has parameters, they should be passed as a list in the second parameter; the list size must exactly match the number of parameters the method expects to get. Similarly, the start_tcm methods start the given TCM on the specified object. As with the rf_field methods above, the difference between the safe and unsafe versions of these methods is that the safe one uses value holders and performs additional run-time checks, while the unsafe version is more efficient.

The following short example demonstrates the usage of the above methods. The following method gets an object of an unknown type (declared as any_struct) and a method name. It goes over all fields of the object whose type is int, and calls the method by the given name, passing the field value as parameter. For simplicity, we assume it is known that the method by the given name indeed exists and has one parameter of type int.

extend sys {

    print_int_fields(obj: any_struct, meth_name: string) is {

        // Keep the reflection representation of the int type itself

        var int_type: rf_type = rf_manager.get_type_by_name("int");

        // Keep the struct type of the object

        var s: rf_struct = rf_manager.get_exact_subtype_of_instance(obj);

        // Keep the method which is to be called

        var m: rf_method = s.get_method(meth_name);

        // Go over fields of the struct

        for each (f) in s.get_fields() do {

            // Is this field of type 'int' ?

            if f.get_type() == int_type then {

                // Retrieve the field value ...

                var value: untyped = f.get_value_unsafe(obj);

                // ... and pass it to the method

                compute m.invoke_unsafe(obj, {value});

            };

        };

    };

};

 

In the next blog in the series, we will discuss some additional relevant reflection methods, give several tips, and look at some more interesting examples.

 

Yuri Tsoglin

e Language team, Specman R&D 

Comments(0)

Leave a Comment


Name
E-mail (will not be published)
Comment
 I have read and agree to the Terms of use and Community Guidelines.
Community Guidelines
The Cadence Design Communities support Cadence users and technologists interacting to exchange ideas, news, technical information, and best practices to solve problems and get the most from Cadence technology. The community is open to everyone, and to provide the most value, we require participants to follow our Community Guidelines that facilitate a quality exchange of ideas and information. By accessing, contributing, using or downloading any materials from the site, you agree to be bound by the full Community Guidelines.