Practical UML Statecharts in C/C++ Event-Driven Programming for ...

556 downloads 258864 Views 185KB Size Report
Practical UML Statecharts in C/C++. Event-Driven Programming for. Embedded Systems. 2nd Edition. Miro Samek. AMSTERDAM . BOSTON . HEIDELBERG • ...
Practical UML Statecharts in C/C++ Event-Driven Programming for Embedded Systems 2nd Edition

Miro Samek

AMSTERDAM . BOSTON . HEIDELBERG • LONDON NEW YORK . OXFORD • PARIS • SAN DIEGO SAN FRANCISCO • SINGAPORE • SYDNEY • TOKYO ELSEVIER

Newnes is an imprint of Elsevier

Table of Contents Part I: Uml State Machines Chapter 1: Getting Started with UML State Machines and Event-Driven Programming. 1.1 Installing the Accompanying Code 1.2 Let's Play 1.2.1 Running the DOS Version 1.2.2 Running the Stellaris Version 1.3 The main() Function 1.4 The Design of the "Fly 'n' Shoot" Game 1.5 Active Objects in the "Fly 'n' Shoot" Game 1.5.1 The Missile Active Object 1.5.2 The Ship Active Object 1.5.3 The Tunnel Active Object 1.5.4 The Mine Components 1.6 Events in the "Fly 'n' Shoot" Game 1.6.1 Generating, Posting, and Publishing Events 1.7 Coding Hierarchical State Machines 1.7.1 Step 1: Defining the Ship Structure 1.7.2 Step 2: Initializing the State Machine 1.7.3 Step 3: Defining State-Handler Functions 1.8 The Execution Model 1.8.1 Simple Nonpreemptive "Vanilla" Scheduler 1.8.2 The QK Preemptive Kernel 1.8.3 Traditional OS/RTOS 1.9 Comparison to the Traditional Approach 1.10 Summary Chapter 2: A Crash Course in UML State Machines 2.1 The Oversimplification of the Event-Action Paradigm 2.2 Basic State Machine Concepts 2.2.1 States 2.2.2 State Diagrams

1

„..'

3 4 5 7 8 11 16 20 21 24 27 29 32 36 39 39 42 43 48 48 49 50 50 52 55 56 59 60 61

vi

Table of Contents

2.3

2.4

2.5

2.2.3 State Diagrams versus Flowcharts 2.2.4 Extended State Machines 2.2.5 Guard Conditions 2.2.6 Events 2.2.7 Actions and Transitions 2.2.8 Run-to-Completion Execution Model UML Extensions to the Traditional FSM Formalism 2.3.1 Reuse of Behavior in Reactive Systems 2.3.2 Hierarchically Nested States 2.3.3 Behavioral Inheritance 2.3.4 Liskov Substitution Principle for States 2.3.5 Orthogonal Regions 2.3.6 Entry and Exit Actions 2.3.7 Internal Transitions 2.3.8 Transition Execution Sequence 2.3.9 Local versus External Transitions 2.3.10 Event Types in the UML 2.3.11 Event Deferral 2.3.12 Pseudostates 2.3.13 UML Statecharts and Automatic Code Synthesis 2.3.14 The Limitations of the UML State Diagrams 2.3.15 UML State Machine Semantics: An Exhaustive Example Designing A UML State Machine 2.4.1 Problem Specification 2.4.2 High-Level Design 2.4.3 Scavenging for Reuse 2.4.4 Elaborating Composite States 2.4.5 Refining the Behavior 2.4.6 Final Touches Summary

Chapter 3: Standard State Machine Implementations

3.1 3.2 3.3

3.4

The Time-Bomb Example 3.1.1 Executing the Example Code A Generic State Machine Interface 3.2.1 Representing Events Nested Switch Statement 3.3.1 Example Implementation 3.3.2 Consequences 3.3.3 Variations of the Technique State Table 3.4.1 Generic State-Table Event Processor 3.4.2 Application-Specific Code

61 63 64 66 67 67 68 69 69 71 73 74 75 77 78 81 82 83 83 85 86 87 91 91 92 93 94 95 96 96 101

102 104 105 106 108 108 112 113 113 114 118

Table of Contents

vii

3.4.3 Consequences 3.4.4 Variations of the Technique Object-Oriented State Design Pattern 3.5.1 Example Implementation 3.5.2 Consequences 3.5.3 Variations of the Technique QEP FSM Implementation 3.6.1 Generic QEP Event Processor 3.6.2 Application-Specific Code 3.6.3 Consequences 3.6.4 Variations of the Technique General Discussion of State Machine Implementations 3.7.1 Role of Pointers to Functions 3.7.2 State Machines and C++ Exception Handling 3.7.3 Implementing Guards and Choice Pseudostates 3.7.4 Implementing Entry and Exit Actions Summary

122 123 124 126 130 131 132 133 137 142 143 144 144 145 145 146 146

Chapter 4: Hierarchical Event Processor Implementation

149

3.5

3.6

3.7

3.8 4.1 4.2 4.3

4.4

4.5

4.6

Key Features of the QEP Event Processor :'. QEP Structure 4.2.1 QEP Source Code Organization Events 4.3.1 Event Signal (QSignal) 4.3.2 QEvent Structure in C 4.3.3 QEvent Structure in C++ Hierarchical State-Handler Functions 4.4.1 Designating the Superstate ( Q _ S U P E R ( ) Macro) 4.4.2 Hierarchical State-Handler Function Example in C 4.4.3 Hierarchical State-Handler Function Example in C++ Hierarchical State Machine Class 4.5.1 Hierarchical State Machine in C (Structure QHsm) 4.5.2 Hierarchical State Machine in C++ (Class QHsm) 4.5.3 The Top State and the Initial Pseudostate 4.5.4 Entry/Exit Actions and Nested Initial Transitions 4.5.5 Reserved Events and Helper Macros in QEP 4.5.6 Topmost Initial Transition (QHsm_init ()) 4.5.7 Dispatching Events (QHsm_dispatch (), General Structure) 4.5.8 Executing a Transition in the State Machine (QHsm_dispatch (), Transition) Summary of Steps for Implementing HSMs with QEP 4.6.1 Step 1: Enumerating Signals 4.6.2 Step 2: Defining Events

150 152 153 154 154 155 157 158 158 158 160 161 162 163 164 166 168 170 174 177 183 185 185

viii

4.7

4.8 4.9

Table of Contents 4.6.3 4.6.4 4.6.5 4.6.6 4.6.7 4.6.8 4.6.9 4.6.10 Pitfalls 4.7.1 4.7.2 4.7.3 4.7.4 4.7.5

Step 3: Deriving the Specific State Machine Step 4: Defining the Initial Pseudostate Step 5: Defining the State-Handler Functions Coding Entry and Exit Actions Coding Initial Transitions Coding Internal Transitions Coding Regular Transitions Coding Guard Conditions to Avoid While Coding State Machines with QEP Incomplete State Handlers Ill-Formed State Handlers State Transition Inside Entry or Exit Action Incorrect Casting of Event Pointers Accessing Event Parameters in Entry/Exit Actions or Initial Transitions 4.7.6 Targeting a Nonsubstate in the Initial Transition 4.7.7 Code Outside the s w i t c h Statement 4.7.8 Suboptimal Signal Granularity 4.7.9 Violating the Run-to-Completion Semantics 4.7.10 Inadvertent Corruption of the Current Event Porting and Configuring QEP Summary

Chapter 5: State Patterns 5.1 Ultimate Hook 5.1.1 Intent 5.1.2 Problem 5.1.3 Solution 5.1.4 Sample Code 5.1.5 Consequences 5.2 Reminder 5.2.1 Intent 5.2.2 Problem 5.2.3 Solution 5.2.4 Sample Code 5.2.5 Consequences 5.3 Deferred Event 5.3.1 Intent 5.3.2 Problem 5.3.3 Solution 5.3.4 Sample Code 5.3.5 Consequences 5.3.6 Known Uses

186 188 188 189 189 190 190 190 191 192 193 193 194 194 195 196 197 198 198 199 201 203 205 205 205 206 207 211 211 211 212 212 213 218 219 219 219 220 222 229 230

Table of Contents

5.4

5.5

5.6

Orthogonal Component 5.4.1 Intent 5.4.2 Problem 5.4.3 Solution 5.4.4 Sample Code 5.4.5 Consequences 5.4.6 Known Uses Transition to History 5.5.1 Intent 5.5.2 Problem 5.5.3 Solution 5.5.4 Sample Code 5.5.5 Consequences 5.5.6 Known Uses Summary

Part II: Real-Time Framework

ix

230 230 230 231 234 243 244 245 245 245 245 246 250 251 251 ,

253

Chapter 6: Real-Time Framework Concepts .'. 6.1 Inversion of Control 6.2 CPU Management 6.2.1 Traditional Sequential Systems 6.2.2 Traditional Multitasking Systems 6.2.3 Traditional Event-Driven Systems 6.3 Active Object Computing Model 6.3.1 System Structure 6.3.2 Asynchronous Communication 6.3.3 Run-to-Completion 6.3.4 Encapsulation 6.3.5 Support for State Machines 6.3.6 Traditional Preemptive Kernel/RTOS 6.3.7 Cooperative Vanilla Kernel 6.3.8 Preemptive RTC Kernel 6.4 Event Delivery Mechanisms 6.4.1 Direct Event Posting 6.4.2 Publish-Subscribe 6.5 Event Memory Management 6.5.1 Copying Entire Events 6.5.2 Zero-Copy Event Delivery 6.5.3 Static and Dynamic Events 6.5.4 Multicasting Events and the Reference-Counting Algorithm 6.5.5 Automatic Garbage Collection 6.5.6 Event Ownership i

J1' if

255 256 257 257 259 263 266 267 269 269 269 271 273 274 276 279 280 281 282 282 284 286 286 287 288

"••'O "r j c o r i L ' S^JI

Table of Contents

6.6 6.7

6.8 6.9

6.5.7 Memory Pools Time Management 6.6.1 Time Events 6.6.2 System Clock Tick Error and Exception Handling 6.7.1 Design by Contract 6.7.2 Errors versus Exceptional Conditions 6.7.3 Customizable Assertions in C and C++ 6.7.4 State-Based Handling of Exceptional Conditions 6.7.5 Shipping with Assertions 6.7.6 Asserting Guaranteed Event Delivery Framework-Based Software Tracing Summary

Chapter 7: Real-Time Framework Implementation 7.1 Key Features of the QF Real-Time Framework 7.1.1 Source Code 7.1.2 Portability 7.1.3 Scalability '. 7.1.4 Support for Modern State Machines 7.1.5 Direct Event Posting and Publish-Subscribe Event Delivery 7.1.6 Zero-Copy Event Memory Management 7.1.7 Open-Ended Number of Time Events 7.1.8 Native Event Queues 7.1.9 Native Memory Pool 7.1.10 Built-in "Vanilla" Scheduler 7.1.11 Tight Integration with the QK Preemptive Kernel 7.1.12 Low-Power Architecture 7.1.13 Assertion-Based Error Handling 7.1.14 Built-in Software Tracing Instrumentation 7.2 QF Structure 7.2.1 QF Source Code Organization 7.3 Critical Sections in QF 7.3.1 Saving and Restoring the Interrupt Status 7.3.2 Unconditional Locking and Unlocking Interrupts 7.3.3 Internal QF Macros for Interrupt Locking/Unlocking 7.4 Active Objects 7.4.1 Internal State Machine of an Active Object 7.4.2 Event Queue of an Active Object 7.4.3 Thread of Execution and Active Object Priority 7.5 Event Management in QF 7.5.1 Event Structure 7.5.2 Dynamic Event Allocation

289 291 291 293 294 294 296 297 300 301 302 303 304 307 308 309 309 310 312 312 312 312 313 313 313 313 313 314 314 315 316 318 319 321 323 324 328 328 330 333 333 335

Table of Contents

7.6 7.7

7.8

7.9

7.10 7.11 7.12 7.13

7.5.3 Automatic Garbage Collection 7.5.4 Deferring and Recalling Events Event Delivery Mechanisms in QF 7.6.1 Direct Event Posting 7.6.2 Publish-Subscribe Event Delivery Time Management 7.7.1 Time Event Structure and Interface 7.7.2 The System Clock Tick and the QF_tick () Function 7.7.3 Arming and Disarming a Time Event Native QF Event Queue 7.8.1 The QEQueue Structure 7.8.2 Initialization of QEQueue 7.8.3 The Native QF Active Object Queue 7.8.4 The "Raw" Thread-Safe Queue Native QF Memory Pool 7.9.1 Initialization of the Native QF Memory Pool 7.9.2 Obtaining a Memory Block from the Pool 7.9.3 Recycling a Memory Block Back to the Pool Native QF Priority Set Native Cooperative "Vanilla" Kernel 7.11.1 The q v a n i l l a . c Source File 7.11.2 The q v a n i l l a . h Header File QP Reference Manual Summary

Chapter 8: Porting and Configuring QF 8.1 The QP Platform Abstraction Layer 8.1.1 Building QP Applications 8.1.2 Building QP Libraries 8.1.3 Directories and Files 8.1.4 The q e p _ p o r t . h Header File 8.1.5 The q f _ p o r t . h Header File Types of Platform-Specific QActive Data Members Base Class for Derivation of QActive The Maximum Number of Active Objects in the Application Various Object Sizes Within the QF Framework QF Critical Section Mechanism Include Files Used by this QF Port Interface Used Only Inside QF, But Not in Applications Active Object Event Queue Operations QF Event Pool Operations 8.1.6 The q f _ p o r t . c Source File 8.1.7 The q p _ p o r t . h Header File I—

xi

339 341 343 343 344 351 351 354 356 359 360 362 362 367 369 372 375 376 377 379 380 384 386 387 389 390 390 391 392 398 400 402 402 403 403 404 404 405 406 406 407 411

xii

8.2

8.3

8.4

8.5

Table of Contents

8.1.8 Platform-Specific QF Callback Functions 8.1.9 System Clock Tick (Calling QF_tick()) 8.1.10 Building the QF Library Porting the Cooperative "Vanilla" Kernel 8.2.1 The q e p _ p o r t . h Header File 8.2.2 The q f _ p o r t . h Header File 8.2.3 The System Clock Tick (QF_tick()) 8.2.4 Idle Processing (QF_onidle ()) QF Port to uC/OS-II (Conventional RTOS) 8.3.1 The q e p _ p o r t . h Header File 8.3.2 The q f _ p o r t . h Header File 8.3.3 The q f _ p o r t . c Source File 8.3.4 Building the uC/OS-II Port 8.3.5 The System Clock Tick (QF_tick ()) 8.3.6 Idle Processing QF Port to Linux (Conventional POSIX-Compliant OS) 8.4.1 The q e p _ p o r t . h Header File 8.4.2 The q f _ p o r t . h Header File 8.4.3 The q f _ p o r t . c Source File Summary

Chapter 9: Developing QP Application 9.1 Guidelines for Developing QP Applications 9.1.1 Rules 9.1.2 Heuristics 9.2 The Dining Philosopher Problem 9.2.1 Step 1: Requirements 9.2.2 Step 2: Sequence Diagrams 9.2.3 Step 3: Signals, Events, and Active Objects 9.2.4 Step 4: State Machines 9.2.5 Step 5: Initializing and Starting the Application 9.2.6 Step 6: Gracefully Terminating the Application 9.3 Running DPP on Various Platforms 9.3.1 "Vanilla" Kernel on DOS 9.3.2 "Vanilla" Kernel on Cortex-M3 9.3.3 uC/OS-II 9.3.4 Linux 9.4 Sizing Event Queues and Event Pools 9.4.1 In Sizing Event Queues 9.4.2 Sizing Event Pools 9.4.3 System Integration 9.5 Summary

412 413 413 414 414 415 417 418 420 422 423 425 430 430 431 431 432 432 435 441 443 444 444 445 446 447 447 449 451 457 460 461 461 465 469 472 476 477 479 480 480

Table of Contents Chapter 10: Preemptive Run-to-Completion Kernel 10.1 Reasons for Choosing a Preemptive Kernel 10.2 Introduction to RTC Kernels 10.2.1 Preemptive Multitasking with a Single Stack 10.2.2 Nonblocking Kernel 10.2.3 Synchronous and Asynchronous Preemptions 10.2.4 Stack Utilization 10.2.5 Comparison to Traditional Preemptive Kernels 10.3 QK Implementation 10.3.1 QK Source Code Organization 10.3.2 The q k . h Header File 10.3.3 Interrupt Processing 10.3.4 The qk_sched.c Source File (QK Scheduler) 10.3.5 The q k . c Source File (QK Startup and Idle Loop) 10.4 Advanced QK Features 10.4.1 Priority-Ceiling Mutex 10.4.2 Thread-Local Storage .10.4.3 Extended Context Switch (Coprocessor Support) 10.5 Porting QK 10.5.1 The q e p _ p o r t . h Header File 10.5.2 The q f _ p o r t . h Header File 10.5.3 The q k _ p o r t . h Header File 10.5.4 Saving and Restoring FPU Context 10.6 Testing the QK Port 10.6.1 Asynchronous Preemption Demonstration 10.6.2 Priority-Ceiling Mutex Demonstration 10.6.3 TLS Demonstration 10.6.4 Extended Context Switch Demonstration 10.7 Summary

xiii 483 483 485 486 487 487 491 494 496 497 498 503 506 511 514 515 518 520 524 525 525 526 531 531 531 535 536 539 540

Chapter 11: Software Tracing for Event-Driven Systems 54 7 11.1 Software Tracing Concepts 542 11.2 Quantum Spy Software-Tracing System 544 11.2.1 Example of a Software-Tracing Session 545 11.2.2 The Human-Readable Trace Output 547 11.3 QS Target Component 550 11.3.1 QS Source Code Organization 552 11.3.2 The QS Platform-Independent Header Files qs . h and qs_dummy. h.. 553 11.3.3 QS Critical Section 560 11.3.4 General Structure of QS Records 561 11.3.5 QS Filters 562 Global On/Off Filter 562

xiv

Table of Contents

Local Filters QS Data Protocol Transparency Endianness 11.3.7 QS Trace Buffer Initializing the QS Trace Buffer QS_initBuf () Byte-Oriented Interface: QS_getByte() Block-Oriented Interface: QS_getBlock() 11.3.8 Dictionary Trace Records Object Dictionaries Function Dictionaries Signal Dictionaries 11.3.9 Application-Specific QS Trace Records 11.3.10 Porting and Configuring QS The QSPY Host Application 11.4.1 Installing QSPY 11.4.2 Building QSPY Application from Sources Building QSPY for Windows with Visual C++ 2005 Building QSPY for Windows with MinGW Building QSPY for Linux 11.4.3 Invoking QSPY Exporting Trace Data to MATLAB 11.5.1 Analyzing Trace Data with MATLAB 11.5.2 MATLAB Output File 11.5.3 MATLAB Script qspy.m 11.5.4 MATLAB Matrices Generated by qspy.m Adding QS Software Tracing to a QP Application 11.6.1 Initializing QS and Setting Up the Filters 11.6.2 Defining Platform-Specific QS Callbacks 11.6.3 Generating QS Timestamps with the QS_onGetTime () Callback 11.6.4 Generating QS Dictionary Records from Active Objects 11.6.5 Adding Application-Specific Trace Records 11.6.6 "QSPY Reference Manual" Summary 11.3.6

11.4

11.5

11.6

11.7

Chapter 12: QP-nano: How Small Can You Co? 12.1 Key Features of QP-nano 12.2 Implementing the "Fly 'n' Shoot" example with QP-nano 12.2.1 The main () function .12.2.2 The q p n _ p o r t . h Header File o@®000

564 566 567 568 569 569 571 573 574 575 577 577 578 580 581 582 584 584 584 584 585 587 587 589 590 593 596 596 598 601 604 607 608 608 611 612 614 615 618

Table of Contents

Signals, Events, and Active Objects in the "Fly 'n' Shoot" Game 12.2.4 Implementing the Ship Active Object in QP-nano 12.2.5 Time Events in QP-nano 12.2.6 Board Support Package for "Fly 'n' Shoot" Application in QP-nano 12.2.7 Building the "Fly 'n' Shoot" QP-nano Application 12.3 QP-nano Structure 12.3.1 QP-nano Source Code, Examples, and Documentation 12.3.2 Critical Sections in QP-nano Task-Level Interrupt Locking ISR-Level Interrupt Locking 12.3.3 State Machines in QP-nano 12.3.4 Active Objects in QP-nano 12.3.5 The System Clock Tick in QP-nano 12.4 Event Queues in QP-nano 12.4.1 The Ready-Set in QP-nano (QF_readySet_) 12.4.2 Posting Events from the Task Level (QActive_post ()) 12.4.3 Posting Events from the ISR Level (QActive_postlSR()) 12.5 The Cooperative "Vanilla" Kernel in QP-nano 12.5.1 Interrupt Processing Under the "Vanilla" Kernel 12.5.2 Idle Processing under the "Vanilla" Kernel 12.6 The Preemptive Run-to-Completion QK-nano Kernel 12.6.1 QK-nano Interface qkn.h 12.6.2 Starting Active Objects and the QK-nano Idle Loop 12.6.3 The QK-nano Scheduler 12.6.4 Interrupt Processing in QK-nano 12.6.5 Priority Ceiling Mutex in QK-nano 12.7 The PELICAN Crossing Example 12.7.1 PELICAN Crossing State Machine 12.7.2 The Pedestrian Active Object 12.7.3 QP-nano Port to MSP430 with QK-nano Kernel 12.7.4 QP-nano Memory Usage 12.8 Summary

xv

12.2.3

Appendix A. Licensing Policy for QP and QP-nano A.I Open-Source Licensing A.2 Closed-Source Licensing A.3 Evaluating the Software A.4 NonProfits, Academic Institutions, and Private Individuals A.5 GNU General Public License Version 2

620 622 626 628 630 631 633 634 635 635 637 640 642 644 645 646 ...649 650 655 655 655 656 658 660 665 666 666 668 671 672 675 678 679 679 680 680 680 681

XVI

Table of Contents

Appendix B. Guide to Notation B.I Class Diagrams B.2 State Diagrams B.3 Sequence Diagrams B.4 Timing Diagrams

685 685 688 689 690

Bibliography Index

693 699

o

Suggest Documents