JOURNAL OF MULTIMEDIA, VOL. 5, NO. 5, OCTOBER 2010
501
Using UML as a front-end for StreamIt programs verification and generation Fateh Boutekkouk and Mohamed Benmohammed Department of computer science, University of Constantine, Algeria {Fateh_boutekkouk, Ben_moh123}@yahoo.fr
Abstract—UML provides all benefits from the objectoriented paradigm, such as encapsulation and reusability. It has been proven very successful and is widely used in software designs while StreamIt is targeted to support stream processing domain for multi-core architectures. However, programming in StreamIt can be error-prone and does not adequately cope with early validated multimedia systems. In addition, software engineers usually prefer to employ UML, due to its higher abstraction level and visual nature. This paper proposes the mapping of the application model from UML to StreamIt. Our flow starts by establishing the application model using UML structure diagrams. The second step consists in formal verification of some desirable and/or undesirable properties such as deadlock using the rewriting logic based Maude language before StreamIt code generation. Index Terms—UML, StreamIt, Maude, formal verification, reverse engineering
I. INTRODUCTION Streaming applications (SAs) are becoming increasingly important and ubiquitous. Most of recent embedded systems are all centred on a stream of voice or video data. SAs are characterized by some common features. The most important ones are: 1. SAs operate on a large (or virtually infinite) sequence of data items (data stream) which enter the program from some external resource and processed for a limited time before being discarded. 2. SAs represent a sequence of transformations on the data streams. The basic unit of this transformation is called a filter. Filters are generally independent and self-contained. 3. SAs have a stable computation pattern. A certain set of filters are repeatedly applied in a regular, predictable order to produce an output stream that is a given function of the input stream. 4. Occasional modification of stream structure due to some external events. 5. Occasional out-of-stream communication. Filters also communicate small amounts of control information on an infrequent and irregular basis. 6. High performance expectations, real time, and power constraints. © 2010 ACADEMY PUBLISHER doi:10.4304/jmm.5.5.501-513
Despite the prevalence of SAs, we can remark the scarcity of abstract and visual streaming programming models. On the other hand, the emergence of the Unified Modelling Language (UML) [6, 24] as a standard for object-oriented modelling may improve the design quality of streaming applications and helps designers to validate their requirements at early stages of development. Due to extension mechanisms offered by UML, UML can be adapted to streaming domain by definition of a set of stereotypes and constraints. On the other side, StreamIt [19] is an architecture independent language that is intended to simplify coding of signal processing and streaming computations. It offers a promising approach for exposing parallelism suitable for multi-core architectures. It is based on synchronous dataflow, with dynamic extensions. In StreamIt, the programmer constructs a stream graph, containing blocks with a single input and a single output, and describes the function of atomic blocks and the structure of composite blocks. The compiler then generates code for each block, and applies optimizations to the stream graph to produce efficient code for the target architecture. In this context, we propose the passage from UML models to a StreamIt model. We choose UML to be the front-end for StreamIt based flow for its user friendliness, documentation capabilities, and wide adoption. Furthermore, UML provides all benefits from the visual object-oriented paradigm such as encapsulation and reusability. Such important features are missed in the StreamIt language. The proposal also allows formal verification of some desirable and/or undesirable properties using Maude language, before StreamIt code generation. The impetus behind this tendency is due to the fact that StreamIt which is an extension of the C language for streaming domain lacks an adequate formal semantics allowing a formal analysis. Maude [10, 21] is a formal language based on a sound and a complete logic called the rewriting logic [8]. The healthy semantics makes the properties verification, relative to the described systems, rigorous and founded. The rewriting logic and the Maude language, in particular, are the fruit of work aiming to unify several concurrency models. Petri nets, state-transitions systems, Lotos, modal logic, temporal logic and other formalisms are integrated in the rewriting logic. Our choice of this
502
JOURNAL OF MULTIMEDIA, VOL. 5, NO. 5, OCTOBER 2010
language was not only based on its expression power, but also on its possibility to do simulation and formal verification. Hence, our work tries to take advantages of StreamIt as a domain specific programming language, UML as a standard for visual object oriented paradigm, and Maude for formal verification. The remaining of this paper is organized as follows: section two puts the light on UML. Section three presents the StreamIt language including its main constructors and execution semantics. Section four speaks about rewriting logic and the Maude language. Section five detaills our approach for UML modeling, Maude-based verification of some properties, and StreamIt code generation from UML models. An example of how our flow is applied is presented. Section six reviews quickley related works before concluding. II. THE UNIFIED MODELLING LANGUAGE (UML) UML is a graphical object-oriented modelling language, originally, was used in software (information) systems. The use of such graphical notation help designer to understand, capture, analyze, and document the client requirements at early stages of development in a semiformal manner. In its basic form, it is applicable to a wide variety of systems (open language). However, several key attributes of UML are important to embedded systems [5, 22, 23]: 1. UML is abstract, and designers can focus on the high-level characteristics of the system, rather than implementation-specific factors. 2. Hardware and software designers would share a common language. 3. A rich set of notations, executable models and semantics suited for modeling different points of view, and simulation. 4. Support for object-based structural decomposition and refinement. The forthcoming UML 2.0 standard comprises thirteen diagram types which can be classified into two categories: structural diagrams and behavioural diagrams. Structural diagrams describe the static structure of software systems and contain six types of diagrams that are the class diagram, the object diagram, the composite structure diagram, the component diagram, the deployment diagram, and the package diagram. Behaviour diagrams represent the dynamic behaviour of a system, and include seven types of diagrams that are the state machine diagram, the sequence diagram, the communication diagram, the interaction overview diagram, the activity diagram, the timing diagram, and the use case diagram. UML2.0 has brought several significant improvements to support concepts related to streaming applications domain especially, the structure diagram (SD) and the new semantics attached to activity diagrams (data flow). The SD describes the structure of the system as a network of components (objects, composite objects and blocks) related by abstract channels (links) for data and control exchange. It is similar to the well known © 2010 ACADEMY PUBLISHER
functional block diagram. Components have ports defining required and provided interfaces. An other type of ports which is important for SAs, is the Flowport concept. The latter allows representing the flow of data between blocks, without defining events and operations. Note that the Flowport concept is introduced by the SysML (System Modelling Language) language as an extension of UML2.0 for system engineering [17]. Figure 1 shows an example of a structure diagram. Activity diagrams specify a workflow, or process, for classes, use cases, and operations. As opposed to statecharts, activity diagrams are preferable when behaviour is not event driven which is more suitable for SAs. Figure 2 shows an example of an activity diagram. According to authors, UML2.0 can be tailored to different application domains by the definition of profiles. A profile extends an application specific UML sub-set using extension mechanisms offered by UML like stereotypes, constraints, and tagged values. Furthermore a profile must provide a methodology. The remarkable maturity of UML-based tools for code generation (e.g. Rhapsody [18]) will push designers to concentrate on higher level of abstractions rather than coding.
top
1
1
in
O1
1
p1
in
O2
p2
out
«flow»
«flow»
«flow»
Figure 1. UML structure diagram.
char sentence []="Benny is 29 years old"; char str[20]; int i
sscanf (sentence,"%s %*s %d",str,&i)
[else] [i>100] [i < 0]
printf ("too young\n")
printf ("%s -> %d\n",str,i)
printf ("too old\n")
Figure 2. UML activity diagram.
out
JOURNAL OF MULTIMEDIA, VOL. 5, NO. 5, OCTOBER 2010
503
Although, abstraction offered by UML may master complexity by reducing the number of processed objects and improve simulation speed, it comes on a price of inaccurate estimation, more refinements automation and extensive use of formal techniques for early validation and verification.
template void->T filterFileReader (string filename) { work push 1 { /* Implementation defined */ } }}
III. THE STREAMIT LANGUAGE
Figure 4. Example of a StreamIt template.
StreamIt [19] is a high level stream programming language targeting multi-core architectures. In StreamIt, programs are represented as graphs where nodes represent computation and edges represent FIFO-ordered communication of data over tapes. In StreamIt, two types of streams exist: filters and hierarchical streams. A filter is the basic programmable unit. Each filter has an independent address space. Thus, all communication with other filters is via the input and output channels, and occasionally via control messages. The main filter method is the work function which represents a steadystate execution step. The work function pops (i.e., reads) items from the filter input tape and pushes (i.e., writes) items to the filter output tape. A filter may also peek at a given index on its input tape without consuming the item. The push, pop, and peek rates are declared as part of the work function, thereby enabling the compiler to apply various optimizations and construct efficient execution schedules. Figure 3 gives an example of a filter named IntAvgFilter; it reads integers off of its input tape, and writes integers to its output tape. Each execution of the work function removes exactly one item from the input tape and examines at most two items; it causes exactly one item to be pushed on to the output tape. A StreamIt template is a parameterized filter where input and/or output types are declared as generic parameters. Figure 4 shows an example of a template filter named FileReader. Hierarchical streams are assembly of filters. StreamIt provides three constructors for composing filters into larger stream graphs that are Pipeline, Split-Join, and Feedback loop. int->int filter IntAvgFilter { init { // empty } work pop 1 peek 2 push 1 { push(peek(0) + peek(1)); pop(); }} Figure 3. Example of a filter in StreamIt.
A. Pipeline constructor The pipeline constructor permits the pipelined execution of child streams. It composes streams in sequence, with the output of one connected to the input of the next. An example of a pipeline appears in Figure 5. The add statement adds a child stream to the current stream object. B. Split-Join constructor The splitjoin construct distributes data to a set of parallel streams, which are then joined together in a round robin fashion. In a splitjoin, the splitter performs the data scattering, and the joiner performs the gathering. A splitter is a specialized filter with a single input and multiple output channels. On every execution step, it can distribute its output to any one of its children in either a duplicate or a round robin manner. A duplicate splitter (indicated by split duplicate) replicates incoming data to each stream connected to the splitter. The splitter counterpart is the joiner. It gathers data from its predecessors in a round robin manner to produce a single output stream. Figure 6 gives an example of a splitjoin that has two children. Incoming data is duplicated onto both streams. The resulting values are then pushed onto the output stream in a round-robin fashion, with values from alternating streams.
float->complex pipeline FloatToComplexAdd { add ReImToComplex(); add ComplexPairAdd(); }
Figure 5. Example of a pipelined stream in StreamIt.
int->int splitjoin AddAndSub { split duplicate; add IntAdder(); add IntSubtractor(); join roundrobin; }
Figure 6. Example of a SplitJoin stream in StreamIt.
© 2010 ACADEMY PUBLISHER
504
JOURNAL OF MULTIMEDIA, VOL. 5, NO. 5, OCTOBER 2010
float->float feedbackloop AddFeedback(float scaling) { join roundrobin; body FloatAdder(); loop FloatScaler(scaling); split duplicate; enqueue 0.0; } Figure 7. Example of the Feedback loop constructor in StreamIt.
C. Feedback loop constructor This constructor permits the introduction of cycles in the graph. A feedback loop has a body stream. Its output passes through a splitter; one branch of the splitter leaves the loop, and the other goes to the loop stream. The output of the loop stream and the loop input then go through a joiner to the body's input. Figure 7 gives an example of the feedback loop constructor. The enqueue statement takes a value and places it in FIFO order on the output tape from the loop stream. D. Execution model StreamIt does not explicitly assume sequential or parallel hardware. A component of the stream graph may fire, possibly in parallel with other firings, if its conditions to execute are met. The scheduler in the compiler chooses an order to execute filters such that the firing conditions are met. A particular back-end may place additional restrictions on firings; a uni-processor back-end may require that only one stream object executes at a time, or a parallel back-end might add the constraint that two objects may not both be executing if the output of one is an input of another. However, these constraints are transparent to the programmer. A filter may fire when its input has at least as many items as its peek rate. It pops its pop rate from the input, and pushes its push rate onto its output. A joiner in a feedback loop or splitjoin may fire when the specified number of items is available on each of its inputs. It pushes the sum of the input weights onto its output. A round-robin splitter may fire when the sum of the number of output items is available on its input, and produces the specified number of items on each output. A duplicate splitter may fire when at least one item is available on its input. It pops one item from its input and pushes one item to each output. The programmer should think of each work function as firing atomically. However, the compiler may choose to run filters in parallel, or to interleave certain work functions (this is sometimes necessary in the case of unbounded I/O rates). Such transformations will not be observable unless the programmer uses print statements (the only function with side effects) within multiple filters.
© 2010 ACADEMY PUBLISHER
E. Teleport messaging While the high-bandwidth flow of data is very predictable in typical streaming applications, realistic applications such as H264 codec also include unpredictable, low-bandwidth control messages for adjusting system parameters (e.g., desired precision in quantization, type of picture, resolution, etc.). To cope with this sort of applications, StreamIt provides a special messaging system called Teleport messaging. With this concept, filters can transmit control messages. The possible messages are defined by message handler functions; a message is sent to a portal, which can have multiple targets registered with it. A portal is a special type of variable that is passed by reference in stream parameters. It is associated with a single message receiver type; the full type name is portal. For each filter that receives messages, the runtime system maintains a queue to hold incoming messages. This queue is checked prior to each execution of the work function; any messages in the queue are dispatched to the appropriate message handlers. For each pair of message senders and receivers, there must be a directed path in the stream graph from one filter to the other (messages cannot be sent between filters on parallel paths in a splitjoin). For more details on this topic, one can refer to [19]. IV. REWRITING LOGIC AND MAUDE A. Rewriting logic The rewriting logic was introduced by Meseguer [8]. This logic having a complete semantics unifies all the formal models that express concurrence. In rewriting logic, the logic formulas are called rewriting rules. They have the following form: R: [t] -> [t’] if C. Rule R indicates that term t is transformed into t’ if a certain condition C if verified. Term represents a partial state of a global state S of the described system. The modification of the global state S of the system to another state S’ is realized by the parallel rewriting of one or more terms that express the partial states. The distributed state of a concurrent system is represented as a term whose subterms represent the different components of the concurrent state. B. Maude Maude [10, 21] is a specification and programming language based on the rewriting logic. Two specifications level are defined. The first level concerns the system specification, while the second one carries on the properties specification. System specification level This level is provided by the rewrite theory. It is mainly specified by the system modules. For a good modular description, three types of modules are defined in Maude. Functional modules allow defining data types and their functions through equations theory. Figure 8.a represents the functional module Nat specifying natural
JOURNAL OF MULTIMEDIA, VOL. 5, NO. 5, OCTOBER 2010
numbers. Such a module is imported in the module FACT (figure 8.b) to calculate the factorial of natural numbers. System modules define the dynamic behaviour of a system. This type of modules extends functional modules by introducing rewriting rules. A maximal degree of concurrence is offered by this type of module. Finally, there are the object-oriented modules that can be reduced to system modules. In relation to system modules, objectoriented modules offer a more appropriate syntax to describe the basic entities of the object paradigm as, among others: objects, messages and configuration. Only one rewriting rule allows expressing the consumption of certain floating messages, the sending of new messages, the destruction of objects, the creation of new objects, state change of certain objects, etc. Figure 9 illustrates the use of a system module BANK-ACCOUNT to define an object counts banking A and the two operations capable to affect its content credit and debit while executing the rewriting rules defined in this module. Note that after the execution of the unconditional rule [credit], the message credit(A, M) is consumed and the content of the account is increased. In the same way, the execution of the conditional rule [debit] requires that the condition (N>=M) be verified. The execution of such rule generates the consumption of the message debit(A,M) and the reduction of the content of the account. Property specification level This specification level defines the system properties to be verified. The system is described using a system module. By evaluating the set of states reachable from an initial state, the model-checking allows to verify a given property in a state or a set of states. This property is expressed in a logic temporal LTL (Linear Temporal Logic) or BTL (Branching Temporal Logic). fmod NAT is sorts Zero NzNat Nat . subsort Zero NzNat < Nat ***constructors op 0 : -> Zero . op s_ : Nat -> NzNat . endfm
(a)
fmod FACT is Including NAT . op _! : Nat -> NzNat . var N : Nat . eq 0 ! = 1 . eq (s N) ! = (s N) * N !. endfm
(b) Figure 8. Functional Modules NAT and FACT.
© 2010 ACADEMY PUBLISHER
505
mod BANK-ACCOUNT is protecting INT . including CONFIGURATION . op Account : -> Cid. op bal :_ : Int -> Attribute . ops credit debit : Oid Nat -> Msg . var A : Oid . vars M N :Int rl [credit]: < A : Account | bal : N > credit(A, M) => < A : Account | bal:N + M > crl [debit] : < A : Account | bal : N > debit(A, M) => < A : Account | bal : N - M > If N >= M . endm Figure 9. The BANK-ACCOUNT module in system module form.
fmod SATISFACTION is protecting LTL . sort State . op _|=_ : State Formula ~> Bool . endfm
Figure 10. A module in Maude implementing the operator of satisfaction of a formula in a state.
The Model-checking supported by Maude's platform essentially uses the LTL logic for its simplicity and the defined decision procedures it offers (for more details, see [21]). LTL operators are represented in Maude using a syntactic form similar to their original form. For example, the operation [] is defined in Maude by the operator (always). This latter is applied to a formula to give a new formula. Furthermore, we need an operator indicating if a given formula is true or false in a certain state. We find such an operator (|=) in a predefined module called SATISFACTION (figure 10). The state State is generic. After specifying the behavior of its system in Maude system module, the user can specify several predicates expressing some properties related to the system. These predicates are described in a new module that imports in its turn, two modules: the first one that describes the system's dynamic aspect, where the second is the module SATISFACTION. Let, for example, M-PREDS (figure 11) the name of the module describing the predicates on the system's states. M is the name of the module describing the system's behavior. The user must specify that the chosen state (chosen configuration in this example) for its own system is sub-type of State type.
506
JOURNAL OF MULTIMEDIA, VOL. 5, NO. 5, OCTOBER 2010
mod M-PREDS is protecting M . including SATISFACTION . subsort Configuration < State . … endm
Figure 11. A module in Maude containing predicates defined by the user about a system described by a module M.
fmod MODEL-CHECKER is including SATISFACTION . … op counterexample : TransitionList TransitionList -> ModelCheckResult [ctor] . op modelCheck : State Formula ~> ModelCheckResult . … endfm
Figure 12. A module in Maude containing the services offered to the user by Maude Model-checker.
At the end, we find the MODEL-CHECKER module (figure 12) that offers the function model-Check. The user can call this function while specifying a given initial state and a formula. Maude model-Checker verifies if this formula is valid (according to the nature of the formula and the procedure of the model-checker adopted by Maude system) in this state or the set of all reachable states form the initial state. If the formula is not valid, a counter example (counterexample) is displayed. The counter example concerns the state in which the formula is not valid. V. OUR APPROACH As mentioned before, our flow starts by establishing the UML model of the application. The latter captures the common StreamIt structural constructors such as filters, templates, pipelines, SplitJoins, and feedback loops at a high level of abstraction using UML2.0 structure diagrams. At this stage, we are interested in the structural aspect. The code of filters works and initialization methods has not been introduced yet. The subsequent steps consist in formal verification and StreamIt code generation. The formal verification is accomplished by the translation of the UML models to a Maude specification. The latter is used to formally verify the correctness of the application against some undesirable and/or desirable properties.
© 2010 ACADEMY PUBLISHER
A. Application modelling In order to capture StreamIt constructors at UML level, we have to define for each StreamIt constructor a stereotype. Hence, we define seven stereotypes that are: filter, template, pipeline, split-join, split, join, and feedback loop. The filter, template, split, and join stereotypes are applied on simple objects that represent application leafs. Each of them has three tagged values that are the minimum, the average, and the maximum number of iterations. Of course, if the number of iterations is not data-dependent, then the above tagged values are equal. In general, these values are introduced whenever the number of iterations is not known at priori (i.e. data dependent). The stereotype split has two supplementary tagged values that are the split mode which can be roundrobin or duplicate, and the split rate. We add to the stereotype join two stereotypes that are the join mode which is generally roundrobin, and the join rate. For each filter object, we define two methods: the init method for filter code initialization and the work method with three arguments: the pop, the push, and the peek rates. Each of these arguments has three values: the minimum, the average, and the maximum rate. The stereotypes pipeline, split-join, and feedback loop are applied on UML composite objects. Each of them has two tagged values that are the occurrences number for repetitive structures and occurrence condition. Similarly, we define two stereotypes for object attributes: parameter to specify a filter or hierarchic stream parameter, and portal to specify a portal. Data flows are modelled via SysML flows. Each flow connects two flow ports; the source (input) port and the target (output) port. For each port, we introduce the stereotype type to specify the type of the transmitted data. Finally, we associate to the data flow a stereotype: channel with one tagged value: available designating the number of available tokens on the channel. Channels act as unbounded FIFO queues for data between filters. Figures 13 shows an example of an UML filter object named compute with a parameter B and two functions: init and work. Figure 14 shows a template. Figure 15 shows an example of a pipeline composite object named f including two filters f1 and f2. Figure 16 shows an example of a Split-Join filter that includes two filters f1 and f2, a split filter, and a join filter.
in
1
«filter»
compute
«parameter» B:int
init():void work(pop:rate=[1,1,4],peek:rate=2,push:rate=1):void
Figure 13. Filter object.
out
JOURNAL OF MULTIMEDIA, VOL. 5, NO. 5, OCTOBER 2010
1
«template»
Identity
in
out
«parameter» Type:int work(pop:int=1,push:int=1,p...
Figure 14. Template object.
f
in
in
1
«filter»
f1
«channel» p
«channel» p 1
out
«filter»
out
f2
«channel»
This conditional rule specifies the execution of the work operation: when firing conditions are verified ((in > pe or in == pe) and ( po < pe or po == pe)), an instance of the class filter f, pops (reads) po data tokens from channel ch1 and pushes (writes) pu data tokens on channel ch2 (available = n2 + pu ). state is an attribute; we use it to determine the firing state. If the firing conditions are not met then state is always false, otherwise, it is true.
Figure 15. Pipeline composite object. «splitjoin»
1
SplitJoin
in
in
1
«split»
splitter
«channel» pf2
pf1
«channel»
«channel»
pf1 1
«filter»
1
f1
p1
pf2
«filter»
f2
«channel»
«channel»
out
1
p2
p2
p1
out
of some parameters which are related to filters like peek, pop and push rates. Following the semantic of StreamIt execution model a filter only fire when its inputs have at least as many items as its peek rate. When a filter fires, it pops its pop rate from the input, and pushes its push rate onto its output. The pop rate must be less or equal to the peek rate. We are interested in the maximum values for filter pop, push rates and iterations number. Let n1, n2, pe, po, pu, and itr be the number of available tokens on an input channel, the number of available tokens on an output channel, the peek rate, the maximum pop rate, the maximum push rate, and the maximum iterations number of a filter f respectively. Using Maude, each firing can be modelled via a set of conditional rewrite rules as follows: crl [work] : ***1 < f : filter | peek : pe , pop : po, push : pu, state : false > < ch1 : channel | available : n1, source : f0, target : f > < ch2 : channel | available : n2, source : f, target : f1 > => < f : filter | peek : pe , pop : po, push : pu, state : true > < ch1 : channel | available : n1 - po, source : f0, target : f > < ch2 : channel | available : n2 + pu , source : f, target : f1 > if (n1 > pe or n1 == pe) and ( po < pe or po == pe) .
«pipeline»
1
507
«join»
joiner
«channel»
Figure 16. SplitJoin composite object.
B. Mapping of UML models to Maude code In order to formally verify some undesirable properties such as deadlock, we have to transform the UML models to a Maude specification. A deadlock can occur due to an improper filters execution orders and/or to a bad choice
© 2010 ACADEMY PUBLISHER
crl [work] : ***2 < f1 : filter | peek : pe , pop : po, push : pu, iter : itr, count : cn, state : false > < ch1 : channel | available : in, source : f0, target : f1 > < ch2 : channel | available : out, source : f1, target : f2 > => < f1 : filter | peek : pe , pop : po, push : pu, iter : itr, count : cn + 1, state : false > < ch1 : channel | available : in - po, source : f0, target : f1 > < ch2 : channel | available : out + pu , source : f1, target : f2 > if (in > pe or in == pe) and ( po < pe or po == pe) and (cn < itr ) . crl [endloopf1] : ***3 < f1 : filter | peek : pe , pop : po, push : pu, iter : itr, count : cn, state : sta >
508
JOURNAL OF MULTIMEDIA, VOL. 5, NO. 5, OCTOBER 2010
=> < f1 : filter | peek : pe , pop : po, push : pu, iter : itr, count : cn, state : true > if (cn == itr) and (sta == false) .
< ch2 : channel | available : out + pu1 , source : f1, target : f2 > if (in > pe1 or in == pe1) and ( po1 < pe1 or po1 == pe1) and (cn < itr) .
Rules 2 and 3 specify the iterative execution of the work operation. The attribute count is introduced to compute the iterations number. In this case, the firing state is true only when the work operation executes for its all iterations itr.
crl [workf1] : ***7 < f1 : filter | peek : pe1 , pop : po1, push : pu1, iter : itr, count : cn1, state : false > < f2 : filter | peek : pe2 , pop : po2, push : pu2, iter : itr, count : cn2, state : false > => < f1 : filter | peek : pe1 , pop : po1, push : pu1, iter : itr, count : cn1, state : false > < f2 : filter | peek : pe2 , pop : po2, push : pu2, iter : itr, count : cn2, state : false > start(f2) if (cn1 == 1) .
crl [workf1] : ***4 < f1 : filter | peek : pe1 , pop : po1, push : pu1,state : false > < ch1 : channel | available : in, source : f0, target : f1 > < ch2 : channel | available : out, source : f1, target : f2 > < f2 : filter | peek : pe2 , pop : po2, push : pu2,state : false > => < f1 : filter | peek : pe1 , pop : po1, push : pu1, state : true > < ch1 : channel | available : in – po1, source : f0, target : f1 > < ch2 : channel | available : out + pu1 , source : f1, target : f2 > < f2 : filter | peek : pe2 , pop : po2, push : pu2,state : false > start(f2) if (in > pe1 or in == pe1) and ( po1 < pe1 or po1 == pe1) . crl [workf2] : ***5 start(f2) < f2 : filter | peek : pe , pop : po, push : pu, state : false > < ch2 : channel | available : in, source : f1, target : f2 > < ch3 : channel | available : out, source : f2, target : f3 > => < f2 : filter | peek : pe , pop : po, push : pu, state : true > < ch2 : channel | available : in - po, source : f1, target : f2 > < ch3 : channel | available : out + pu, source : f2, target : f3 > if (in > pe or in == pe) and ( po < pe or po == pe) .
Rules 4 and 5 specify the sequential execution of two filters f1 and f2. We introduce the start method to specify the fact that f2 begins execution only when f1 terminates its execution (firing). crl [workf1] : ***6 < f1 : filter | peek : pe1 , pop : po1, push : pu1, iter : itr, count : cn, state : false > < ch1 : channel | available : in, source : f0, target : f1 > < ch2 : channel | available : out, source : f1, target : f2 > => < f1 : filter | peek : pe1 , pop : po1, push : pu1, iter : itr, count : cn + 1, state : false > < ch1 : channel | available : in – po1, source : f0, target : f1 >
© 2010 ACADEMY PUBLISHER
crl [endloopf1] : ***8 < f1 : filter | peek : pe , pop : po, push : pu, iter : itr, count : cn, state : sta > => < f1 : filter | peek : pe , pop : po, push : pu, iter : itr, count : cn, state : true > if (cn == itr) and (sta == false) . crl [workf2] : ***9 start(f2) < f2 : filter | peek : pe , pop : po, push : pu, iter : itr, count : cn, state : false > < ch2 : channel | available : in, source : f1, target : f2 > < ch3 : channel | available : out, source : f2, target : f3 > => < f2 : filter | peek : pe2 , pop : po2, push : pu2, iter : itr, count : cn + 1, state : false > < ch2 : channel | available : in, source : f1, target : f2 > < ch3 : channel | available : out, source : f2, target : f3 > start(f2) if (in > pe or in == pe) and ( po < pe or po == pe) and (cn < itr ) . crl [endloopf2] : ***10 start(f2) < f2 : filter | peek : pe2 , pop : po2, push : pu2, iter : itr, count : cn, state : sta > => < f2 : filter | peek : pe2 , pop : po2, push : pu2, iter : itr, count : cn, state : true > if (cn == itr) and (sta == false) .
Rules 6, 7, and 9 specify two filters f1 and f2 executing in a pipeline fashion. Pipelining execution implies the iterative execution of filters. In the first iteration, only f1 executes. In the second iteration, filter f1 triggers f2 using the start method. f1 and f2 execute in parallel in the second and all following iterations. At the end of execution, both f1 and f2 execute exactly itr times. Rules 8 and 10 specify the iterative execution of filters f1 and f2 respectively. crl [splitroundrobin] :
***11
JOURNAL OF MULTIMEDIA, VOL. 5, NO. 5, OCTOBER 2010
< splitter : splitRR | push : pu, state : false > < ch0 : channel | available : n0, source : f0, target : splitter > < ch1 : channel | available : n1, source : splitter, target : f1 > < ch2 : channel | available : n2, source : splitter, target : f2 > => < splitter : splitRR | push : pu, state : true > < ch0 : channel | available : n0 - (2 * pu), source : f0, target : splitter > < ch1 : channel | available : n1 + pu, source : splitter, target : f1 > < ch2 : channel | available : n2 + pu, source : splitter, target : f2 > if (n0 > (2 * pu) or n0 == (2 * pu)) .
Rule 11 specifies the round robin splitter execution with one input channel ch0 and two output channels ch1 and ch2. In this case the splitter reads from the input channel 2*pu tokens and adds pu token to each output channel. crl [splitduplicate] : ***12 < splitter : splitD | state : false > < ch0 : channel | available : n0, source : f0, target : splitter > < ch1 : channel | available : n1, source : splitter, target : f1 > < ch2 : channel | available : n2, source : splitter, target : f2 > => < splitter : splitD | state : true > < ch0 : channel | available : n0 - 1, source : f0, target : splitter > < ch1 : channel | available : n1 + 1, source : splitter, target : f1 > < ch2 : channel | available : n2 + 1, source : splitter, target : f2 > if (n0 > 1 or n0 == 1) .
Rule 12 specifies the duplicate splitter execution with one input channel ch0 and two output channels ch1 and ch2. In this case the splitter reads from the input channel one token and adds one token to each output channel. crl [joiner] : ***13 < joiner : join | pop : po, state : false > < ch0 : channel | available : n1, source : f1, target : joiner > < ch1 : channel | available : n2, source : f2, target : joiner > < ch2 : channel | available : n3, source : joiner, target : f3 > => < joiner : join | pop : po, state : true > < ch0 : channel | available : n1 - po, source : f1, target : joiner > < ch1 : channel | available : n2 - po, source : f2, target : joiner > < ch2 : channel | available : n3 + (2 * po), source : joiner, target : f3 > if (po < n1 or po == n1) and (po < n2 or po == n2) .
© 2010 ACADEMY PUBLISHER
509
Rule 13 specifies the joiner execution with two input channels ch0 and ch1, and one output channel ch2. In this case, the joiner reads po tokens from each input channel and adds 2*po tokens to the output channel. Concerning the feedback loop, we apply the same principles as above. We must note that in a the feedback loop constructor, the joiner has exactly two input channels (one from the input stream and the other from the loop output) and the splitter has exactly two output channels (one to the output stream and the other to the loop input). The body and loop constructors are ordinary filters. In order to specify the enqueue statement semantics, we add a condition (n1 > 0) on the firing rule for the joiner as follows: crl [loopjoiner] : ***14 < joiner : join | pop : po, state : false > < ch0 : channel | available : n0, source : f0, target : joiner > < ch1 : channel | available : n1, source : loop, target : joiner > < ch2 : channel | available : n2, source : joiner, target : body > => < joiner : join | pop : po, state : true > < ch0 : channel | available : n0 - po, source : f0, target : joiner > < ch1 : channel | available : n1 - po, source : loop, target : joiner > < ch2 : channel | available : n2 + (2 * po), source : joiner, target : body > if (po < n0 or po == n0) and (po < n1 or po == n1) and (n1 > 0) .
Using the Maude command “search in modulename : initial =>! X:conf such that End(X:conf) == true .”, we can easily verify whether all filters terminate their executions successfully (all firings states are true) which is a desirable property (i.e. there is no deadlock). End is a function defined as: sort conf . subsort conf < Configuration . op sta : conf -> state . op End : conf -> Bool . eq sta (< f : filter | peek : pe , pop : po, push : pu, state : st >) = st . eq sta (< f : filter | peek : pe , pop : po, push : pu, count: cnt, iter : itr, state : st >) = st . eq End (f) = if sta(f) == true then true else false fi .
Note that the command “search S =>! S’, where =>!” means that we are looking for terminal states, that is, states from which no further rewritings can take place. initial is the initial configuration of the system and modulename is the name of the system module. Another important property we can verify is the steady state: each valid stream graph has a state where the amount of data buffered between filters does not change. In other words a steady state can be executed repeatedly forever without growing data buffers between filters. In
510
order to verify this property, we can define the following operations: op steady : conf -> Bool . (< ch : channel | available : n, source : f1, target : f2 >) eq steady (< ch : channel | available : n, source : f1, target : f2 >) = if n == 0 then true else false fi .
The steady function defines a strict property (at the end of firings, all filters buffers have a size equal to zero). Similarly, we can for instance define a no strict property concerning the steady state. In this case, designer may define a threshold S for buffers size. In this case, our aim is to verify whether the buffer size in each firing can exceed S or not. For this purpose, we have to define a property named ThresholdSteady then apply Maude model checker. var cf : Configuration . op ThresholdSteady : Configuration -> Prop . ceq < ch : channel | available : n, threshold : S, source : f1, target : f2 > cf|= ThresholdSteady (< ch : channel | available : n, threshold : S, source : f1, target : f2 > cf) = true if n < S .
JOURNAL OF MULTIMEDIA, VOL. 5, NO. 5, OCTOBER 2010
D. Case study As a case study, we have tested our flow for the MP3 decoder [19, 20]. For the sake of space, we show only pipeline filters. Figures 17, 18, 19, and 20 show the UML structure diagrams for the pipelines MP3 (the top filter), the MultiChannelPCMSynthesis, the FilterBank, and the PCMSynthesis modules respectively. The PCMSynthesis pipeline includes five children: filter3, buildU, multivD, splitjoin1, and convertShort. splitjoin1 is a split-Join composite filter. It includes a splitter, a joiner, and filter4. Here, filter4 execute iteratively (32 times). But since filter4 is a splitjoin child, then we can say that we have thirty two (32) copies of filter4 and all the copies execute in parallel. We have used core Maude version 2.3. According to the model checker execution, the model checker signals a counter example for the Thresholdsteady property verification for PCMSynthesis filter. The time mentioned is approximately 2497 ms on Mobile AMD 3600 Sympron ™ 1.99 GHz. We think that such amount of time is acceptable for a complicated example (i.e. PCMSynthesis) including more than 1000 lines of code (rewriting rules). Unfortunately we have no results for other works for the same example. Compared to other model checkers, Maude model checker is relatively faster [21].
C. StreamIt code generation from UML structural models The last step in our flow is the StreamIt code generation from UML structure diagrams. Since the internal code of filters has not been known yet, the generated code consists only of filters skeletons including signatures (with eventual parameters), and structural constructors. To accomplish our objective, we have developed a Rhapsody-based tool, that given an UML structure diagrams, it generates automatically a Maude specification. After validating the application, a StreamIt code is generated. We have used the Visual Basic interpreter (VB) which is an API (Application Programming Interface) integrated in the Rhapsody environment [18]. Using such API, we can easily parse UML models and generate text files for both Maude and StreamIt. A limitation of using VB is that generated code (for both Maude and StreamIt) is Rhapsody-dependent. In order to make our UML models interoperable with other tools, we have to generate a XMI file from Rhapsody-based UML models, process this file and then generate the target code using XML parsers (for instance JDOM).
© 2010 ACADEMY PUBLISHER
«pipeline»
1
Using the command “red modelCheck(initial, [] ThresholdSteady (initial)) .”, we can easily verify the property. Note that symbol [] means globally: the threshold steady property must hold for every state of every computational path. initial is the initial configuration.
MP3
p0
p0
1
«template»
FileReader
«channel»
«parameter» Para... work(pop:int=0,pu... init():void p1 «channel»
p1 1
«pipeline»
MultiChannelPCMSynthesis «parameter» n:int=2
p2
«channel»
p2 1
«filter»
output_int
p3
p3
«channel»
init():void work(pop:int=1,pu...
Figure 17. Structure diagram of the MP3 top level filter.
JOURNAL OF MULTIMEDIA, VOL. 5, NO. 5, OCTOBER 2010
511
«pipeline»
1
FilterBank «pipeline»
1
MultiChannelPCMSynthesis
p1
p2
p2 1
MultiChannelPCMSynthesisInterval «channel»
1
«f ilter»
Antialias
«splitjoin»
«channel»
init():void work(pop:int...
«parameter» n:int
p1
p1
1
p3 «channel» p3
«split»
splitter
1
«channel»
«f ilter»
IMDCT p2
«channel»
p2
init():void work(pop:int...
«pipeline»
1
FilterBank
p4 p4
p2
1
«channel» «f ilter»
filter1 p3 p3
«channel»
1
p2 p2
init():void work(pop:i...
«channel» «join»
p5
joiner
«channel» p5
«channel»
«splitjoin»
1
freqInversion
Figure 18. MultiChannelPCMSynthesis Pipeline.
p6 «channel» p6 «pipeline»
1
PCMSynthesis
p7 p3 «channel»
Figure 19. FilterBank Pipeline diagram.
© 2010 ACADEMY PUBLISHER
512
JOURNAL OF MULTIMEDIA, VOL. 5, NO. 5, OCTOBER 2010
«pip eline»
1
PCMSynthesis
1
«filt er»
filter3 p6
p6 «channel»
init():void work(pop:i p7 «channel» p7 1
«filt er»
buildU
init():void p8 «channel» p8 1
«filt er»
multiVD
init():void p9 «channel» p9 «spl itjoin»
1
splitjoin1
p10 «channel» p10 1
«filt er»
convertShort p7 «channel» p7
Figure 20. PCMSynthesis Pipeline diagram.
VI. RELATED WORK We can classify today state of the practice approaches for streaming applications design into five main classes that are UML-based approaches, formal approaches, C/C++ based approaches, streaming languages, and hybrid approaches. For each class, we try to mention the most relevant works. A. UML based approaches Many UML2.0 profiles have been proposed by both academia and industry. Among UML2.0 profiles we can mention in particular the GASPARD2 profile for modelling and simulation of intensive signal processing embedded systems [16]. There are other UML2.0 profiles that are more general in the sense we can apply them for a large variety of applications including the streaming ones such as the TUT profile [13]. The main problem with these profiles is the lack of formal support for analysis, refinement, and early validation. B. Formal approaches The application of formal techniques in the streaming
© 2010 ACADEMY PUBLISHER
domain is becoming more interesting especially for applications requiring safety, and predictability (critical systems). We can mention Ptolemy [14]. It is based on many heterogeneous MOCs (models of computations) such as Kahn Process Networks (KPN), and Synchronous Data Flow (SDF). C. C/C++ based approaches The use of the programming languages C and C++ in streaming applications design is still omnipresent. These languages bring many benefits since most designers are well familiar with C/C++. We do not forget the optimization capability offered by the C compiler to minimize time, memory and power consumption. Such optimizations are mandatory in the embedded systems field. But they are too detailed and are not very suitable for hardware part. For this raison, authors have extended these languages, so they can be applied at system level. Among these extensions we find in particular SystemC and SpecC languages [15]. A second main limitation of these approaches is the lack of formal support for analysis and verification. D. Streaming languages The stream programming paradigm provides a natural representation of data streams and offers a promising approach for exposing parallelism suitable for multi-core architectures [11, 12]. Stream languages such as StreamIt, Brook, SPUR, Cg, Baker, and Spidle are examples of such a paradigm [12]. For instance in the StreamIt language [19], a program is represented as a set of filters that communicate through FIFO data channels. During program execution, actors fire repeatedly in a periodic schedule. The main limitation of this paradigm is the lack of formal support for verification. E. Hybrid approaches This class tends to combine between the abovementioned approaches. For instance and in order to surmount the UML problem (lack of a well defined semantics), many works try to generate formal specifications from UML diagrams. VII. CONCLUSION AND FUTURE WORK In this paper, we present our idea that consists in the mapping of UML models to StreamIt programs. Furthermore, our proposal allows formal verification of some properties using the Maude language before StreamIt code generation. In deed, we can say that, we have expressed the execution semantics of the StreamIt language using rewriting logic. Such early verification is mandatory especially in critical systems. Certainly, UML brings many advantages due to its visual modelling capabilities and abstraction. But, UML as it is can not meet all applications targeting multi-core architectures such as operating systems code generation form UML diagrams. This is one of the current issues. As a perspective, we intend to apply the reverse engineering process to integrate StreamIt legacy code into an UML environment. The reverse engineering is the
JOURNAL OF MULTIMEDIA, VOL. 5, NO. 5, OCTOBER 2010
transformation of a programming language code to UML models. Such transformation will enable designers to exploit UML-based tools for visual modelling, high level estimations, and formal analysis. To meet this goal, we first transform StreamIt file (.str) to an XMI representation from which UML models are generated. The XMI file can be generated using the VB integrated in Rhapsody or another tool supporting this feature. By importing the XMI file into the Rhapsody environment (using the command Import XMI Into Rhapsody), we can easily, recuperate the XMI StreamIt file and generate the UML models. Another solution is to use the VB directly. In this case, our program has to read an input StreamIt file (.str), then creating UML diagrams. Another perspective is to find a convenient way to model operating system and multi-core architectures at UML level. We plan also to test the execution of model checker of the entire MP3 decoder on a more sophisticated processor. REFERENCES [1] E. Riccobene, P. Scandura, A. Rosti, and S. Bocchino, “A SOC Design Methodology Involving a UML2.0 Profile for SystemC,” in Proceedings of the Design, Automation and Test in Europe Conference end Exhibition (DATE’05), 2005. [2] F. Boutekkouk and M. Benmohammed, “A Novel UML2.0-based approach for System On a Chip modeling and Co-design,” in VLSI-SOC PhD Forum, Nice, France, 2006. [3] F. Boutekkouk and M. Benmohammed, “Analyse dirigée par les scénarios de l’ordonnaçabilité d’une application embarquée temps réel,” in CGE’05. Ecole militaire polytechnique, Bordj El Bahri, Alger, 16-17 Avril, 2007. [4] F. Boutekkouk, S. Bilavarn, M. Auguin, and M. Benmohammed, “UML profile for estimating Application Worst Case Execution Time on System-On-Chip,” in SOC2008: International Symposium on System-on-Chip. Tampere, Finland, 5-6 November, 2008. [5] F. Boutekkouk, S. Bilavarn, M. Auguin, and M. Benmohammed, UML2.0 profiles for Embedded Systems and Systems On a Chip (SOCs), in JOT (Journal of Object Technology), January 2009. [6] G. Booch, J. Rumbaugh, and I. Jacobson, Unified Modeling Language User Guide (Addison-Wesley, 1999). [7] I. Ahmed, Y. He, and M. L. Liou, “Video compression with parallel processing,” Parallel Computing Journal 28, 2002, 1039-1078. [8] J. Meseguer, “Rewriting as a unified model of concurrency,” in proceedings of the Concurr’90 Conference, Amsterdam, pp. 384-400, Springer LNCS Vol. 458, 1990. [9] L. Appvrille, M. Waseem, R. Ameur Boulifa, S. Coudert, and R. Pacalet, “A UML-based Environment for System Design Space Exploration,” 13th IEEE International Conference on Electronics, Circuits and Systems (ICECS’2006), Nice, France, 2006. [10] M. Clavel and al, Maude: Specification and Programming in Rewriting Logic, internal report, SRI International, 1999. [11] M. Drake, H. Hoffman, R. Rabbah, and S. Amarasinghe, “MPEG-2 Decoding in a Stream Programming Language,” in IPDPS, Rhodes Island, Greece, April 2006.
© 2010 ACADEMY PUBLISHER
513
[12] M. I. Gordon, W. Thies, and S. Amarasinghe, “Exploiting Coarse Grained Task, Data, and Pipeline Parallelism in Stream Programs,” in ASPLOS06, San Jose, California, USA, 21-25 October, 2006. [13] P. Kukkala, J. Riihimaki, M. Hannikainen, TD. Hamalainen, and K. Kronlof, “UML2.0 Profile for Embedded System Design,” in Proceedings of the Design, Automation and Test in Europe Conference end Exhibition (DATE’05), 2005. [14] Ptolemy Project, http://Ptolemy.eecs.berkely.edu. [15] R. Domer, System-level modeling and design with the SpecC language, Dissertation at the university of Dortmund, Germany, 2000. [16] R. Ben Atitallah, P. Boulet, A. Cuccuru, J.L. Dekeyser, A. Honré, O. Labbani, S. Le Bleu, P. Marquet, E. Piel, J. Taillard, and H. Yu, Gaspard2 UML profile documentation, N° 0342. INRIA. Rapport technique,2007. [17] Systems Modelling Language (SysML) Specification. OMG document: ad/2006-03-08-01, version 1. Draft, April 2006. [18] Rhapsody case tool reference manual. I-Logix Inc. http://www.ilogix.com. [19] StreamIt Language Specification Version 2.1,
[email protected], September, 2006. [20] S. Stuijk, Predictable Mapping of Streaming Applications on Multiprocessors. PhD thesis. University of Eindhoven, 25 October 2007. [21] T. McCombs, Maude 2.0 Primer, Version 1.0, Internal report, SRI, International 2003. [22] UML Profile for System on a Chip (SOC), OMG Available Specification, version 1.0.1 formal /06-08-01, 2006. [23] UML Profile for MARTE, Beta 1, OMG Adopted Specification, ptc/07-08-04, 2007. [24] www.uml.org.
Fateh Boutekkouk was born in Constantine (Algeria). He received his BS and PhD degrees in Computer science from the University of Constantine (Algeria). He is a lecturer at the University of Oum el Bouaghi (Algeria) since 2003. His current research interests are Software Engineering, Embedded Systems and Systems on Chip (SOC) design. Mohamed Benmohammed was born in Constantine, Algeria. He received his B.Sc. degree from the High School of Computer Science (C.E.R.I) Algiers, Algeria, in 1983, and the PhD degree in Computer Science from the University of Sidi Belabbes, Algeria, in 1997. Currently, he is an assistant Professor at University of Constantine. His current research interests are Parallel architectures and high level synthesis.