123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546 |
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE section PUBLIC "-//Boost//DTD BoostBook XML V1.1//EN"
- "http://www.boost.org/tools/boostbook/dtd/boostbook.dtd">
- <section id="safe_numerics.safety_critical_embedded_controller">
- <title>Safety Critical Embedded Controller</title>
- <?dbhtml stop-chunking?>
- <para>Suppose we are given the task of creating stepper motor driver
- software to drive a robotic hand to be used in robotic micro surgery. The
- processor that has been selected by the engineers is the <ulink
- url="http://www.microchip.com/wwwproducts/en/PIC18F2520">PIC18F2520</ulink>
- manufactured by <ulink url="http://www.microchip.com">Microchip
- Corporation</ulink>. This processor has 32KB of program memory. On a
- processor this small, it's common to use a mixture of 8, 16, and 32 bit data
- types in order to minimize memory footprint and program run time. The type
- <code>int</code> has 16 bits. It's programmed in C. Since this program is
- going to be running life critical function, it must be demonstrably correct.
- This implies that it needs to be verifiable and testable. Since the target
- micro processor is inconvenient for accomplishing these goals, we will build
- and test the code on the desktop.</para>
- <section>
- <title>How a Stepper Motor Works</title>
- <figure float="0">
- <title>Stepper Motor</title>
- <mediaobject>
- <imageobject>
- <imagedata align="left" contentwidth="216"
- fileref="StepperMotor.gif" format="GIF" width="50%"/>
- </imageobject>
- </mediaobject>
- </figure>
- <para>A stepper motor controller emits a pulse which causes the motor to
- move one step. It seems simple, but in practice it turns out to be quite
- intricate to get right as one has to time the pulses individually to
- smoothly accelerate the rotation of the motor from a standing start until
- it reaches the some maximum velocity. Failure to do this will either limit
- the stepper motor to very low speed or result in skipped steps when the
- motor is under load. Similarly, a loaded motor must be slowly decelerated
- down to a stop.</para>
- <para><figure>
- <title>Motion Profile</title>
- <mediaobject>
- <imageobject>
- <imagedata fileref="stepper_profile.png" format="PNG" width="100%"/>
- </imageobject>
- </mediaobject>
- </figure></para>
- <para>This implies the the width of the pulses must decrease as the motor
- accelerates. That is the pulse with has to be computed while the motor is
- in motion. This is illustrated in the above drawing. A program to
- accomplish this might look something like the following:</para>
- <literallayout class="normal" linenumbering="unnumbered">setup registers and step to zero position
- specify target position
- set initial time to interrupt
- enable interrupts
- On interrupt
- if at target position
- disable interrupts and return
- calculate width of next step
- change current winding according to motor direction
- set delay time to next interrupt to width of next step</literallayout>
- <para>Already, this is turning it to a much more complex project than it
- first seemed. Searching around the net, we find a popular <ulink
- url="../../example/stepper-motor.pdf">article</ulink> on the operation of
- stepper motors using simple micro controllers. The algorithm is very well
- explained and it includes complete <ulink url="../../example/motor.c">code
- we can test</ulink>. The engineers are still debugging the prototype
- boards and hope to have them ready before the product actually ships. But
- this doesn't have to keep us from working on our code.</para>
- </section>
- <section>
- <title>Updating the Code</title>
- <para>Inspecting this <ulink url="../../example/motor.c">code</ulink>, we
- find that it is written in a dialect of C rather than C itself. At the
- time this code was written, conforming versions of the C compiler were not
- available for PIC processors. We want to compile this code on the <ulink
- url="http://ww1.microchip.com/downloads/en/DeviceDoc/50002053G.pdf">Microchip
- XC8 compiler</ulink> which, for the most part, is standards conforming. So
- we made the following minimal changes:</para>
- <para><itemizedlist>
- <listitem>
- <para>Factor into <ulink
- url="../../example/motor1.c">motor1.c</ulink> which contains the
- motor driving code and <ulink
- url="../../example/motor_test1.c">motor_test1.c</ulink> which tests
- that code.</para>
- </listitem>
- <listitem>
- <para>Include header <code><xc.h></code> which contains
- constants for the <ulink
- url="http://www.microchip.com/wwwproducts/en/PIC18F2520">PIC18F2520</ulink>
- processor</para>
- </listitem>
- <listitem>
- <para>Include header <code><stdint.h></code> to include
- standard Fixed width integer types.</para>
- </listitem>
- <listitem>
- <para>Include header <code><stdbool.h></code> to include
- keywords true and false in a C program.</para>
- </listitem>
- <listitem>
- <para>The original has some anomalies in the names of types. For
- example, int16 is assumed to be unsigned. This is an artifact of the
- original C compiler being used. So type names in the code were
- altered to standard ones while retaining the intent of the original
- code.</para>
- </listitem>
- <listitem>
- <para>Add in missing <code>make16</code> function.</para>
- </listitem>
- <listitem>
- <para>Format code to personal taste.</para>
- </listitem>
- <listitem>
- <para>Replaced enable_interrupts and disable_interrupts functions
- with appropriate PIC commands.</para>
- </listitem>
- </itemizedlist></para>
- <para>The resulting program can be checked to be identical to the original
- but compiles on with the Microchip XC8 compiler. Given a development
- board, we could hook it up to a stepper motor, download and boot the code
- and verify that the motor rotates 5 revolutions in each direction with
- smooth acceleration and deceleration. We don't have such a board yet, but
- the engineers have promised a working board real soon now.</para>
- </section>
- <section>
- <title>Refactor for Testing</title>
- <para>In order to develop our test suite and execute the same code on the
- desktop and the target system we factor out the shared code as a separate
- module which will used in both environments without change. The shared
- module <ulink url="../../example/motor2.c"><code><ulink
- url="../../example/motor1.c">motor2.c</ulink></code></ulink> contains the
- algorithm for handling the interrupts in such a way as to create the
- smooth acceleration we require.</para>
- <literallayout> <ulink url="../../example/motor2.c"><code><ulink
- url="../../example/motor_test2.c">motor_test2.c</ulink></code></ulink> <ulink
- url="../../example/motor2.c"><code><ulink
- url="../../example/example92.cpp">example92.cpp</ulink></code></ulink>
- #include ... #include ...
- PIC typedefs ... desktop types ...
- \ /
- \ /
- #include <ulink url="../../example/motor2.c"><code><ulink
- url="../../example/motor2.c">motor2.c</ulink></code></ulink>
- / \
- / \
- PIC test code desktop test code</literallayout>
- </section>
- <section>
- <title>Compiling on the Desktop</title>
- <para>Using the target environment to run tests is often very difficult or
- impossible due to limited resources. So software unit testing for embedded
- systems is very problematic and often skipped. The C language on our
- desktop is the same used by the <ulink
- url="http://www.microchip.com/wwwproducts/en/PIC18F2520">PIC18F2520</ulink>.
- So now we can also run and debug the code on our desktop machine. Once our
- code passes all our tests, we can download the code to the embedded
- hardware and run the code natively. Here is a program we use on the
- desktop to do that:</para>
- <programlisting><xi:include href="../../example/example92.cpp"
- parse="text" xmlns:xi="http://www.w3.org/2001/XInclude"/></programlisting>
- <para>Here are the essential features of the desktop version of the test
- program.<orderedlist>
- <listitem>
- <para>Include headers required to support safe integers.</para>
- </listitem>
- <listitem>
- <para>Specify a <link
- linkend="safe_numerics.promotion_policy">promotion policy</link> to
- support proper emulation of PIC types on the desktop.</para>
- <para>The C language standard doesn't specify sizes for primitive
- data types like <code>int</code>. They can and do differ between
- environments. Hence, the characterization of C/C++ as "portable"
- languages is not strictly true. Here we choose aliases for data
- types so that they can be defined to be the same in both
- environments. But this is not enough to emulate the <ulink
- url="http://www.microchip.com/wwwproducts/en/PIC18F2520">PIC18F2520</ulink>
- on the desktop. The problem is that compilers implicitly convert
- arguments of C expressions to some common type before performing
- arithmetic operations. Often, this common type is the native
- <code>int</code> and the size of this native type is different in
- the desktop and embedded environment. Thus, many arithmetic results
- would be different in the two environments.</para>
- <para>But now we can specify our own implicit promotion rules for
- test programs on the development platform that are identical to
- those on the target environment! So unit testing executed in the
- development environment can now provide results relevant to the
- target environment.</para>
- </listitem>
- <listitem>
- <para>Define PIC integer type aliases to be safe integer types of he
- same size.</para>
- <para>Code tested in the development environment will use safe
- numerics to detect errors. We need these aliases to permit the code
- in <ulink url="../../example/motor2.c">motor2.c</ulink> to be tested
- in the desktop environment. The same code run in the target system
- without change.</para>
- </listitem>
- <listitem>
- <para>Emulate PIC features on the desktop.</para>
- <para>The PIC processor, in common with most micro controllers these
- days, includes a myriad of special purpose peripherals to handle
- things like interrupts, USB, timers, SPI bus, I^2C bus, etc.. These
- peripherals are configured using special 8 bit words in reserved
- memory locations. Configuration consists of setting particular bits
- in these words. To facilitate configuration operations, the XC8
- compiler includes a special syntax for setting and accessing bits in
- these locations. One of our goals is to permit the testing of the
- identical code with our desktop C++ compiler as will run on the
- micro controller. To realize this goal, we create some C++ code
- which implements the XC8 C syntax for setting bits in particular
- memory locations.</para>
- </listitem>
- <listitem>
- <para>include <ulink
- url="../../example/motor1.c">motor1.c</ulink></para>
- </listitem>
- <listitem>
- <para>Add test to verify that the motor will be able to keep track
- of a position from 0 to 50000 steps. This will be needed to maintain
- the position of out linear stage across a range from 0 to 500
- mm.</para>
- </listitem>
- </orderedlist></para>
- <para>Our first attempt to run this program fails by throwing an exception
- from <ulink url="../../example/motor1.c">motor1.c</ulink> indicating that
- the code attempts to left shift a negative number at the
- statements:</para>
- <programlisting>denom = ((step_no - move) << 2) + 1;</programlisting>
- <para>According to the C/C++ standards this is implementation defined
- behavior. But in practice with all modern platforms (as far as I know),
- this will be equivalent to a multiplication by 4. Clearly the intent of
- the original author is to "micro optimize" the operation by substituting a
- cheap left shift for a potentially expensive integer multiplication. But
- on all modern compilers, this substitution will be performed automatically
- by the compiler's optimizer. So we have two alternatives here:</para>
- <itemizedlist>
- <listitem>
- <para>Just ignore the issue.</para>
- <para>This will work when the code is run on the PIC. But, in order to
- permit testing on the desktop, we need to inhibit the error detection
- in that environment. With safe numerics, error handling is determined
- by specifying an <link
- linkend="safe_numerics.exception_policy">exception policy</link>. In
- this example, we've used the default exception policy which traps
- implementation defined behavior. To ignore this kind of behavior we
- could define our own custom <link
- linkend="safe_numerics.exception_policy">exception
- policy</link>.</para>
- </listitem>
- <listitem>
- <para>change the <code><< 2</code> to <code>* 4</code>. This
- will produce the intended result in an unambiguous, portable way. For
- all known compilers, this change should not affect runtime performance
- in any way. It will result in unambiguously portable code.</para>
- </listitem>
- <listitem>
- <para>Alter the code so that the expression in question is never
- negative. Depending on sizes of the operands and the size of the
- native integer, this expression might return convert the operands to
- int or result in an invalid result.</para>
- </listitem>
- </itemizedlist>
- <para>Of these alternatives, the third seems the more definitive fix so
- we'll choose that one. We also decide to make a couple of minor changes to
- simplify the code and make mapping of the algorithm in the article to the
- code more transparent. With these changes, our test program runs to the
- end with no errors or exceptions. In addition, I made a minor change which
- simplifies the handling of floating point values in format of 24.8. This
- results in <ulink url="../../example/motor2.c">motor2.c</ulink> which
- makes the above changes. It should be easy to see that these two versions
- are otherwise identical.</para>
- <para>Finally our range test fails. In order to handle the full range we
- need, we'll have to change some data types used for holding step count and
- position. We won't do that here as it would make our example too complex.
- We'll deal with this on the next version.</para>
- </section>
- <section>
- <title>Trapping Errors at Compile Time</title>
- <para>We can test the same code we're going to load into our target system
- on the desktop. We could build and execute a complete unit test suite. We
- could capture the output and graph it. We have the ability to make are
- code much more likely to be bug free. But:</para>
- <itemizedlist>
- <listitem>
- <para>This system detects errors and exceptions on the test machine -
- but it fails to address and detect such problems on the target system.
- Since the target system is compiles only C code, we can't use the
- exception/error facilities of this library at runtime.</para>
- </listitem>
- <listitem>
- <para><ulink
- url="https://en.wikiquote.org/wiki/Edsger_W._Dijkstra">Testing shows
- the presence, not the absence of bugs</ulink>. Can we not prove that
- all integer arithmetic is correct?</para>
- </listitem>
- <listitem>
- <para>For at least some operations on safe integers there is runtime
- cost in checking for errors. In this example, this is not really a
- problem as the safe integer code is not included when the code is run
- on the target - it's only a C compiler after all. But more generally,
- using safe integers might incur an undesired runtime cost.</para>
- </listitem>
- </itemizedlist>
- <para>Can we catch all potential problems at compiler time and therefore
- eliminate all runtime cost?</para>
- <para>Our first attempt consists of simply changing default exception
- policy from the default runtime checking to the compile time trapping one.
- Then we redefine the aliases for the types used by the PIC to use this
- exception policy.</para>
- <programlisting>// generate compile time errors if operation could fail
- using trap_policy = boost::numeric::loose_trap_policy;
- ...
- typedef safe_t<int8_t, trap_policy> int8;
- ...
- </programlisting>
- <para>When we compile now, any expressions which could possibly fail will
- be flagged as syntax errors. This occurs 11 times when compiling the
- <ulink url="../../example/motor2.c">motor2.c</ulink> program. This is
- fewer than one might expect. To understand why, consider the following
- example:</para>
- <para><programlisting>safe<std::int8_t> x, y;
- ...
- safe<std::int16_t> z = x + y;
- </programlisting>C promotion rules and arithmetic are such that the z will
- always contain an arithmetically correct result regardless of what values
- are assigned to x and y. Hence there is no need for any kind of checking
- of the arithmetic or result. The Safe Numerics library uses compile time
- range arithmetic, C++ template multiprogramming and other techniques to
- restrict invocation of checking code to only those operations which could
- possible fail. So the above code incurs no runtime overhead.</para>
- <para>Now we have 11 cases to consider. Our goal is to modify the program
- so that this number of cases is reduced - hopefully to zero. Initially I
- wanted to just make a few tweaks in the versions of
- <code>example92.c</code>, <code>motor2.c</code> and
- <code>motor_test2.c</code> above without actually having to understand the
- code. It turns out that one needs to carefully consider what various types
- and variables are used for. This can be a good thing or a bad thing
- depending on one's circumstances, goals and personality. The programs
- above evolved into <ulink
- url="../../example/example93.c"><code>example93.c</code></ulink>,
- <code><ulink url="../../example/motor3.c">motor3.c</ulink></code> and
- <ulink
- url="../../example/motor_test3.c"><code>motor_test3.c</code></ulink>.
- First we'll look at <code>example93.c</code>:</para>
- <programlisting><xi:include href="../../example/example93.cpp"
- parse="text" xmlns:xi="http://www.w3.org/2001/XInclude"/></programlisting>
- <para>Here are the changes we've made int the desktop test
- program<orderedlist>
- <listitem>
- <para>Specify exception policies so we can generate a compile time
- error whenever an operation MIGHT fail. We've aliased this policy
- with the name <code>trap_policy</code>. The default policy of which
- throws a runtime exception when an error is countered is aliased as
- <code>exception_policy</code>. When creating safe types, we'll now
- specify which type of checking, compile time or runtime, we want
- done.</para>
- </listitem>
- <listitem>
- <para>Create a macro named "literal" an integral value that can be
- evaluated at compile time.</para>
- <para>"literal" values are instances of safe numeric types which are
- determined at compile time. They are <code>constexpr</code> values.
- When used along with other instances of safe numeric types, the
- compiler can calculate the range of the result and verify whether or
- not it can be contained in the result type. To create "literal"
- types we use the macro <code><link
- linkend="safe_numerics.safe_literal.make_safe_literal">make_safe_literal</link>(n,
- p, e)</code> where n is the value, p is the <link
- linkend="safe_numerics.promotion_policy">promotion policy</link> and
- e is the <link linkend="safe_numerics.exception_policy">exception
- policy</link>.</para>
- <para>When all the values in an expression are safe numeric values,
- the compiler can calculate the narrowest range of the result. If all
- the values in this range can be represented by the result type, then
- it can be guaranteed that an invalid result cannot be produced at
- runtime and no runtime checking is required.</para>
- <para>Make sure that all literal values are x are replaced with the
- macro invocation "literal(x)".</para>
- <para>It's unfortunate that the "literal" macro is required as it
- clutters the code. The good news is that is some future version of
- C++, expansion of <code>constexpr</code> facilities may result in
- elimination of this requirement.</para>
- </listitem>
- <listitem>
- <para>Create special types for the motor program. These will
- guarantee that values are in the expected ranges and permit compile
- time determination of when exceptional conditions might occur. In
- this example we create a special type c_t to the width of the pulse
- applied to the motor. Engineering constraints (motor load inertia)
- limit this value to the range of C0 to C_MIN. So we create a type
- with those limits. By using limits no larger than necessary, we
- supply enough information for the compiler to determine that the
- result of a calculation cannot fall outside the range of the result
- type. So less runtime checking is required. In addition, we get
- extra verification at compile time that values are in reasonable
- ranges for the quantity being modeled.</para>
- <para>We call these types "strong types".</para>
- </listitem>
- </orderedlist></para>
- <para>And we've made changes consistent with the above to <ulink
- url="../../example/motor3.c">motor3.c</ulink> as well<programlisting><xi:include
- href="../../example/motor3.c" parse="text"
- xmlns:xi="http://www.w3.org/2001/XInclude"/></programlisting><orderedlist>
- <listitem>
- <para>Define variables using strong types</para>
- </listitem>
- <listitem>
- <para>Surround all literal values with the "literal" keyword</para>
- </listitem>
- <listitem>
- <para>Re-factor code to make it easier to understand and compare
- with the algorithm as described in the original <ulink
- url="../../example/stepper-motor.pdf">article</ulink>.</para>
- </listitem>
- <listitem>
- <para>Rewrite interrupt handler in a way which mirrors the original
- description of the algorithm and minimizes usage of state variable,
- accumulated values, etc.</para>
- </listitem>
- <listitem>
- <para>Distinguish all the statements which might invoke a runtime
- exception with a comment. There are 12 such instances.</para>
- </listitem>
- </orderedlist></para>
- <para>Finally we make a couple minor changes in <ulink
- url="../../example/motor_test3.c">motor_test3.c</ulink> to verify that we
- can compile the exact same version of motor3.c on the PIC as well as on
- the desktop.</para>
- </section>
- <section>
- <title>Summary</title>
- <para>The intent of this case study is to show that the Safe Numerics
- Library can be an essential tool in validating the correctness of C/C++
- programs in all environments - including the most restricted.<itemizedlist>
- <listitem>
- <para>We started with a program written for a tiny micro controller
- for controlling the acceleration and deceleration of a stepper
- motor. The algorithm for doing this is very non-trivial and
- difficult prove that it is correct.</para>
- </listitem>
- <listitem>
- <para>We used the type promotion policies of the Safe Numerics
- Library to test and validate this algorithm on the desk top. The
- tested code is also compiled for the target micro controller.</para>
- </listitem>
- <listitem>
- <para>We used <emphasis>strong typing</emphasis> features of Safe
- Numerics to check that all types hold the values expected and invoke
- no invalid implicit conversions. Again the tested code is compiled
- for the target processor.</para>
- </listitem>
- </itemizedlist></para>
- <para>What we failed to do is to create a version of the program which
- uses the type system to prove that no results can be invalid. I turns out
- that states such as</para>
- <programlisting>++i;
- c = f(c);</programlisting>
- <para>can't be proved not to overflow with this system. So we're left with
- having to depend upon exhaustive testing. It's not what we hoped, but it's
- the best we can do.</para>
- </section>
- </section>
|