Quantcast
Channel: Cadence Functional Verification
Viewing all articles
Browse latest Browse all 652

Generic Dynamic Runtime Operations With e Reflection - Part 3: Additional Capabilities and Conclusion

$
0
0

 

This post concludes the series of blog posts that discuss the dynamic capabilities of the Reflection API in ePart one described the basics of generic value assignments and retrievals, using untyped and value holders. Part Two explained how to manipulate field values and invoke methods in a generic manner. If you have not read those two blogs and are not familiar with those concepts yet, I strongly recommend reading them before reading the rest of this post. 

In this post, we'll take a look at a couple of additional reflection methods, and see an example that makes use of several features described in this blog series.

As described in the previous posts, you can use untyped variables or value holders to store values of unknown types and retrieve them. But how can you print such a generic value or convert it to a string? And how can you, given two generic values, compare between them?

Using the print action or one of the output-producing constructs such as out() or message() with untyped values directly will not achieve the desired effect. Furthermore, passing untyped to routines such as out() is under deprecation and will be disallowed in future Specman versions. Using to_string() with such values is also disallowed. There is a good reason for this: Since an untyped variable does not "know" the actual type of the stored value, it also does not know how to correctly convert this value to a string or how to print it.

Similarly, comparing between two untyped values using the == operator does not do the job. Since the untyped variables do not know the actual types of the values (and do not even know whether their types are the same or compatible), they do not know how to compare. The comparison operator works differently with different types.

The following two reflection methods help solve these problems.

rf_type.value_to_string(value: untyped): string

This method converts the given value to a string, assuming that the value belongs to the type on which it is called.

rf_type.value_is_equal(value1: untyped, value2: untyped): bool

This method compares between the two values, assuming that both values belong to the type on which it is called, using the == operator semantics of that type, and returns TRUE if the values are equal.

With both methods, it is your responsibility to make sure that the untyped parameters actually store values of the given type. If that is not the case, the behavior of these methods is undefined, and may even lead to a crash.

The following example shows a method that gets two struct objects of an unknown type, compares between all scalar fields of these two objects, and displays a message if the values of some scalar field in the two objects are different.

compare_structs(first: any_struct, second: any_struct) is {

 

        // What is the type of the first object?

        var s: rf_struct = rf_manager.get_struct_of_instance(first);

 

        // If the second object is not of the same type - print a message and return

        if s != rf_manager.get_struct_of_instance(second) {

            outf("Types of %s and %s differ\n", first, second);

            return;

        };

 

        // Go over all fields of both objects, according to their struct type

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

           

            // We are only interested in scalar fields - skip others

            if f.get_type() is not a rf_scalar {

                continue;

            };

 

            // Retrieve the value of the field from both objects

            var first_f_val: untyped = f.get_value_unsafe(first);

            var second_f_val: untyped = f.get_value_unsafe(second);

 

            // Compare the two values, and if they are not equal, print a message

            if not f.get_type().value_is_equal(first_f_val, second_f_val) {

 

                // Use value_to_string() to print the actual values

                outf("Value of %s for %s is %s, whereas for %s it is %s\n",

                    f.get_name(),
                    first, f.get_type().value_to_string(first_f_val),

                    second, f.get_type().value_to_string(second_f_val));

            };

        };

    };

}; 

 

To conclude, the Reflection API in e allows you not only to make static queries about your code, but also to perform runtime operations in a generic manner. In addition to the reflection methods described in this blog series, there are more methods that allow operations on lists, checking whether a given field's value is consistent with its declared type (e.g., for when subtypes or scalar subrange types), checking whether a given constraint is satisfied by a given struct, and so on. For more information, consult Cadence online help, or access the Reflection API online documentation (in the eDoc format):
<you_incisiv_install_dir>/specman/docs/reflection_api_edoc/html/index.html
directly from your favorite web browser.

 

Yuri Tsoglin

e Language team, Specman R&D 


Viewing all articles
Browse latest Browse all 652

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>