This is an example on how to add a command in Python, as it would appear in the commands.py file of a module's source code subdirectory:
# this line imports definitions needed below from cli import * # this is my Python function that will be called when the # command is invoked from the Simics prompt. def my_command_fun(int_arg, str_arg, flag_arg): print "My integer: %d" % int_arg print "My string: %s" % str_arg print "Flag is", if flag_arg: print "given" else: print "not given" return int_arg # register our new command new_command("my-command", my_command_fun, args = [arg(int_t, "arg", "?", 10), arg(str_t, "name"), arg(flag_t, "-f")], alias = "mc", type = "my-module-commands", short = "my command does it", doc_items = [("NOTE", "This command is best")], see_also = ["my_other_command"], doc = """ <b>my-command</b> is best. This is its documentation. <i>arg</i> is the first argument...""")
The code above will define a command called my-command. When the command is invoked from the Simics command line interface, the function my_command_fun() will be called. The function must take exactly as many arguments as the command; in this case 3: an integer, a string, and a flag. The first command argument is called "arg" and is optional, indicated by "?". If omitted by the user, the command function will be called with the default value, 10. The second argument is called "name" and is required (since there is no "?", there is no default value). The last argument is a flag and will have a value of 1 if the flag is given, otherwise 0. Flags are thus always optional.
If the command function returns a value (a string or an integer) Simics will print this value on the terminal or pass it on to other commands as input arguments, e.g., print -x (my-command 15 foo).
Writing help my-command at the Simics prompt will display:
NAME my-command - my command does it SYNOPSIS my-command [arg] name [-f] ALIAS mc DESCRIPTION my-command is best. This is its documentation. arg is the first argument. NOTE This command is best SEE ALSO my_other_command
The command can be invoked in different ways, here are some examples:
simics> my-command 2 foo -f My integer: 2 My string: foo Flag is given 2 simics> my-command bar My integer: 10 My string: bar Flag is not given 10 simics> my-command 20 -f Argument error: argument number 2 is missing in 'my-command', string expected. SYNOPSIS: my-command [arg] name [-f] simics> print -x (mc -f name="hello there!" arg = 23) My integer: 23 My string: hello there! Flag is given 0x17
In the last case the alias is used and the command is passed to the print command that outputs the value in hexadecimal notation.
All the parameters of the new_command() function will be explained bellow. After the parameter name follows the type of the parameter and if the argument is required, recommended, or optional:
To create an argument list of an integer and a string, use:
..., args = [arg(int_t), arg(str_t)], ...
It is, however, recommended that names for the parameters are specified. This is done as a second argument for arg():
..., args = [arg(int_t, "value"), arg(str_t, "file")], ...
This way the documentation of the argument list (help command) will use these names and also makes it possible to enter the argument in any order at the command line, e.g.,
command file = "/tmp/bar" value = 17
The flag type requires the name to be specified and the name must begin with a hyphen, e.g., "–all". The corresponding value passed to the command handler function will be 1 if the flag is given on the command line or 0 otherwise.
The addr type can be used for addresses. It understands argument of the form p:0xcf00 (physical address), v:0xff00 (virtual address), or 0xffdc at the command line. The command handler function will receive a tuple of the prefix and the address, e.g., ("v", 0xcff0). If only the address is given, "v" will be used.
Sometimes it is convenient to have other arguments than flags optional. To indicate this, add "?" as the third argument to arg(), and the default value as the fourth; e.g.,
..., args = [arg(int_t, "value", "?", 1), arg(str_t, "file")], ...makes value an optional argument with 1 as its default value.
The arg function takes more parameters than those already mentioned:
..., args = [arg(str_t, "cpu", expander = exp_fun, is_a = test_fun)], ...
will connect an argument completion (expander) function for the cpu arg, i.e., the function exp_fun() will be called when the user presses the TAB key while entering the argument value. The expander function takes an argument representing the text the user has written for the argument. For example, if the user presses TAB after typing command cpu = ultr, the exp_fun() will be passed "ultr" and should return a list of strings completing "ultr". Here is an example of an expander function:
def exp_fun(comp): return get_completions(comp, ["ultraI", "ultraII", "ultraIII", "pentium"])
When called with "ultr", it will return ["ultraI", "ultraII", "ultraIII"]. The get_completions function filters the list and keep elements with prefix comp. The expander functions only works for the string type for the moment.
The is_a argument takes a function that tests if the argument is valid. It should return 1 for valid values and 0 otherwise. For example the read-reg command has as first argument a CPU (optional) and as second argument a register. If a user types read-reg g7, CLI will incorrectly interpret g7 as a CPU since g7 is a valid string and the command will fail. The is_a function could therefore return 0 for non-CPUs and pass the string to the next argument, which will match since g7 is a register.
A command argument can be of multiple types as well (polyvalues). For example,
..., args = [ arg((str_t, int_t, flag_t), ("cpu","value","-all"), "?", (int_t, 0, "value"), expander = (exp1, exp2, None)) ], ...
will create an argument that is either a string, an integer, or a flag. The argument passed to the command handler function is a tuple specifying the arg-type, the value, and the name of the argument. E.g., command foo will pass (str_t, "foo", "cpu") to the command handler function. This is why the default value looks the way it does. The corresponding expander function will also be used. command cpu = abc<tab> will use the exp1 expander.
The full name of a namespace command will be <class_or_interface>.command (angle brackets included) and this is what is printed when command are listed, but as stated above the command is invoked with object.command.
Device commands are typically implemented using namespace commands. This is because they usually operate on a single configuration object, e.g., a MMU or cache object.