• No results found

Interface Declarations

3.1 Modules and Interface Declarations

3.1.1 Interface Declarations

Besides the name, each module has an interface that defines the input and output data streams where values are read from and values are written to in each reaction step. The computation of the output values and the next internal state is determined by the body statement that is considered in the next section. The body statement must therefore know the names, types and further information on the input and output variables. This information is provided by the declaration of variables in the module’s interface.

The interface consists of the declaration of the variables that hold in each reaction step the values that are read from input streams and that are written to output streams. In addition to the names, the interface declaration provides also for all variables the types, data flow directions, and storage classes. The available types have already been described in detail in the previous chapter, so that we only have to add how types are used in the declaration.

Concerning the dataflow direction, variables declared in the interface are classified into (uncontrollable) inputs, controllable inputs, and outputs of the module. To this end, the names of the variables are possibly prefixed as fol- lows:

• Uncontrollable input variables are not prefixed. • Controllable input variables are prefixed with ?. • Output variables are prefixed with &.

The body statement of the module can only determine the value of out- put variables in each reaction step. The values of input variables, regardless whether they are controllable or not, are determined by the environment. The difference between controllable and uncontrollable variables is that control- lable variables are viewed to be under control of the system, but not by the current module, whether uncontrollable variables are neither under control of the system nor by the module. The code generation and the semantics of mod- ules and statements does not distinguish between controllable and uncontrol- lable variables. This distinction is used for applications of supervisory control, where it can be checked whether for all uncontrollable input streams, there are controllable input streams and output streams such that a given specifica- tion holds. This is important for debugging where these questions can help to determine a modulo that can be changed so that a desired specification will finally hold. Supervisory control is a formal method that is a generalization of formal verification.

Finally, declared variables have either one of the storage classes event or

memorized, which is not relevant for input variables. The storage class deter-

mines the semantics of the generated output data streams as follows: Mem- orized output variables store their current value unless it is changed by an action of the module. Event output variables do not store their current value on their own, and instead are reset to a default value if the module does not provide a new value in the considered reaction step. For this reason, one may view input variables having by default the event storage class.

The classification of variables into event or memorized ones is very impor- tant and motivated by hardware circuits: outputs of combinational gates are not memorized and therefore, these can be modeled with output variables of event storage class. In contrast, outputs of registers are memorized and there- fore correspond with the memorized storage class. Programming languages used for the implementation of software do only know memorized variables whose default use is for hardware circuit synthesis however too expensive. The same holds for their use in formal verification methods like model check- ing, where memorized output variables lead to state variables of the consid- ered automaton or Kripke structure, while event output variables may be sim- ply defined in terms of the state variables. The compiler can easily determine whether it is possible to define an event variable in terms of other variables as will be explained in Chapter6.

Behavioral Modules

The syntax of the type declarations follows the style that is used in the C programming language. A general template for a module is as follows, where the implements ModuleCall is optional:

module Name(decl1,...,decln)

implements ModuleCall { BodyStmt

}

In the above template, Name is the name of the module and BodyStmt is the statement of the module that determines its behavior. The Name of a module may be any identifier that can also be used as a variable as explained in the previous chapter, but names of variables and modules have to be distinct. Each decli is the declaration of one or more variables. These declarations have in

turn the following syntax:

StorageClass type name1[n1] . . . [nd1], . . . , namem[n1] . . . [ndm],

StorageClassis thereby either event or empty which means that the variable’s storage class is either event or memorized. The type type is specified next with the syntax we have already described in the previous chapter with that for an array type array(α,n) only the base type α is written at this place. The

remaining declaration consists of a comma-separated list of prefixed names of the declared variables. As already mentioned above, the names of controllable input variables and output variables have to be prefixed with the symbols ? and &, respectively. For array types, the name of the variable is suffixed with the array dimensions embraced with square brackets.

In the frequent case event bool for the declaration of so-called pure sig- nals, i.e. variables with storage class event of type bool, it is allowed to omit the type bool, so that event is an abbreviation of event bool. As an example, consider the following start of a module:

module M1(int<3> a[8], event &o, nat<5> b, bool ?p, int &r) This declares a module with name M1 having the arguments in its interface:

number name type storage class dataflow

1 a array(int<3>,8) – uncontrollable input

2 o bool event output

3 b nat<5> – uncontrollable input

4 p bool – controllable input

5 r int memorized output

Specification Modules

The above explanations referred to behavioral modules that are used to model the system’s behavior. In addition, Quartz also offers specification modules that do not model the behavior of a system, and are used instead to formally specify the properties that the module should fulfill. The syntax of specifica- tion modules is as follows:

spec Name(decl1,...,decln)

implements ModuleCall { name1 : [task1] spec1;

.. .

namem : [taskm] specm;

}

Besides the use of the spec keyword instead of module there is only the dif- ference that the body statement has been replaced with a nonempty list of specifications. This list contains entries of a name, a task and a specification. For the names, the rules we mentioned for variables in the previous chapter apply, and the specifications are also as explained in the previous chapter. It remains to discuss the optional task entry, which is as follows:

• ProveA: This task means that it has to be checked whether the property holds for all initial situations.

• ProveE: This task means that it has to be checked whether the property holds for at least one initial situation.

• DisproveA: This task means that it has to be checked whether the property is false for all initial situations.

• DisproveE: This task means that it has to be checked whether the property is false for at least one situation.

Note that the path quantifiers can not be used to distinguish between the above cases. For example, ProveA: A phi means that it is to be checked whether in all initial states all paths satisfy the path property phi, while ProveA: E phi means that it is to be checked whether in all initial states there is a path that satisfies phi. Analogously, ProveE: A phi means that it is to be checked whether there is an initial state such that all paths starting in that initial state satisfy the path property phi, while ProveE: E phi checks whether there is an initial state with a path starting in that state that satisfies phi.

Note that all of the above tasks can be reduced to a standard model check- ing problem that checks whether all initial states are contained in the set of

states that satisfy the given specification (note that I 6= {}):

• [ProveA] : phi holds iff I ⊆JphiKK.

• [ProveE] : phi holds iff I 6⊆J!phiKK, i.e. I ∩JphiKK6= {}. • [DisproveA] : phi holds iff I 6⊆JphiKK.

• [DisproveE] : phi holds iff I ⊆J!phiKK, i.e. I ∩JphiKK= {}.

Even though the four tasks can be mapped to a standard model checking problem, it is necessary to distinguish at least two of them so that the model checker can report a success in all cases, while otherwise a failure would have to be reinterpreted as a success, which may lead to confusions.

In future versions of Quartz, more tasks will be available. In particular, tasks for supervisory control and worst case execution time analysis will be added.

While the meaning of a behavioral module is a state-based function that maps input values to output values, the meaning of a specification module is to check the tasks that are listed in the module. If the specification module is the first (or the only) module in a file, then this amounts to prove the properties for the universal Kripke structure that has one state for all possible variable assignments, and where all states are connected with each other. A [ProveA] task will then check the validity of the property, while a [ProveE] task checks whether the property is satisfiable. Analogously, [DisproveA] and [DisproveE] check whether the property is not valid and is not satisfiable, respectively.

If the specification module is not the first module in a file, then it is either dead code or it is called by the first module of that file. Specification modules can only be called by the implements clause in the header of a module. Dif- ferent problems are specified depending on whether the specification module is called by a behavioral or a specification module, as is described in the next paragraph.

The Refinement Relation

Finally, the definition of modules also specifies the ‘implements’ relation be- tween modules. This relation is a binary relation with the following meaning: module M1implements module M2iff the following holds:

• If both M1 and M2 are behavioral modules, then M1 implements M2

means that M1 is a refined version of M2 or in other words that M2 is

an abstraction of M1. Formally, this means that there is a simulation re- lation between M1 and M2, so that M2 can simulate each transition of

M2.

• If M1is a behavioral module and M2 is a specification module, then M1

implements M2means that all tasks listed in M2refer to the Kripke struc-

ture associated with M1. Hence, the resulting problems lead to classic

model checking problems.

• If both M1and M2 are specification modules, then the conjunction of the

properties of M1should imply the conjunction of the properties listed in

M2. The result is therefore a theorem proving task.

• The remaining case, where M2 is a behavioral module and M1is a speci-

fication module, has not yet been given a meaning.

Simulation relations are very important to reduce the complexity of model checking problems. It is known that all universal properties (i.e., those with- out positive/negative occurrences of E/A) that have already been verified for M1do also hold for M2. Thus, this can be used for compositional reasoning

and abstract interpretation.