Verifying SystemC with Scenario

This paper proposes a new approach for the analysis and veriﬁcation of complex systems. The core of the method consists in combining model-checking and abstract interpretation for analysis and veriﬁcation. The system is modeled by a Labeled Transition System obtained from a SystemC description, and the properties to be veriﬁed are formalized as an observer automaton with assertions. To ease the speciﬁcation of properties, we introduce a dedicated language, named Scenario . The contributions of the paper are twofold: the language for describing the expected properties and behavior of a system as Scenario , and a static analysis for verifying such properties.


INTRODUCTION
Modern system languages like SystemVerilog or SystemC [9] have widely spread in the industry for modeling complex, component-based systems.A SystemC code typically contains a large number of components that interacts through ports, channels and events.The overall complexity of systems makes standard model-checking or static analysis not applicable.
A common "divide and conquer" approach for such systems is to use an observer automaton to drive the analysis for only a small number among all the possible behaviors of the system at the same time.Although observer automata are widely known, it is not a common formalism to write specifications and properties with.We thus introduce a dedicated Scenario language for this purpose.
The scenario language is fully imperative, like pascal or C, and it is able to express many behaviors for the entire system and to specify properties that must occur during the execution of the system.It can also be used for analyzing Quality-of-Service (QoS) properties.Finally, a scenario is compiled into an observer automaton.
Model-checking is a common framework for analyzing component-based systems.For very complex systems, it suffers from exhaustively analyzing all the possible states of the system during its execution.We introduce a more static analysis method, based on abstract interpretation [4] in order to manipulate directly large collections of system-states, without requiring exhaustive exploration.
We combine the two techniques by defining a semi-abstract model for the product of the SystemC code with the Scenario observer automaton.As an intermediate transformation, we use classical techniques for translating SystemC code into a Labeled Transition System (LTS), for instance by using the SystemC FL framework [12].
With Scenario, we provide engineers with a comfortable and natural programming language for expressing system behaviors and properties as assertion statements.By applying our verification algorithms, such assertions can be proved to be always verified.When an assertion comes to be violated, the engineer has enough feedback on the system context for understanding the reason(s) of the failure.Debugging on an automatically generated counterexample scenario brings such a feedback.
The paper is structured as follows: first, we introduce in Section 2 a metaphoric example that is representative of standard SystemC programming patterns.Then, we present the syntax of the Scenario language in Section 3, and the compilation process into the observer automaton backend in Section 3.4.The second part of the paper, in Section 4, is dedicated to the analysis and verification techniques.
We conclude by generalizing the approach to other input formalisms and more precise analysis methods.

COMPONENT-BASED SYSTEMS WITH SYSTEMC
SystemC is used for designing systems with both hardware and software components.It is a C++ library that defines templates for modeling components, communication ports, and connections.Each component is represented by a class that holds data.The behavior of each component is computed inside threads that are executed concurrently by the SystemC library following a precise operational semantics [9].
As an illustration of such a system, we introduce an example which is representative for systemon-chip descriptions.It is a kind of FIFO, represented by a tank, with a consumer and a producer represented by pumps, as depicted in figure 1.The tank is equipped with two detectors that are sensitive to the high and low level of its content.These detectors are connected to a controller that powers on the draining pump when the level inside the tank becomes too high, until it reaches down the lower level.

Description of the Components
Beside the system graphical representation in figure 1 is the corresponding SystemC code.The constructor SC CTOR creates the instances of the tank, the two pumps and the controller.The template function connect, not represented here, just creates a channel and connects an outputport with an input-port with that channel.
The tank and the detectors are modelled by the class-component tank in figure 2. Adding or removing content from the tank is performed by calling its add method with a positive or negative amount.The two detectors are represented by output ports of type sc out <bool> that are updated by the add method.
The two pumps are modeled by the same class-component pump in figure 2. A pump has one thread that fills the tank at a clocked rate.The amount of fuild added or removed from the tank is hold by the faucet input port of the pump, of type sc in < int >.The thread code of a pump is a typical SystemC optimized code for a such a behavior: basically, the thread repeatedly waits for the clock-signal, then fills the tank with the value of the faucet.However, if the faucet comes to be zero, the thread exits the clocked loop and waits until further modification of the faucet occurs.These two waiting points of pump's thread are identified by @1 and @2 comments in figure 2.
Finally the controller holds two input ports of type sc in <bool> for turning on and off the faucet of the draining pump through the output port cmd of type sc out < int >.The controller is modeled by a SystemC method-thread introduced by SC METHOD.A method-thread is sensitive to several events, and is fired each time any of these events is notified.Here, the controller method adjusts the faucet value each time any of the detectors changes its value.
i n t level = 0; i n t h i _ l e v e l = 900 ; i n t l o _ l e v e l = 600 ; The "Tank" SystemC-modules

Behaviors of the System
There are a lot of possible behaviors for such a system during simulation.Intuitively, if the draining pump is powered enough to encompass the filling one, the expected behavior is as follows: first the tank is filled in by the filling pump, until the high-detector is fired.Then, the drain is powered by the controller, and the tank is eventually emptied down to the low-detector.At this point, the drain is disabled and the tank starts to be filled again.
A classical model checker must analyze all the concrete states of the system, that ranges between 900 and 1000 different ones in this tiny example.This is in contradiction with the intuitive simple behavior depicted above that only deals alternating filling and draining phases.Such a simple behavior is naturally represented by the following automaton written in pseudo-code style: where Fill is the filling behavior until the high detector is triggered and Drain is the symetric draining phase.However, the behavior of the system actually consists in three distinct phases.In the first one, the tank is filled from zero to the low-detector level.In the second phase, the tank is filled up to the high-detector level.And in the last phase, the tank is drained back to the low-detector level.So, a more precise description is provided by the following scenario: Fill ; w h i l e ( true ) { Drain ; Fill ' } By putting distinction between the initial filling phase (Fill) and subsequent ones (Fill'), the engineer obtains a more precise analysis.In fact, what is needed is a way to specify sequences of phases where different properties holds, even when the threads of the system are in the same configuration of activity.In this example, the tank level is between zero and low level during phase Fill, and between high and low level during phase Fill', whereas the tank is filled in during both phases.

SCENARIO
Our objective is here to specify the properties and the expected behaviors of a given system.As system languages have specific development flow, they require some kind of integrated verification plateform.SystemC FL [13] defines a proper intermediate plateform between SystemC and Model checkers.Lussy [10] is an intermediary between SystemC and the synchronous Lustre language.Instead of directly exporting the intern results into another formalism, the scenario concept [8] based on Live Sequence Charts rather authorizes reasoning, constructions, manipulation and verification in the formalism itself.With different kinds of scenarios, like use-case scenario, specification scenario, integration scenario, Quality of Service scenario, the scenarios are a hope to follow the development flow with successive transformations.Many concepts of our scenarios are related to the assume-guarantee approach.However, in a scenario, assume and guarantee expressions are strongly integrated with the control-flow of the system, and is much more expressive.Our fully imperative programming language allows for more complex behavior descriptions.The recent results of [3] extended by [1] may offer a way to automatically generate scenario parts for composing systems.

Syntax for Scenario
A scenario is a sequence of statements organized into loops, conditionals, and such.Atomic statements allow to compute values for local variables.Expressions may refer to the system components variables and port values.The most important statement for specifying the system behavior deals with synchronization.Such statements are defined in the next section.

Synchronization Expression
Synchronization with the system process is specified by instruction sync ω, where ω is an expression combining event notifications with logical combinators: The precise semantics of sync ω is defined in Section 6. Intuitively, sync ω specifies that the system executes for a while, until enough notified events have been observed for satisfying the ω condition.For instance sync a ∧ b blocks until both events a and b are notified.
However, the engineer wants generally to distinguish between different behaviors of the system, with respect to which event condition occurred.Also, different behaviors might occur depending on special conditions.Thus, there is the need for synchronizing with events, but also the need for filtering and verifying properties over the system.
The synchronization expressions are introduced by the following statements: The parallel construct S 1 ||S 2 is used when the system either behaves either like S 1 or like S 2 .Generally, S 1 and S 2 would start by conditions (assume e) and synchronizations (sync ω).
When the internals of the system does not satisfy an expression e, the execution of the system does not synchronize with the assume e statement.Actually, one may filter out such simulations, in order to restrict the use-case of the system for instance; otherwise, one may prefer to signal an error when such a case is considered as an assertion violation, by using the assert e statement.

Observer Automaton as Scenario Language back-end
Our static analysis works with an observer automaton, compiled from the scenario language.The (finite) states or points of an automaton are denoted by η, and the control-graph of the automaton is defined by a collection of directed transitions.The control-graph is directly obtained by compilation of the scenario (see Section 3.4), thus it is a finite graph.
Each transition from state η to state η is ruled by one of the three following forms: Guarding rule : η e −→ η e evaluates to true Update rule : η x:=e −→ η x is assigned to the evaluation of e Synchronization : η ω −→ η the evolution of the system is synchronized with ω The semantics of an observing automaton is expressed by a kind of synchronous product between the system under analysis and the automaton.This product may be considered as a new system that consists of: • the state η of the observer automaton, • the values of each variable of the scenario, • the state of the observed system The Section 4 explores into more details this product and the associated semantics.

Compilation of Scenario into Observer Automaton
As a preliminary remark, let us point out that many other languages compile naturally into observer automata.Examples include standard imperative programming languages, but also safety properties in temporal logic formalisms like [6].
For each construction of the Scenario language, a compilation scheme is defined for generating an automaton from node η to node η , using intermediate (fresh) nodes η 1 . . .η n .To simplify notations, any (recursive) call to the compilation scheme for scenario S is also denoted by η S −→ η .
The compilation scheme for imperative constructs are straightforward: Remark that other standard forms can be added to enrich the language, such as for loops and break statements.
For compiling assertions, we introduce a special node fail from which no transition exits.Any simulation that synchronizes with the scenario and fall into the fail node corresponds to the violation of the expected property.Other synchronization statements compilation scheme are straightforward:

Scenario and the tank example
Now, let us specify some example behaviors like "filling until hi/lo is notified" with scenario.This is simply identified by a synchronization statement sync as follows, where main denotes the instance of the system:

ANALYSIS & VERIFICATION
In this section, we introduce a practical framework for analyzing a system with respect to the observer automaton compiled from a scenario.Although our approach reuses existing techniques, we adapted them in order to fit well with each others.
Recall the overall methodology.The SystemC description of the components to be analyzed is translated in some LTS automaton.On another side, an observer automaton is obtained by compilation of the scenario to be analyzed.
We then define a product analysis of these two automata, that operates on a semi-abstract model.This model is precise over the states of the automaton, but abstracts the variables with overapproximations, following the abstract-interpretation theory [4].
Thus, applying model-checking techniques together with fixpoint computation over abstract domains, we obtain efficiently an over-approximation of all the possible executions that are represented by the scenario.The scenario is said to be verified when there is no execution that falls into the fail node.This section illustrates the different parts of the methodology.First, we briefly recall how an LTS can be obtained from SystemC code ; the same process can be adapted for other source languages such as VHDL, Lustre or Signal.Second, we define the abstract model and illustrate how to compute the product between the LTS and the observing automaton.Finally, we illustrate the technique with an example scenario over the "tank" system.

System LTS-Model
There are many techniques to translate SystemC code into an LTS.For instance, one can use the SystemC FL framework.We choose LTS and not an intern formalism like [11] or finite state machine [7] first to make this language independant from SystemC, and second to be able to build it and to refine it on the fly.
The SystemC framework commonly distinguishes between different phases for the execution of the system: • During the elaboration phase, components are initialized, ports are connected with each others, and threads are defined and spawned.
• During the simulation phase, the SystemC library schedules thread executions, communication through ports and event notifications.The simulation phase consists of different levels of cycles, summarized below [9] Section 4.2.1: 1. all runnable threads are executed until they block on a wait.2. notified events are propagated to wake up the blocked threads = evaluate phase.
3. port values are updated through channels = update phase.4. when 1-3 loop is finished, the threads specified to wake-up at 0 unit of time effectively wake-up and the execution goes back to 1 = delta notification.5. when 1-4 loop is finished, time advance is notified and the execution goes back to 1 = timed notification phase.
For analyzing a system with scenario, we assume the elaboration phase to be terminated, so that all components, ports and thread are completely defined and initialized.Then, the system can be represented as a collection of p threads that operates on m shared variables.
For analyzing system-level properties, we are not interested in the very low-levels of SystemC scheduling.Rather, we focus on the evolution of the system at δ-cycle.At the beginning of a δcycle, each thread is waiting on a wait statement.We call configuration of the system the array (p 1 , . . ., p n ) registering the waiting statements of each thread.
The LTS model of the system thus consists in one distinct state for any possible system configuration.Each transition of the LTS is obtained by collecting the effects of all internal steps of execution and conditions until a new waiting-configuration is reached.Effects consist of conditions, variable modifications and event notifications.
For determining these transitions, we must take into account with model-checking techniques the possible interleaving of the SystemC scheduler.However, this exhaustive exploration never goes beyond the next waiting configuration, thus avoiding much of the possible combinatorial explosion in practice.
For the "tank" example, we have few accessible configurations and transitions.There is one thread running for each pump, one method-thread for the controller, and one thread that shortly terminates for initiating the filling pump.The initiating thread has no waiting point and the controller thread has only one implicit waiting point.Thus, configurations are completely defined by the two pump's thread waiting points.See figure 3 for a (simplified) illustration of the LTS obtained for the "tank" system (where F and D are the filling and draining pumps, T denotes the tank).

Compiling the Scenario
To illustrate our methodology, we introduce the following example of scenario for representing the "tank" behavior: This scenario first waits for the tank to be filled above the lower detector.Then, it asserts that the tank level remains between the lower and higher detectors.Notice that the range have been enlarged with respect to detectors levels, in order for the controller action to take place.

The Abstract Model
For modeling the product of the system-LTS and the observer automaton of the scenario, we introduce an abstract model for representing a collection of many system states.Contrary to classical model checking techniques where each variable is assigned to one single value, we model the possible values of (integer) variables with an interval including all its reachable values.
This over-approximation is inspired by abstract-interpretation techniques.In practice, we use more precise (but more complex) abstract domains, that enable to take into account relational properties between variables.
The abstract model represents the possible configurations of the system that might occur at each point of the scenario.For each possible configuration, both the shared variables of the system and the local variables of the scenario are represented by intervals.
Thus, at each point η of the scenario, we have a matrix D, with one row for each configuration, and one column for each of the n processes and the m variables: On the "tank" example, the only variable of interest if the tank level.The other ones are not represented in the following examples to save place.Here, we represent the matrices with four columns: two for the filling and draining waiting-points of the pumps, and two columns for the draining pump one, and two columns for the lower and upper bounds of the tank.

Analysis
The instruction sync main.tank.losynchronizes with the system and the synchronization result assumes the tank level is over 600.Then, the first approximation of the model at point η 1 is: The next iteration enters the loop, verifies the assert and synchronizes with the clock.As it returns in the same configuration than η 1 a merge occurs with the previous state, resulting in D(η 1 ) = @2,@1 601..602 Remark also that D(η 2 ) = D(η 1 ).After few steps of approximations, the fixpoint is not yet reached.Following abstract interpretation methodology, we apply widening and narrowing strategies [5] to reach the fixpoint.This leads to: D(η 1 ) = @2,@1 601..900 However, a new transition appears from this configuration when the high detector is triggered.We thus find a new configuration where the draining pump has been powered on: D(η 1 ) = @2,@1 601..900 @2,@2 901..901 Again, the domain is not stable, and exploration must continue.In a similar way to filling phase, we find the next approximation: D(η 1 ) = @2,@1 601..900 @2,@2 897..901 Applying again widening and narrowing, we finally reach the correct definitive fixpoint: D(η 1 ) = D(η 2 ) = @2,@1 601..900 @2,@2 597..901 Remark the assertion verification is compatible with the tank level interval in both configurations.
As the fail point is not reachable, the synchronous product ensures all local properties are verified on the scenario.

CONCLUSION
Scenarios reveal to be a very interesting paradigm to drive the formal verification of componentbased systems.The synchronization verdict not only delivers a success or failure verdict, but also a stable description of (1) the domain of the scenario variables, (2) the possible reachable system configurations, (3) the domain of the system variables, and finally (4) the possible sets of notified events at each scenario point.Note that on a failure verdict, an automatic refinement like [2] may generate more structure in the scenario to make it successfully synchronize.
Many extensions and applications of the scenario language are possible.To cite only few of them, remark that an observer automaton is very closed to the LTS of a system.Actually, it is possible to consider scenario for simulating, abstracting and specifying components of the system.In such a context, applying the synchronous product between two scenarios may be considered as assembling component specifications.
In the last case, the reduction by {a,b} was not fully applied in order to show the intermediate result: both a and b are triggered, and finally the condition is not met.We can now define how to synchronize a simulation of the system with a condition: providing n is minimal for this property.
Such a definition exactly models the intuition of observing a simulation of the system until the condition holds: the events A i triggered during the simulation of the system successively reduce the condition ω, until only negative constraint remains.

s y n c
main .tank .lo ; f o r (;;) { a s s e r t 695 <= main .tank .level <= 905 ; s y n c c l o c k ; }
As another example, it is also possible to filter out some system simulations, by excluding those simulations where an event would occur.Hence, a more precise scenario can be written as follows:Finally, by using local variables the following scenario states that the tank's level has decreased during the draining phase:s y n c S .tank .hi ; w h i l e ( true ) { i n t mark = main .tank .level ; s y n c main .tank .lo ; a s s e r t main .tank .level ≤ mark ; s y n c main .tank .hi ; } s y n c main .tank .hi ; w h i l e ( true ) { s y n c main .c l o c k ; s y n c main .tank .hi ; VECOS 2008 } s y n c main .tank .hi ; w h i l e ( true ) { s y n c main .tank .lo ∧ ¬main .tank .hi ; s y n c main .tank .hi ∧ ¬main .tank .lo ; }