Download Real-Time and Embedded Guide
Transcript
Chapter 14. Design principles measurable; one components steers the inputs to “actuators” that can change the value of (some of) the relevant physical signals. Chapter 16 gives a more detailed description. The control application is used in the following Chapters to illustrate the theory of good design with practical examples. 14.2. Loose coupling Only the simplest of applications can be programmed as one single task, with nothing else but straightforward communication with peripheral hardware. Most other projects need multiple components, and hence synchronization and data exchange between them. Many developers make the mistake of putting too much coupling between their components. That means that, in the implementation of one software component, they use knowledge about the implementation internals of other software components, or about the specific architecture (and operating system!) in which the components are used. Some typical examples are: • A task in one component suspends and restarts a task in another component. This implicitly means that the first task knows (or rather, pretends to know!) when it is appropriate and safe to influence the activities of the other task. • Component A uses a finite state machine to structure its internal working; component B bases its interaction with component A on deciding which state it wants A to go to. This means that the implementation of A cannot be changed without changing B also. • Task X delays itself during 100 milliseconds, in order to allow task Y to be scheduled and get started. This means that the “proper” synchronization of X and Y depends on platform and configuration dependent timing; and this timing tends to change drastically when functionality is added, or hardware is updated. • There is only one interrupt service routine in the application, and the programmer doesn’t do the effort of splitting its implementation into a real ISR (that does nothing but the really essential processing) and a DSR (that takes care of the further processing of the interrupt, Section 3.4). Again, this is a software structure that is error prone when updating the system or migrating it to other hardware. • Application programmer S raises the priority of a task, because it doesn’t run “fast enough” within the current situation. She also switches to priority inheritance (Section 4.8) for some of the system’s critical section. Her colleague H adds his part of the system, and also feels the need to raise the priorities of “his” tasks and critical sections. This phenomenon reflects an implicit use of knowledge about the operating system, and, in practice, often leads to a race that eventually ends in most tasks running at the highest priorities, which, of course, reduces the usefulness of priority-based scheduling. (TODO: make this list of bad examples as exhaustive as possible.) The solution to these coupling problems is, of course, quite simple: avoid every form of coupling. Or rather, strive for loose coupling, because complete decoupling is only possible for tasks that have nothing to do with each other. This loose coupling advice, however, is difficult to translate into concrete guidelines. It’s one of these things that make good (software) engineering stand out from the rest, and makes program design into an “art”. Understanding what exactly causes the problems in the list of examples given above, is already a good beginning; as is understanding the relevant software patterns in 133