If you have had a chance to work with languages like Java or C#, you might have come across Annotations. Since the Specman 17.10 version, annotations have become part of the e language! (See Java annotation and Basic Introduction to Data Annotation in .Net Framework.)
What are annotations? Annotations are a form of metadata that provides information about an entity in your code. However, they have no direct effect on the operation of the entity they annotate. For example, using the development_status– the annotation below - we provide some information on the development status of the struct packet_s.
@development_status(developer="David", req_ID="REQ-2.3",comment="Integrating with Lisa")
struct packet_s{
…
};
We will get back to this example. But before we do, let’s answer some questions you might have in mind.
Hmm…. isn’t that what comments are for? Well, yes,annotations are similar to comments. However, they are structured, and more importantly, you can write a code that easily “reads” and processes the information of these comments.
Hmm…isn’t this why we have members for units/structs (we can even define them as static)? Yes, this is true. However, when you add annotations to some entity (some struct for example):
- You do not impact the run time of the entity
- You can use the same annotation for different types of entities
- You set them at design time (when you write your code) and you cannot change them in run time
So to sum it all up, you can see annotations as structured comments that you can later on process, that do not affect run-time, and that can be placed almost anywhere.
Hmm… when do I use annotations? First, I must admit that you can spend your entire life working successfully with Specman without using annotations. However, for an even more productive verification system, it is recommended you continue reading this blog and consider how you can enhance your development environment using this advanced feature.
Usually, you use annotations when you want to perform some off-line actions on your code, without having any effect on run-time. These kinds of actions generally include: unit-tests, linting tools, dynamic documentation, etc.
With e, you can annotate different language constructs, such as type, struct, struct member, etc. Let’s look at an example of a situation in which annotations can be very helpful.
Hopefully, you are already running a linting tool (either a custom one, or the Cadence tool: HAL), running on a regular basis (otherwise, it is highly recommended you read about HAL in csdnshelp). Let’s assume that you are using HAL and you have your own set of custom rules corresponding to your methodology. Now with annotations, you can enrich HAL by annotating some entities with information HAL can read and use to implement more specific checks.
For example, let’s say you want to make sure that Transaction level units/structs do not have TCMs. How do you do that?
Define the annotation
First, you define the annotation in some central location. In our case, you define a Transaction level annotation:
annotation@transaction_level{};
Note that annotations can have fields, but in our case, we just want a very simple annotation.
Annotate
After you have defined it in some central location, it is available for all the engineers. Now, every engineer is asked to annotate each entity (struct/unit), which is a Transaction level entity, by adding the transaction_level annotation before the declaration:
@transaction_level
unit checker_u{ ….
};
Read the annotations
After the engineers have annotated their code with this annotation, you want to actually read it. In your linting tool, you can find all the entities that have this annotation and produce some message in case they have a TCM. Let’s look at the following code:
for each rf_struct (s) in rf_manager.get_user_types() do {
if( transaction_level::get_attached_annotation(s.get_declaration())!=NULL){
for each rf_method (m) in s.get_methods(){
if(m.is_tcm()){
out(s.get_name()," is annotated as a transaction level entity while it has the following TCM: ",m.get_name());
};//tcm
};//for each method
};//struct with annotation
}//for each user struct
In this code, we use a predefined static method available for each annotation you define, called: get_attached_annotation. This method returns an annotation object representing the particular annotation that is attached to a given entity. If this entity does not have this specific annotation, it returns NULL. In this piece of code, we:
- Iterate over each struct the user has defined
- Then if it has a transaction_level annotation, we iterate over its methods
- If a method is actually a TCM, we print a message
In order to see it in action, I put the following code that violated the methodology in my environment:
@transaction_level
unit checker_u{
my_tcm()@sys.any is {
out("I am a TCM in a Transaction Level unit.");
};
};
Then, I added the code mentioned at the beginning of this section (which prints a message when a Transaction level entity has a TCM) to my custom rules in HAL. (I will not get into details about HAL since it is out of this scope, but it is very easy to add custom rules to HAL.) When I ran HAL on my code, I got the following message:
What is cool about this example is the fact that I have told my linting tool something important by putting this information in the right place, next to the relevant entity, where it is visible and natural. Any other solution would require some hardcoding information about my entities in the linting tool, which is clearly not what we want to do.
There are other multiple examples of what you could do with annotations. Let’s go back to the first example of annotation at the beginning of this blog: @development_status. Sometimes, you check-in your code before the requirement/feature is completed. You might do it for multiple reasons. First, it is never healthy to have a code on a private view for too long. Second, if you want to integrate with another team and you know your code is “dead” for the time being and there is no risk, it may be much easier to just check it in the repository. However, you might want to control these cases and monitor them. In this case, you should first define the annotation, including some fields that provide information:
annotation@development_status{
developer:string;
req_ID: string;
comment:string;
};
And then you can annotate it where relevant:
@development_status(developer="David", req_ID="REQ-2.3",comment="Integrating with Lisa")
struct packet_s{
…
};
Now, you can, for example, produce reports about these cases from some tool/script and send them to the relevant people (the developer, the integrator or the manager) to closely monitor these cases.
Any suggestion on other cool uses? Would you like to share your ideas with us? We’d be glad to hear from you…
Orit Kirshenberg, Specman team