547 lines
25 KiB
XML
547 lines
25 KiB
XML
<?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>
|