Home > Community > Blogs > Functional Verification > extending multiple when subtypes simultaneously
Login with a Cadence account.
Not a member yet?

Register | Membership benefits


* 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: *

Extending Multiple When-Subtypes Simultaneously

Comments(0)Filed under: Functional Verification, e, Specman, AOP, IES-XL, macros, ClubT

[For those of you that didn't / can't make it to a ClubT last week/this week, here is a meaty technical article from guest bloggers Matan Vax (R&D Architect), Yuri Tsoglin (R&D), and Dean D'Mello (CoreComp Architect)]

When subtypes let you add properties and behavior to a struct or a unit when one or more enumerated/boolean fields take on specific values. The fields that define when subtypes are called determinants.

Consider the following data type:

      type size_t: [TINY, SMALL, BIG, HUGE];
      struct packet {
            size: size_t;
           // etc.

If the behavior of a corrupt packet applies only to huge packets, you can define it thus:

      extend HUGE packet {
             corrupt: bool;
             keep corrupt => ...; // mess up data if corrupt flag is TRUE

This is, of course, elementary e. But imagine that similar functionality applies to both tiny and huge packets. Can you extend both at once? Is there a tiny-or-huge packet type?

This request is often raised by e users, as there is no built-in language construct for doing so. Fortunately, e is an extensible language, so users can introduce their own constructs with macros to address specific needs.

Here is one way to define the syntax of the required construct:

      extend [TINY, HUGE] packet {
             // ... definitions that apply to both TINY & HUGE packets

We need to come up with a macro that expands such code into a separate 'extend' statement for each of the specified determinant values. Since the number of values may vary, we need to expand the code iteratively. So we need a computed macro, which will return a string of code to be parsed when code with calls to the macro is loaded/compiled.

The definition of the macro is rather straightforward:

      define <multi_when'statement> "extend \[<detr'name>,...\] <base'type> (<MEMBERS>{<struct_member>;...})" as computed {

            for each in <detr'names> do {
                   result = appendf("%s extend %s %s %s;",result,it,<base'type>,<MEMBERS>);

The "trick" to this is the use of a repetition operator in the match expression - [<detr'name>,...]. It reads: one or more comma separated determinant names (identifiers), enclosed in square brackets. With that in the match expression, the variable <detr'name> holding the respective list of matched strings is available inside the macro body. For convenience we also use the submatch label <MEMBERS>  for the struct-member block.

The macro capabilities of Specman were greatly enhanced over the past few years, including debug tools: you can see the expansion of loaded macros and use the step-debugger with macro code too! The e reference manual has a more information on computed macros, explaining the structure of match expressions and their operators. It also has some interesting application examples, and info on debugging capabilities.

An important thing note to about this "multi-when" pattern: It syntactically duplicates the required definitions in the scope of each subtype. It does not provide a way to abstract over the added functionality. So in the above example one cannot declare a field or variable of type '[TINY, HUGE] packet', and so cannot get access to field corrupt other than by having a variable of the specific subtype - either 'TINY packet' or 'HUGE packet'. There is simply a different 'corrupt' field for each subtype. When implementing a macro solution it is important for the macro writer to understand the needs of those who will use it as well as the capabilities and limitations of the generated code.

If the "disjoint" property is something that is worth abstracting over (and this is often the case), there is a simple way to introduce the commonality in real semantic terms. Instead of the macro example above, we need to:

1.  Introduce a new boolean attribute
2.  Put the relevant definitions under it
3.  Determine the relation to existing properties through constraints

Here is how it would be done for our example:

     extend packet {
            corruptible: bool;
            keep corruptible == (size in [TINY, HUGE]);
            when corruptible packet {
                 corrupt: bool;

Note that the new subtype 'corruptible packet' lets you access the functionality without having to know the packet's size. The condition on the corruptible property is as flexible as could be. One can declare that, for example, corruptible packets are all but small packets, or dependent on ranges of other scalar attributes.

This pattern too can be captured in a simple macro and made into a single construct with native look & feel. We leave its definition to the reader...


Happy macro writing!

Matan Vax, Yuri Tsoglin, Dean D'Mello

(Plus a shout-out to AE Nils Luetke-Steinhorst who inspired this post based on recent work with a customer on this issue.)

Team Specman


Leave a Comment

E-mail (will not be published)
Community Guidelines