Capturing Real-Time Requirements - Embedded.com

Capturing Real-Time Requirements

Requirements are too often co-mingled with design elements. Here's a way to focus on capturing only the essentials, with UML.

Many developers regard requirements capture with a disdain normally reserved for Windows crashes and Richard Simmons exercise videos. They see it as a waste of time that diverts them from what they ought to be doing: cranking out code. However, in a requirements-driven process, the developers always know that what they're doing actually relates to the goals and purposes of the system.

To properly understand what features ought to be designed and implemented, as well as how they ought to work, it is necessary to have a deep understanding of the following concepts: the purposes of the system; the workflow of the user (if applicable) with respect to the system; the set of features the system provides; the devices with which it must interact and how those interactions take place; what should happen when something expected or “bad” occurs; and exactly how the features must be visible to the user and the external devices. These are all part of the requirements or specification of the system. If you understand the requirements thoroughly, your development work will be more productive, you'll have less reworking to do, and your customers will be happy.

In a requirements-centric development, all work relates in some way to the requirements specification of the system. Early in analysis, we try to understand how the system fits into its environment (including the user). Soon, we're detailing exactly which features we want the system to provide to optimize its work in that environment and exactly how we want those features to appear to elements in the system's environment. Later, we design the internals of the system to meet those specifications, and finally we construct test vectors from the system to ensure the appropriate level of completeness, fidelity, and accuracy.

I find that real-time and embedded developers often have difficulty in separating requirements from design. The chosen design is usually just one of several ways of meeting the requirements. Many bright and experienced developers have had the design aspect so ingrained in them that they find this distinction elusive. I have developed an approach for understanding, capturing, and manipulating requirements based on my work with complex projects at NASA and elsewhere, which is the focus of this article. This approach is part of the ROPES process.[1]

Types of requirements

Just as there are two kinds of people (those who divide people into two kinds and those who don't), there are two kinds of requirements: functional and quality of service. Functional requirements encompass what the system should do and how it should behave in a variety of circumstances. For example:

  • The system shall adjust the angle of the telescope under user control.
  • The system shall deliver anesthetic agents in gaseous form at a desired concentration.
  • Locking clamps shall engage when the elevator cable breaks.
  • The device shall alarm if the heart rate falls below 30 beats per minute.

Quality of service (QoS) requirements specify how well a functional requirement shall be accomplished. In real-time and embedded systems, QoS requirements may specify properties of the system (for example, range, speed, throughput, capacity, reliability, maintainability, evolvability, time to market, safety, predictability, schedulability), or properties of the process. As a rule of thumb, if it's something that can be quantified or optimized, then it is a QoS requirement. For example (QoS requirements italicized):

  • The angle of the telescope shall be set in units of 0.1 degrees with a maximum error of 0.01 degrees.
  • The anesthetic agent shall be controllable from 0.00% to 5.00% by volume in units of 0.01% with an accuracy of 0.005%.
  • Locking clamps shall engage in the event of an elevator support cable breakage within less than 0.5 seconds.
  • The device shall alarm within 10 seconds if the heart rate falls below 30 beats per minute.

The defining characteristic of real-time systems is the level to which QoS timing requirements figure into the correctness of the system. In non-real-time systems, late is acceptable. In real-time systems, late is unacceptable. Put another way, a real-time system is not necessarily fast, but it is predictably timely. Of course, real-time systems may be hard real-time, which means that responses to events for aperiodic systems or actions taken when a periodic task begins (time-driven systems) must complete by a specified deadline.

Systems may also be soft real-time. For example:

  • Event responses shall be handled on average within a certain time.
  • A certain number of event responses shall be handled within a specified timeframe.
  • A specified failure rate is permitted.

Because the mathematics required to analyze soft real-time systems is more difficult than for the simpler, hard real-time case, it is very common to treat soft real-time systems as hard real-time to simplify the analysis.[2] The result of this approach is an overdesign of the system, with, typically, an increase in the recurring cost due to the overdesigned hardware platform.

In my approach, functional requirements are modeled as use cases, descriptive specifications, actions, and message sequences. QoS requirements are modeled as constraints of some kind, applied against one or more functional requirements.

Use cases

A use case is a named coherent collection of related requirements organized around system capability. A use case is large-scale, typically corresponding to three to 10 pages of textual requirements. Use cases define little in the way of specific requirements per se, but they serve as a way to organize and manage them. A good use case:

  • Focuses on the user's or actor's perspective of the system (not the implementation of its interfaces or its internals)
  • Captures a closely related set of requirements
  • Returns a result visible to one or more actors
  • Does not reveal or imply system internal structure or implementation
  • Is independent from other use cases and may be concurrent with them
  • Consists of a set of messages exchanged between the system and one or more actors (more than just one!)

Relationships among use cases can be used, but there's a caveat: many newcomers to use case modeling use these relationships to do a functional decomposition of the system's internal structure; this is not what they are for! The purpose of use case relations is to depict relations among these clumps of requirements. The most common relations are specializations (stereotypes, to be specific) of the dependency relation (shown using a dashed line with an open arrowhead). The <> relation means that a larger use case includes a smaller one. For example, a use case for a spacecraft might be “Take a picture of a planet” and another might be “Send information to Earth-side Station.” Executing each of these use cases involves rolling the spacecraft to a specific orientation-either to point the camera at the planet or to aim the antenna at the Earth. Thus, they could both <> a smaller use case, such as “Adjust Attitude.”

<> is similar to <> except that the smaller use case is optional and only used in certain situations. For example, suppose that certain commands sent to a spacecraft could potentially lead to a loss of the spacecraft. You might want user validation and authorization guaranteed before accepting such commands. In this case, the larger “Process Ground Command” use case might be extended by a “Validate User.”

Additionally, one use case may be more general or specific than another. For example, there may be multiple ways to do a Validate User use case: Validate by Authorization Code, Validate by Fingerprint Scan, or Validate by Voice Recognition. Each of these is a specialized form of the general Validate User use case.

We will use these relations in a very specific way when we capture requirements for large complex systems.

Detailed requirements

Since a use case is a container of detailed requirements, just providing the name of the use case isn't enough. We need to provide the details. In the ROPES process we call this “detailing the use case.”

There are two primary means to detail a use case-by example or by specification. By far, the most common is by example. This is done by constructing sets of scenarios of message exchange between the system and the actors that are associated with that use case. This approach has advantages and disadvantages. The advantages include the simplicity of the representation and the ease with which non-technical stakeholders can understand how the system operates with respect to the use case. The disadvantages include the fact that a use case can be represented by an infinite set of scenarios; the number that is actually captured must be trimmed down somehow. Also, there is typically no way to say “and not” when you give an example. That is, there is no way to specify prohibited behaviors.

Detailing a use case by specification gets around these disadvantages. It provides a single location for the details that applies to each of the infinite set of scenarios. It can also state prohibitions as requirements. On the downside, particularly when formal languages (such as statecharts) are used to specify requirements, a three-digit IQ is required, which may disallow certain managers and marketers from understanding the requirements. My recommendation is to generally use both, as we will see later.

Scenarios and message sequence charts

A scenario is a specific path through a use case. The most common representation of a scenario is a message sequence chart, as shown in Figure 1. The vertical lines are called instance lines, and at the system specification level, they represent the actors and either the use case or the system fulfilling the role of the use case. I prefer to use the use case because it helps me identify the context of the particular scenario. Note that at this level, we do not include objects inside the system. Looking ahead, later we will add internal objects to our scenarios to show how our designs actually meet our requirements, but they should not be on the system-level use case scenarios. The goal at this point is to capture requirements, not design.

A typical system might have anywhere from half a dozen to a dozen use cases, and each use case might have half a dozen to several dozen scenarios of interest. Since there is an infinite set from which the scenarios can be drawn, how do you decide which ones to explicitly represent? The ROPES process guideline is simple: add scenarios to a use case only when they demonstrate or depict at least one or more new requirements. You're done when you can't come up with any scenarios that add a new requirement.

Functional requirements are shown on sequence diagrams as ordered message sequences. That is, you're showing that a particular sequence of messages must be allowed. If the order within a message set is unimportant, you can attach a constraint {unordered} to the set of messages. QoS requirements are shown as constraints that attach to the instance lines, individual messages, or to sets of messages. The most common constraints are timeliness constraints, typically applied to an ordered pair of messages. In Figure 1, a timing constraint is shown down at the bottom using a common notation: a vertical line between two horizontal bars marking points in time on the scenario. Other QoS constraints are shown in note boxes on the right of the diagram.

Specifications for requirements capture

The other primary approach to detailing requirements is to do it by specification. Either informal or formal languages can be used, or a combination of the two. By informal languages, we usually mean written specifications. Some authors have elaborate fields used to specify the use case. For example, Schneider and Winters suggest:[3]

  • Use case name
  • Actors
  • Priority (project)
  • Status (project)
  • Preconditions
  • Postconditions
  • Extension points
  • Included use cases
  • Flow of events
  • List of related diagrams (sequence, statechart, activity, and so on)

Of these, I feel only the preconditions and postconditions are required. The other things are shown using other views (such as the diagrams themselves).

For formal languages, the UML provides the statechart and its cousin, the activity chart. Statecharts are most applicable when the use case has states, which are distinguishable conditions of existence as defined by a set of events or messages accepted, behaviors performed, and reachability of subsequent states. When the use case is in State A, it accepts a certain set of messages and events, has a certain set of behaviors, and can reach a finite set of other states. It is distinguishable from other such states in that one or more of these things is different. When an autopilot is executing “Controlling Flight Path,” there are certain things it can and cannot do when taking off vs. when in cruise or landing states.

Activity charts are just a specialized form of a statechart. Activity charts are used when the primary means to transition from one state to the next depends on completion of the actions executed within a state rather than upon receipt of an explicit message or event from somewhere else.

Consider a soda pop machine with two actors (the Customer and the Service Rep). Let's focus on a Deliver Soda Can use case. It is difficult to imagine drawing individually all the possible ways in which users might insert coins and press buttons to get a can of soda from the machine, even without the ability to adjust the price. However, it is relatively straightforward to do so using a statechart, as shown in Figure 2.

The statechart in the figure has only four states to manage the transaction of the user inserting coins and selecting the desired flavor of soda.[4] All the actions directly relevant to the specification of the use case are shown on the statechart (although not their implementation). Notice also that no internal objects are identified, but some data are: specifically, amt tracks how much the user has entered, and AMOUNT_REQUIRED is the cost of a single can of soda. There are various operations used within the actions, but it isn't at all implied what objects there are or how they relate to each other.

In fact, a set of objects will realize this use case (that's UML-speak for “implement”). All that we can be sure of is that, in any correct design, the objects specified will collectively be able to provide the services as specified by the statechart in Figure 2.

In the final analysis, either statecharts or activity diagrams can be used for specification of requirements.

Relating specifications and scenarios

When you use a formal language, such as statecharts, to specify a use case, you are capturing the entire infinite set of scenarios all in one place. A scenario is nothing more than a particular path through the statechart. For example, Figure 3 shows one particular scenario represented by this statechart. In this scenario, the cost of the soda is 55 cents. The customer puts in two quarters and a dime, and receives a nickel in change. Then he selects Coke, but there is no more Coke, and the machine displays a message to that effect. The customer then selects Diet Coke and the system delivers it. Notice that some of the relevant states in the state machine are shown on the use case instance line-this aids the reader in relating the scenario back to the statechart specification.

Of course, there are other paths through the statechart; these are simply other scenarios. In general, you will want to construct the set of scenarios from the statechart. You do this by making a different scenario for every different path through the statechart, although you'll only want to do the looping paths once, and representative examples of the concurrent regions (and-states).

Moving from requirements into design

As mentioned earlier, a use case is ultimately realized by a set of objects working together to provide the necessary behavior. This set of objects is called a collaboration in UML. It has a specific notation (a dashed oval) that is not commonly used. Most often, the use case collaboration is shown as a class diagram, showing the relevant classes of the objects that participate, at run-time, in the collaboration.

Getting a good set of objects can be tricky, as it is not at all obvious from the use case model. In the ROPES process, you use object identification strategies to identify the object participating in the collaboration. The ROPES process identifies about a dozen different strategies which, while different and distinct, overlap in the objects they find to a significant degree. Commonly, you will apply three or four different strategies simultaneously to identify all objects in the collaboration. Using such an approach, one could come up with an object model such as that shown in Figure 4.

The really important question is how do you ensure and demonstrate that the design model really does realize the use case model? The answer is through execution. We evaluate and demonstrate the adequacy of a design model by executing that design model. Specifically, we want to execute the very same scenarios we used to state requirements using the newly added design elements. If the design can execute all of the requirements then we've done a good job. If it can't, then we need to fix our design model.

To do this, first we come up with a set of scenarios at the system use case level where the players are the actors and The System (or the system playing the role of a specific use case). To test the adequacy of a design, we do the same scenarios but now we add the design elements to the scenario and show the exact exchange of messages among them necessary to fully realize the behavior.

Figure 5 is the same scenario shown in Figure 3, but we've added the collaborative elements from the class diagram.[5] We can walk through the scenario and see how the objects inside the system collaborate to realize it.

This approach of validation via execution can be applied at any level of abstraction. As we add more detail to a highly complex system with subsystems, components, and composite classes, we can be sure that we're doing a good job when, at the specified level of abstraction, it executes all of the scenarios defined for the use case.

This approach means that even if you use statecharts to specify requirements, you will also draw scenarios. Since a statechart can have loops and concurrent regions, it can represent an infinite set of scenarios. Which scenarios should you draw and trace through the levels of design abstraction? Again, the ROPES process has an answer: draw a scenario for every non-looping path through the or-states and each looping path exactly once; for concurrent regions, do some representative interleaving of the concurrent regions.

Caught

We've discussed an approach to the capturing of requirements for real-time and embedded systems that has been shown to be effective in projects both large and small. Use cases, as defined in the UML, are used as an organizing principle for managing requirements and a combination of scenarios and specifications to capture the detailed requirements. For large systems, a system engineering phase captures the subsystem architecture and maps the level-0 use cases onto it. This allows teams to go off and work independently with some assurance that their subsystems will properly fit into the system when they're all done.

Bruce Powel Douglass has over 20 years of experience designing safety-critical real-time applications in a variety of hard real-time environments. He has authored several books, including Real-Time UML and Doing Hard Time: Developing Real-Time Systems with UML, Objects, Frameworks and Patterns. Bruce worked with methodologists at Rational and other companies to develop the UML specification. Bruce is Chief Evangelist at I-Logix, and can be reached at bpd@ilogix.com.

Endnotes

1. Douglass, Bruce Powel. “On the ROPES,” Embedded Systems Programming, December 2000, p. 140.
Back

2. There is the old saying that hard real-time systems are hard, but soft real-time systems are harder, meaning that the accurate characterization of soft real-time systems is technically much more difficult.
Back

3. Schneider, Geri and Jason P. Winters. Applying Use Cases: A Practical Guide. New York: Addison-Wesley, 1998.
Back

4. Douglass, Bruce Powel. “UML Statecharts,” Embedded Systems Programming, January 1999, p. 22.
Back

5. Remember that scenarios show instances, not types (that is, classes). So the scenario has two different Rack instances, for example, but the class diagram shows only a single Rack class.
Back

Return to November 2001 Table of Contents

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.