How do you define elegant or clean code? Usually, you know it when you see it; defining it is harder. It is usually a simple, clear and well-structured code. OO programming languages (like Java, C++, System Verilog, and also e) provide you with aids to write elegant code. This blog is about static members in e. Can you live without them? Usually, you can. What can they do for you? Help you write elegant and clean code. What if you don’t care about your code being elegant? Well, first you should care. But even if you don’t, there are problems you just cannot solve without static members.
Since Incisive version 15.2, the static keyword is part of e. This means that you can declare structs’ members as static (fields, methods or events) and get members that belong to the struct type, rather than to an instance of the struct. This also means that no matter how many instances of the struct are created (even none), there is only one copy of each static member which is shared by all instances of the struct type. This is actually the same idea as in the other OO programming languages mentioned above.
Let’s look at a simple example. In the code below, the field max_addressis declared static. This means that there will be only a single max_addressfield that will be created and initialized to 0x1000 before any instance of packet_s is created.
extend packet_s{ staticmax_address : uint = 0x1000; };
|
You can access this field directly from every instance of packet_s (as if it is a member of it). From other places it can be accessed using the struct type name followed by “::” as in the example below.
extend packet_s{ staticmax_address : uint = 0x1000; keep addr <= value(max_address); };
extend monitor_s{ on reconfiguration_ended { packet::max_address =0x10000; }; }; |
Let’s look at some typical scenarios where using static fields can be useful.
Scenario1: Configuration
Let’s say that we want to limit the address of the packets during the run while the limit can be dynamic and changed during the run (as we have just seen in the example). It makes sense that it will be within the packet_s declaration, but since it is the same value for all instances, it also makes sense to hold it in a static field member. We could have put it under sys or ‘global’ but is this the right thing to do? If logically this is part of packet_s, then it should be defined as part of packet_s.
Now let’s say that we want to track how many times this field is being set in our environment. We could do this by making sure that max_address can be set through a set method (set_max_address) where we can count the number of times it is being called. To do that, we need to have a static method and an additional static field. We would also define max_addressas private so that the only way to set it would be through the method.
extend packet_s{ private staticmax_address : uint = 0x1000; keep addr <= value(max_address);
staticnum_set_max_address_call:uint=0;
staticset_max_address(new_val : uint ) is { max_address = new_val; num_set_max_address_call+=1; }; }; |
What if you still need a central configuration for the entire environment? Some users define a configuration struct type and instantiate it under sys. A more elegant way is to have this configuration object (instantiated in the most appropriate place) with static methods that return the configuration items.
Prior to version 15.2, this is the old style you would use:
… var config_s := get_enclosing_unit(env).config; if config…..
|
Now, with the support of static members, you can define a config_s struct which holds the configuration and have static methods that return the configuration details. These methods can be accessed from anywhere without knowing where the instance actually resides.
… if (config_s::bus_mode() == …) then … |
What did we get? Less code and no need to know where the configuration object is instanced – in a word, more robust and elegant code.
Scenario 2: Have a unique ID for each instance
Let’s take a look at another scenario. Let’s say that we want to make sure that each packet has a unique id (pkt_unique_id). An easy way to do it is by adding a single counter for all packet_s instances (packets_counter).
extend packet_s{ pkt_unique_id : uint; staticpackets_counter : uint=0;
init() is also{ //get the current unique id from the counter value pkt_unique_id=packets_counter;
//increment the counter packets_counter+=1; }; }; |
Scenario 3: collect information for coverage
Let’s go back to the previous example where the maximum possible address was defined. You might want (for coverage purposes) to also know what was the maximum address that was generated de-facto. Using a static field (maximum_address) is the convenient way:
extend my_packet_s{ staticmaximum_address : uint =0; post_generate () is also{ if (addr >maximum_address) then { maximum_address=addr; } }; }; |
Scenario 4: Template struct field
I started by saying that using static is more elegant than adding fields to global. But you could say “I don’t much care for elegance. Defining a field in global works, and that’s good enough for me!” So what about this – consider the template of stack below. How would you handle adding a singleton max_size to a stack? Naturally you would want a different max_size for each type instantiated (as you want one max_size value for “stack of packets” and a different max_size value for “stack of instructions”).
template struct stack of <type> { }; |
How would you do it without a static member? Well, you can’t (at least not without lots of macros and tricks…).
How do you do it with static member? Very simple, you just define a static field.
template struct stack of <type> { static max_size: uint; … }; |
If you think of other things you can do with static members that you cannot simply do without-we will be happy to hear.
These were only simple examples to “give” you a taste; of course, there is more information in cdnshelp. We suggest you leave a place in your toolbox for static members. We encourage you also to check what you currently have under sys and global- can you find there constructs that logically belong to a specific context? Does it make sense to define them as static members in a more appropriate place?
What is your opinion?
Let’s say you are part of the Specman development team. Would you enable users to override a static method in a when subtype? Take several minutes to think about it.
Well, apparently, this is not a trivial question. Static method means that the method doesn't need an instance to operate on while when subtype is all about a concrete instance. So, no, this is not allowed (as C++ and Java do not allow it). If you think that it makes sense to enable it, we are happy to hear your voice!
Orit Kirshenberg Specman team