I am a Verification Engineer at Intel. I started the Verification blog to store solutions to small (and big) problems I've faced in my day to day work. I want to share them with the community in the hope that they may be useful to someone else.
Wednesday, 21 October 2015
Friday, 16 October 2015
Makefile
The make utility automatically determines which pieces of a large program need to be recompiled, and issues commands to recompile them.
To prepare to use make, you must write a file called the makefile that describes the relationships among files in your program and provides commands for updating each file.
To prepare to use make, you must write a file called the makefile that describes the relationships among files in your program and provides commands for updating each file.
In a program, typically, the executable file is updated from object files, which are in turn made by compiling source files. Once a suitable makefile exists, each time you change some source files, this simple shell command:
make
You can provide command line arguments to make to control which files should be recompiled, or how.
A simple makefile consists of “rules” with the following shape:
target ... : prerequisites ...
recipe
...
...
make
You can provide command line arguments to make to control which files should be recompiled, or how.
A simple makefile consists of “rules” with the following shape:
target ... : prerequisites ...
recipe
...
...
A target is usually the name of a file that is generated by a program; examples of targets are executable or object files. A target can also be the name of an action to carry out, such as ‘clean’.
A prerequisite is a file that is used as input to create the target. A target often depends on several files.
A recipe is an action that make carries out. A recipe may have more than one command, either on the same line or each on its own line.
A prerequisite is a file that is used as input to create the target. A target often depends on several files.
A recipe is an action that make carries out. A recipe may have more than one command, either on the same line or each on its own line.
Please note: you need to put a tab character at the beginning of every recipe line!
If you prefer to prefix your recipes with a character other than tab, you can set the .RECIPEPREFIX variable to an alternate character.
.RECIPEPREFIX
The first character of the value of this variable is used as the character make assumes is introducing a recipe line. If the variable is empty (as it is by default) that character is the standard tab character. For example, this is a valid
makefile:
.RECIPEPREFIX = >
all:
> @echo Hello, world
The value of .RECIPEPREFIX can be changed multiple times; once set it stays in effect for all rules parsed until it is modified.
The .RECIPEPREFIX is only supported since v3.82.
Usually a recipe is in a rule with prerequisites serves to create a target file if any of the prerequisites change.
However, the rule that specifies a recipe for the target may not need to have prerequisites. For example, the rule containing the delete command associated with the target ‘clean’ does not have prerequisites.
If you prefer to prefix your recipes with a character other than tab, you can set the .RECIPEPREFIX variable to an alternate character.
.RECIPEPREFIX
The first character of the value of this variable is used as the character make assumes is introducing a recipe line. If the variable is empty (as it is by default) that character is the standard tab character. For example, this is a valid
makefile:
.RECIPEPREFIX = >
all:
> @echo Hello, world
The value of .RECIPEPREFIX can be changed multiple times; once set it stays in effect for all rules parsed until it is modified.
The .RECIPEPREFIX is only supported since v3.82.
Usually a recipe is in a rule with prerequisites serves to create a target file if any of the prerequisites change.
However, the rule that specifies a recipe for the target may not need to have prerequisites. For example, the rule containing the delete command associated with the target ‘clean’ does not have prerequisites.
Rule, then, explains how and when to remake certain files which are the targets of the particular rule. make carries out the recipe on the prerequisites to create or update the target.
A rule can also explain how and when to carry out an action.
A rule appears in the makefile and says when and how to remake certain files, called the rule’s targets (most often only one per rule). It lists the other files that are the prerequisites of the target, and the recipe to use to create or update the target.
The order of rules is not significant, except for determining the default goal: the target for make to consider, if you do not otherwise specify one. The default goal is the target of the first rule in the first makefile. If the first rule has multiple targets, only the first target is taken as the default.
.DEFAULT_GOAL
Sets the default goal to be used if no targets were specified on the command line.
The .DEFAULT_GOAL variable allows you to discover the current default goal, restart the default goal selection algorithm by clearing its value, or to explicitly set the default goal.
The following examples illustrates these cases:
A rule appears in the makefile and says when and how to remake certain files, called the rule’s targets (most often only one per rule). It lists the other files that are the prerequisites of the target, and the recipe to use to create or update the target.
The order of rules is not significant, except for determining the default goal: the target for make to consider, if you do not otherwise specify one. The default goal is the target of the first rule in the first makefile. If the first rule has multiple targets, only the first target is taken as the default.
.DEFAULT_GOAL
Sets the default goal to be used if no targets were specified on the command line.
The .DEFAULT_GOAL variable allows you to discover the current default goal, restart the default goal selection algorithm by clearing its value, or to explicitly set the default goal.
The following examples illustrates these cases:
Note that assigning more than one target name to .DEFAULT_GOAL is invalid and will result in an error.
Phony Targets:
A phony target is one that is not really the name of a file; rather it is just a name for a recipe to be executed when you make an explicit request. There are two reasons to use a phony target:
1) To avoid a conflict with a file of the same name, and
2) To improve performance.
If you write a rule whose recipe will not create the target file, the recipe will be executed every time the target comes up for remaking. Here is an example:
clean:
rm *.o temp
Because the rm command does not create a file named clean, probably no such file will ever exist. Therefore, the rm command will be executed every time you say ‘make clean’.
In this example, the clean target will not work properly if a file named clean is ever created in this directory. Since it has no prerequisites, clean would always be considered up to date and its recipe would not be executed. To avoid this problem you can explicitly declare the target to be phony by making it a prerequisite of the special target .PHONY
For example:
.PHONY: clean
clean:
rm *.o temp
If you write a rule whose recipe will not create the target file, the recipe will be executed every time the target comes up for remaking. Here is an example:
clean:
rm *.o temp
Because the rm command does not create a file named clean, probably no such file will ever exist. Therefore, the rm command will be executed every time you say ‘make clean’.
In this example, the clean target will not work properly if a file named clean is ever created in this directory. Since it has no prerequisites, clean would always be considered up to date and its recipe would not be executed. To avoid this problem you can explicitly declare the target to be phony by making it a prerequisite of the special target .PHONY
For example:
.PHONY: clean
clean:
rm *.o temp
Once this is done, ‘make clean’ will run the recipe regardless of whether there is a file named clean.
Phony targets are also useful in conjunction with recursive invocations of make.
Rules without Recipes or Prerequisites:
If a rule has no prerequisites or recipe, and the target of the rule is a nonexistent file, then make imagines this target to have been updated whenever its rule is run. This implies that all targets depending on this one will always have their recipe run.
An example will illustrate this:
clean: FORCE
rm $(objects)
FORCE:
Here the target ‘FORCE’ satisfies the special conditions, so the target clean that depends on it is forced to run its recipe. There is nothing special about the name ‘FORCE’, but that is one name commonly used this way.
As you can see, using ‘FORCE’ this way has the same results as using ‘.PHONY: clean’. Using ‘.PHONY’ is more explicit and more efficient. However, other versions of make do not support ‘.PHONY’; thus ‘FORCE’ appears in many makefiles.
Using Variables in Recipes:
To substitute a variable’s value, write a dollar sign followed by the name of the variable in parentheses or braces: either ‘$(foo)’ or ‘${foo}’ is a valid reference to the variable foo.
if you want a dollar sign to appear in your recipe, you must double it (‘$$’). For shells like the default shell, that use dollar signs to introduce variables, it’s important to keep clear in your mind whether the variable you want to reference is a make variable (use a single dollar sign) or a shell variable (use two dollar signs).
For example:
LIST = one two three
print:
for i in $(LIST); do \
echo $$i; \
done
Phony targets are also useful in conjunction with recursive invocations of make.
Rules without Recipes or Prerequisites:
If a rule has no prerequisites or recipe, and the target of the rule is a nonexistent file, then make imagines this target to have been updated whenever its rule is run. This implies that all targets depending on this one will always have their recipe run.
An example will illustrate this:
clean: FORCE
rm $(objects)
FORCE:
Here the target ‘FORCE’ satisfies the special conditions, so the target clean that depends on it is forced to run its recipe. There is nothing special about the name ‘FORCE’, but that is one name commonly used this way.
As you can see, using ‘FORCE’ this way has the same results as using ‘.PHONY: clean’. Using ‘.PHONY’ is more explicit and more efficient. However, other versions of make do not support ‘.PHONY’; thus ‘FORCE’ appears in many makefiles.
Using Variables in Recipes:
To substitute a variable’s value, write a dollar sign followed by the name of the variable in parentheses or braces: either ‘$(foo)’ or ‘${foo}’ is a valid reference to the variable foo.
if you want a dollar sign to appear in your recipe, you must double it (‘$$’). For shells like the default shell, that use dollar signs to introduce variables, it’s important to keep clear in your mind whether the variable you want to reference is a make variable (use a single dollar sign) or a shell variable (use two dollar signs).
For example:
LIST = one two three
print:
for i in $(LIST); do \
echo $$i; \
done
#Command:
# make print
#Output:
# one
# two
# three
# two
# three
Variable references work by strict textual substitution.
Environment variables can be accessed by using both $ (a single dollar sign) and $$ (two dollar signs).
For example:
.PHONY: display
display:
echo $(HOME)
echo $$HOME
Environment variables can be accessed by using both $ (a single dollar sign) and $$ (two dollar signs).
For example:
.PHONY: display
display:
echo $(HOME)
echo $$HOME
#Command:
# make display
#Output:
# /home/sagar.shah
# /home/sagar.shah
Generally in most of the shells HOME is predefined environment variable.
But if make file also has (locally defined) variable with same name then locally defined value has high priority.
HOME = /home/sagar.shah/docs
.PHONY: display_home
display_home:
echo $(HOME)
echo $$HOME
.PHONY: display_home
display_home:
echo $(HOME)
echo $$HOME
#Command:
# make display_home
#Output:
# /home/sagar.shah/docs
# /home/sagar.shah/docs
Automatic Variables:
$@
The file name of the target of the rule. If the target is an archive member, then ‘$@’ is the name of the archive file. In a pattern rule that has multiple targets, ‘$@’ is the name of whichever target caused the rule’s recipe to be run.
$%
The target member name, when the target is an archive member. For example, if the target is foo.a(bar.o) then ‘$%’ is bar.o and ‘$@’ is foo.a.
‘$%’ is empty when the target is not an archive member.
$<
The name of the first prerequisite.
If the target got its recipe from an implicit rule, this will be the first prerequisite added by the implicit rule.
$?
The names of all the prerequisites that are newer than the target, with spaces between them.
For prerequisites which are archive members, only the named member is used.
$^
The names of all the prerequisites, with spaces between them.
For prerequisites which are archive members, only the named member is used.
A target has only one prerequisite on each other file it depends on, no matter how many times each file is listed as a prerequisite. So if you list a prerequisite more than once for a target, the value of $^ contains just one copy of the name.
This list does not contain any of the order-only prerequisites; for those see the ‘$|’ variable, below.
$+
This is like ‘$^’, but prerequisites listed more than once are duplicated in the order they were listed in the makefile. This is primarily useful for use in linking commands where it is meaningful to repeat library file names in a particular order.
$|
The names of all the order-only prerequisites, with spaces between them.
S(MAKE)
The value of this variable is the file name of Makefile.
For Example:
.PHONY:create_dir
create_dir:
mkdir -p dir
.PHONY:create
create:
touch file.txt
$(MAKE) create_dir
$(MAKE) create_dir
#Command:
# make create
#Output:
# Directory named "dir" and File named "file.txt" will be created
Multiple Targets in a Rule:
A rule with multiple targets is equivalent to writing many rules, each with one target, and all identical aside from that. The same recipe applies to all the targets, but its effect may vary because you can substitute the actual target name into the recipe using ‘$@’. The rule contributes the same prerequisites to all the targets also.
For Example:
bigoutput littleoutput : text.g
generate text.g -$(subst output,,$@) > $@
is equivalent to
bigoutput : text.g
generate text.g -big > bigoutput
littleoutput : text.g
generate text.g -little > littleoutput
Conditional Parts of Makefiles:
A conditional directive causes part of a makefile to be obeyed or ignored depending on the values of variables. Conditionals can compare the value of one variable to another, or the value of a variable to a constant string.
This conditional uses three directives: one ifeq, one else and one endif.
The ifeq directive begins the conditional, and specifies the condition. It contains two arguments, separated by a comma and surrounded by parentheses. Variable substitution is performed on both arguments and then they are compared. The lines of the makefile following the ifeq are obeyed if the two arguments match; otherwise they are ignored.
The else directive causes the following lines to be obeyed if the previous conditional failed. In the example above, this means that the second alternative linking command is used whenever the first alternative is not used. It is optional to have an else in a conditional.
The endif directive ends the conditional. Every conditional must end with an endif.
The syntax of a simple conditional with no else is as follows:
conditional-directive
text-if-true
endif
The text-if-true may be any lines of text, to be considered as part of the makefile if the condition is true. If the condition is false, no text is used instead.
The syntax of a complex conditional is as follows:
conditional-directive
text-if-true
else
text-if-false
endif
or:
conditional-directive-one
text-if-one-is-true
else conditional-directive-two
text-if-two-is-true
else
text-if-one-and-two-are-false
endif
There can be as many “else conditional-directive” clauses as necessary. Once a given condition is true, text-if-true is used and no other clause is used; if no condition is true then text-if-false is used. The text-if-true and text-if-false can be any number of lines of text.
The syntax of the conditional-directive is the same whether the conditional is simple or complex; after an else or not.
There are four different directives that test different conditions.
ifeq (arg1, arg2)
ifeq ’arg1’ ’arg2’
ifeq "arg1" "arg2"
ifeq "arg1" ’arg2’
ifeq ’arg1’ "arg2"
Expand all variable references in arg1 and arg2 and compare them. If they are identical, the text-if-true is effective; otherwise, the text-if-false, if any, is effective.
Often you want to test if a variable has a non-empty value. When the value results from complex expansions of variables and functions, expansions you would consider empty may actually contain white-space characters and thus are not seen as empty.
However, you can use the strip function to avoid interpreting white-space as a non-empty value.
For example:
ifeq ($(strip $(foo)),)
text-if-empty
endif
will evaluate text-if-empty even if the expansion of $(foo) contains white-space characters.
ifneq (arg1, arg2)
ifneq ’arg1’ ’arg2’
ifneq "arg1" "arg2"
ifneq "arg1" ’arg2’
ifneq ’arg1’ "arg2"
Expand all variable references in arg1 and arg2 and compare them. If they are different, the text-if-true is effective; otherwise, the text-if-false, if any, is effective.
ifdef variable-name
The ifdef form takes the name of a variable as its argument, not a reference to a variable. The value of that variable has a non-empty value, the text-if-true is effective; otherwise, the text-if-false, if any, is effective.
Variables that have never been defined have an empty value.
The text variable-name is expanded, so it could be a variable or function that expands to the name of a variable.
For example:
bar = true
foo = bar
ifdef $(foo)
The syntax of the conditional-directive is the same whether the conditional is simple or complex; after an else or not.
There are four different directives that test different conditions.
ifeq (arg1, arg2)
ifeq ’arg1’ ’arg2’
ifeq "arg1" "arg2"
ifeq "arg1" ’arg2’
ifeq ’arg1’ "arg2"
Expand all variable references in arg1 and arg2 and compare them. If they are identical, the text-if-true is effective; otherwise, the text-if-false, if any, is effective.
Often you want to test if a variable has a non-empty value. When the value results from complex expansions of variables and functions, expansions you would consider empty may actually contain white-space characters and thus are not seen as empty.
However, you can use the strip function to avoid interpreting white-space as a non-empty value.
For example:
ifeq ($(strip $(foo)),)
text-if-empty
endif
will evaluate text-if-empty even if the expansion of $(foo) contains white-space characters.
ifneq (arg1, arg2)
ifneq ’arg1’ ’arg2’
ifneq "arg1" "arg2"
ifneq "arg1" ’arg2’
ifneq ’arg1’ "arg2"
Expand all variable references in arg1 and arg2 and compare them. If they are different, the text-if-true is effective; otherwise, the text-if-false, if any, is effective.
ifdef variable-name
The ifdef form takes the name of a variable as its argument, not a reference to a variable. The value of that variable has a non-empty value, the text-if-true is effective; otherwise, the text-if-false, if any, is effective.
Variables that have never been defined have an empty value.
The text variable-name is expanded, so it could be a variable or function that expands to the name of a variable.
For example:
bar = true
foo = bar
ifdef $(foo)
frobozz = yes
endif
The variable reference $(foo) is expanded, yielding bar, which is considered to be the name of a variable. The variable bar is not expanded, but its value is examined to determine if it is non-empty.
Note that ifdef only tests whether a variable has a value. It does not expand the variable to see if that value is nonempty.
Consequently, tests using ifdef return true for all definitions except those like foo =.
To test for an empty value, use ifeq ($(foo),).
For example,
bar =
foo = $(bar)
ifdef foo
frobozz = yes
else
frobozz = no
endif
sets ‘frobozz’ to ‘yes’,
while:
foo =
ifdef foo
frobozz = yes
else
frobozz = no
endif
sets ‘frobozz’ to ‘no’.
ifndef variable-name
If the variable variable-name has an empty value, the text-if-true is effective; otherwise, the text-if-false, if any, is effective. The rules for expansion and testing of variable-name are identical to the ifdef directive.
The other two directives that play a part in a conditional are else and endif. Each of these directives is written as one word, with no arguments. Extra spaces are allowed and ignored at the beginning of the line, and spaces or tabs at the end. Extra spaces are allowed and ignored at the beginning of the conditional directive line, but a tab is not allowed. (If the line begins with a tab, it will be considered part of a recipe for a rule.)
A comment starting with ‘#’ may appear at the end of the line.
Makefile variable assignment types:
Basically there are four types of assignment:
1) = for creating recursive variables.
Values within variables are recursively expanded when the variable is used, not when it's declared.
2) := for creating simple variables.
Setting of a variable with simple expansion of the values inside - values within it are expanded at declaration time.
3) ?= for conditional assignment.
Setting of a variable only if it doesn't have a value
4) += for appending text to a variable.
Appending the supplied value to the existing value (or setting to that value if the variable didn't exist), important feature when recursive variables are used.
Recipe Echoing:
Normally make prints each line of the recipe before it is executed. We call this echoing because it gives the appearance that you are typing the lines yourself.
When a line starts with ‘@’, the echoing of that line is suppressed. The ‘@’ is discarded before the line is passed to the shell. Typically you would use this for a command whose only effect is to print something, such as an echo command to indicate progress through the makefile:
@echo About to make distribution files
When make is given the flag ‘-n’ or ‘--just-print’ it only echoes most recipes, without executing them. In this case even the recipe lines starting with ‘@’ are printed. This flag is useful for finding out which recipes make thinks are necessary without actually doing them.
The ‘-s’ or ‘--silent’ flag to make prevents all echoing, as if all recipes started with ‘@’. A rule in the makefile for the special target .SILENT has the same effect.
Errors in Recipes:
After each shell invocation returns, make looks at its exit status. If the shell completed successfully (the exit status is zero), the next line in the recipe is executed in a new shell; after the last line is finished, the rule is finished.
If there is an error (the exit status is nonzero), make gives up on the current rule, and perhaps on all rules. Sometimes the failure of a certain recipe line does not indicate a problem.
For example,
you may use the mkdir command to ensure that a directory exists. If the directory already exists, mkdir will report an error, but you probably want make to continue regardless.
To ignore errors in a recipe line, write a ‘-’ at the beginning of the line’s text (after the initial tab). The ‘-’ is discarded before the line is passed to the shell for execution.
For example,
clean:
-rm -f *.o
This causes make to continue even if rm is unable to remove a file.
When you run make with the ‘-i’ or ‘--ignore-errors’ flag, errors are ignored in all recipes of all rules. A rule in the makefile for the special target .IGNORE has the same effect.
Table of all the options make understands:
‘-i’
‘--ignore-errors’
Ignore all errors in recipes executed to remake files.
‘-n’
‘--just-print’
‘--dry-run’
‘--recon’
Print the recipe that would be executed, but do not execute it (except in certain circumstances)
‘-q’
‘--question’
“Question mode”
endif
The variable reference $(foo) is expanded, yielding bar, which is considered to be the name of a variable. The variable bar is not expanded, but its value is examined to determine if it is non-empty.
Note that ifdef only tests whether a variable has a value. It does not expand the variable to see if that value is nonempty.
Consequently, tests using ifdef return true for all definitions except those like foo =.
To test for an empty value, use ifeq ($(foo),).
For example,
bar =
foo = $(bar)
ifdef foo
frobozz = yes
else
frobozz = no
endif
sets ‘frobozz’ to ‘yes’,
while:
foo =
ifdef foo
frobozz = yes
else
frobozz = no
endif
sets ‘frobozz’ to ‘no’.
ifndef variable-name
If the variable variable-name has an empty value, the text-if-true is effective; otherwise, the text-if-false, if any, is effective. The rules for expansion and testing of variable-name are identical to the ifdef directive.
The other two directives that play a part in a conditional are else and endif. Each of these directives is written as one word, with no arguments. Extra spaces are allowed and ignored at the beginning of the line, and spaces or tabs at the end. Extra spaces are allowed and ignored at the beginning of the conditional directive line, but a tab is not allowed. (If the line begins with a tab, it will be considered part of a recipe for a rule.)
A comment starting with ‘#’ may appear at the end of the line.
Makefile variable assignment types:
Basically there are four types of assignment:
1) = for creating recursive variables.
Values within variables are recursively expanded when the variable is used, not when it's declared.
2) := for creating simple variables.
Setting of a variable with simple expansion of the values inside - values within it are expanded at declaration time.
3) ?= for conditional assignment.
Setting of a variable only if it doesn't have a value
4) += for appending text to a variable.
Appending the supplied value to the existing value (or setting to that value if the variable didn't exist), important feature when recursive variables are used.
Recipe Echoing:
Normally make prints each line of the recipe before it is executed. We call this echoing because it gives the appearance that you are typing the lines yourself.
When a line starts with ‘@’, the echoing of that line is suppressed. The ‘@’ is discarded before the line is passed to the shell. Typically you would use this for a command whose only effect is to print something, such as an echo command to indicate progress through the makefile:
@echo About to make distribution files
When make is given the flag ‘-n’ or ‘--just-print’ it only echoes most recipes, without executing them. In this case even the recipe lines starting with ‘@’ are printed. This flag is useful for finding out which recipes make thinks are necessary without actually doing them.
The ‘-s’ or ‘--silent’ flag to make prevents all echoing, as if all recipes started with ‘@’. A rule in the makefile for the special target .SILENT has the same effect.
Errors in Recipes:
After each shell invocation returns, make looks at its exit status. If the shell completed successfully (the exit status is zero), the next line in the recipe is executed in a new shell; after the last line is finished, the rule is finished.
If there is an error (the exit status is nonzero), make gives up on the current rule, and perhaps on all rules. Sometimes the failure of a certain recipe line does not indicate a problem.
For example,
you may use the mkdir command to ensure that a directory exists. If the directory already exists, mkdir will report an error, but you probably want make to continue regardless.
To ignore errors in a recipe line, write a ‘-’ at the beginning of the line’s text (after the initial tab). The ‘-’ is discarded before the line is passed to the shell for execution.
For example,
clean:
-rm -f *.o
This causes make to continue even if rm is unable to remove a file.
When you run make with the ‘-i’ or ‘--ignore-errors’ flag, errors are ignored in all recipes of all rules. A rule in the makefile for the special target .IGNORE has the same effect.
Table of all the options make understands:
‘-i’
‘--ignore-errors’
Ignore all errors in recipes executed to remake files.
‘-n’
‘--just-print’
‘--dry-run’
‘--recon’
Print the recipe that would be executed, but do not execute it (except in certain circumstances)
‘-q’
‘--question’
“Question mode”
Do not run any recipes, or print anything; just return an exit status that is zero if the specified targets are already up to date, one if any remaking is required, or two if an error is encountered.
‘-s’
‘--silent’
‘--quiet’
Silent operation; do not print the recipes as they are executed.
‘-v’
‘--version’
Print the version of the make program plus a copyright, a list of authors, and a notice that there is no warranty; then exit.
‘-t’
‘--touch’
Marks targets as up to date without actually running any recipes.
Touch files (mark them up to date without really changing them) instead of running their recipes. This is used to pretend that the recipes were done, in order to fool future invocations of make.
‘-s’
‘--silent’
‘--quiet’
Silent operation; do not print the recipes as they are executed.
‘-v’
‘--version’
Print the version of the make program plus a copyright, a list of authors, and a notice that there is no warranty; then exit.
‘-t’
‘--touch’
Marks targets as up to date without actually running any recipes.
Touch files (mark them up to date without really changing them) instead of running their recipes. This is used to pretend that the recipes were done, in order to fool future invocations of make.
Tuesday, 6 October 2015
UVM Callback with example
Callback mechanism is used for altering the behavior of the transactor(also called BFM) without modifying the transactor. One of the many promises of Object-Oriented programming is that it will allow for plug-and-play re-usable verification components.
Verification Engineers will hook the transactors together to make a verification environment. In SystemVerilog, this hooking together of transactors can be tricky. Callbacks provide a mechanism whereby independently developed objects may be connected together in simple steps.
Some requirements are often unpredictable when the transactor is first written. So a transactor should provide some kind of hooks for executing the code which is defined afterwards. In uvm, these hooks are created using callback methods.
For instance, a driver is developed and an empty method is called before driving the transaction to the DUT. Initially this empty method does nothing. As the implementation goes, user may realize that he needs to print the state of the transaction or to delay the transaction driving to DUT or inject an error into transaction. Callback mechanism allows executing the user defined code in place of the empty callback method without changing the behavior of the transactor(BFM).
Sometimes we are using third party VIP in our verification environment. Third party wants their code to be encrypted and also wants to give facility to their customer to change the behavior of driver(BFM) without allowing to modify driver code. In this case they provide hooks at some particular places like just after getting transaction from sequence (after get_next_item), just before start driving to the interface, etc. These hooks are created using callback.
Other example of callback usage is in monitor. Callbacks can be used in a monitor for collecting coverage information or for hooking up to scoreboard to pass transactions for self checking. With this, user is able to control the behavior of the transactor in verification environment and individual testcases without doing any modifications to the transactor itself.
Let's go through one callback example in which error is injected through callback of driver.
Verification Engineers will hook the transactors together to make a verification environment. In SystemVerilog, this hooking together of transactors can be tricky. Callbacks provide a mechanism whereby independently developed objects may be connected together in simple steps.
Some requirements are often unpredictable when the transactor is first written. So a transactor should provide some kind of hooks for executing the code which is defined afterwards. In uvm, these hooks are created using callback methods.
For instance, a driver is developed and an empty method is called before driving the transaction to the DUT. Initially this empty method does nothing. As the implementation goes, user may realize that he needs to print the state of the transaction or to delay the transaction driving to DUT or inject an error into transaction. Callback mechanism allows executing the user defined code in place of the empty callback method without changing the behavior of the transactor(BFM).
Sometimes we are using third party VIP in our verification environment. Third party wants their code to be encrypted and also wants to give facility to their customer to change the behavior of driver(BFM) without allowing to modify driver code. In this case they provide hooks at some particular places like just after getting transaction from sequence (after get_next_item), just before start driving to the interface, etc. These hooks are created using callback.
Other example of callback usage is in monitor. Callbacks can be used in a monitor for collecting coverage information or for hooking up to scoreboard to pass transactions for self checking. With this, user is able to control the behavior of the transactor in verification environment and individual testcases without doing any modifications to the transactor itself.
Let's go through one callback example in which error is injected through callback of driver.
Saturday, 3 October 2015
UVM Driver and Sequencer Handshaking
In UVM, there is a mechanism to be followed when we want to send the transactions from the sequencer to the Driver in order to provide stimulus to the DUT.
The transfer of request and response sequence items between sequences and their target driver is facilitated by a TLM communication mechanism implemented in the sequencer.
The transfer of request and response sequence items between sequences and their target driver is facilitated by a TLM communication mechanism implemented in the sequencer.
Sending a sequence_item to a driver:
To send a sequence_item to a driver there are four steps that need to occur:
Step 1 - Creation
The sequence_item is derived from uvm_object and should be created via the factory:
Using the factory creation method allows the sequence_item to be overridden with a sequence_item of a derived type if required.
Step 2 - Ready - start_item()
The start_item() call is made, passing the sequence_item handle as an argument. This call blocks until the sequencer grants the sequence and the sequence_item access to the driver.
Step 3 - Set
The sequence_item is prepared for use, usually through randomization, but it may also be initialised by setting properties directly.
Step 4 - Go - finish_item()
The finish_item() call is made, which blocks until the driver has completed its side of the transfer protocol for the item. No simulation time should be consumed between start_item() and finish_item().
Step 5 - Response - get_response()
This step is optional, and is only used if the driver sends a response to indicate to indicate that it has completed transaction associated with the sequence_item. The get_response() call blocks until a response item is available from the sequencers response FIFO.
Step 1 - Creation
The sequence_item is derived from uvm_object and should be created via the factory:
Using the factory creation method allows the sequence_item to be overridden with a sequence_item of a derived type if required.
Step 2 - Ready - start_item()
The start_item() call is made, passing the sequence_item handle as an argument. This call blocks until the sequencer grants the sequence and the sequence_item access to the driver.
Step 3 - Set
The sequence_item is prepared for use, usually through randomization, but it may also be initialised by setting properties directly.
Step 4 - Go - finish_item()
The finish_item() call is made, which blocks until the driver has completed its side of the transfer protocol for the item. No simulation time should be consumed between start_item() and finish_item().
Step 5 - Response - get_response()
This step is optional, and is only used if the driver sends a response to indicate to indicate that it has completed transaction associated with the sequence_item. The get_response() call blocks until a response item is available from the sequencers response FIFO.
Late Randomization
In the sequence_item flow above, steps 2 and 3 could be done in any order. However, leaving the randomization of the sequence_item until just before the finish_item() method call has the potential to allow the sequence_item to be randomized according to conditions true at the time of generation. This is sometimes referred to as late randomization.
The alternative approach is to generate the sequence_item before the start_item() call, in this case the item is generated before it is necessarily clearhow it is going to be used.
The alternative approach is to generate the sequence_item before the start_item() call, in this case the item is generated before it is necessarily clearhow it is going to be used.
In previous generation verification methodologies, such as Specman and the AVM, generation was done at the beginning of the simulation and a stream of pre-prepared sequence_items was sent across to the driver. With late randomization, sequence_items are generated just in time and on demand.
Driver side Operation:
Multiple APIs used by driver code to interact with the sequencer. First go through these APIs,
get_next_item()
This method blocks until a REQ sequence_item is available in the sequencers request FIFO and then returns with a pointer to the REQ object.
The get_next_item() call implements half of the driver-sequencer protocol handshake, and it must be followed by an item_done() call which completes the handshake. Making another get_next_item() call before issuing an item_done() call will result in a protocol error and driver-sequencer deadlock.
try_next_item()
This is a non-blocking variant of the get_next_item() method. It will return a null pointer if there is no REQ sequence_item available in the sequencers request FIFO. However, if there is a REQ sequence_item available it will complete the first half of the driver-sequencer handshake and must be followed by an item_done() call to complete the handshake.
item_done()
The non-blocking item_done() method completes the driver-sequencer handshake and it should be called after a get_next_item() or a successful try_next_item() call.
This method blocks until a REQ sequence_item is available in the sequencers request FIFO and then returns with a pointer to the REQ object.
The get_next_item() call implements half of the driver-sequencer protocol handshake, and it must be followed by an item_done() call which completes the handshake. Making another get_next_item() call before issuing an item_done() call will result in a protocol error and driver-sequencer deadlock.
try_next_item()
This is a non-blocking variant of the get_next_item() method. It will return a null pointer if there is no REQ sequence_item available in the sequencers request FIFO. However, if there is a REQ sequence_item available it will complete the first half of the driver-sequencer handshake and must be followed by an item_done() call to complete the handshake.
item_done()
The non-blocking item_done() method completes the driver-sequencer handshake and it should be called after a get_next_item() or a successful try_next_item() call.
If it is passed no argument or a null pointer it will complete the handshake without placing anything in the sequencer's response FIFO. If it is passed a pointer to a RSP sequence_item as an argument, then that pointer will be placed in the sequencer's response FIFO.
The get_next_item() method initiate the sequencer arbitration process, which results in a sequence_item being returned from the active sequence which has selected. This means that the driver is effectively pulling sequence_items from the active sequences as it needs them.
When the handle to a sequence_item is passed as an argument to
the finish_item() method the drivers get_next_item() method call completes with a pointer to the same sequence_item. When the driver makes any changes to the sequence_item it is really updating the object inside the sequence. The drivers call to item_done() unblocks the finish_item() call in the sequence and then the sequence can access the fields in the
sequence_item, including those which the driver may have updated as part of the response side of the pin level transaction.
The get_next_item() method initiate the sequencer arbitration process, which results in a sequence_item being returned from the active sequence which has selected. This means that the driver is effectively pulling sequence_items from the active sequences as it needs them.
When the handle to a sequence_item is passed as an argument to
the finish_item() method the drivers get_next_item() method call completes with a pointer to the same sequence_item. When the driver makes any changes to the sequence_item it is really updating the object inside the sequence. The drivers call to item_done() unblocks the finish_item() call in the sequence and then the sequence can access the fields in the
sequence_item, including those which the driver may have updated as part of the response side of the pin level transaction.
The connection between a driver and a sequencer is typically made in the connect_phase() method of an agent. With the standard UVM driver and sequencer base classes, the TLM connection between a driver and sequencer is a one to one connection - multiple drivers are not connected to a sequencer, nor are multiple sequencers connected to a driver.
uvm_sequence and uvm_driver have predefined sequence_item for both request and response.
So, you can directly use predefined variables for sequence_item,
uvm_sequence and uvm_driver have predefined sequence_item for both request and response.
So, you can directly use predefined variables for sequence_item,
Subscribe to:
Posts (Atom)