
\chapter{Using SOTL}

The SOTL programming model, writing many relatively independent
threads which are usually blocked waiting for events, turns out to be
very powerful in a workstation-based simulated environment.  In
addition to providing convenient mechanisms for simulating common
types of external hardware, the same model facilitates writing status
monitors, test scripts, and interfaces to other host programs.

\section{Writing Hardware Simulators}

SOTL is very well-suited to hardware modeling.  A typical external
hardware device communicates to the microprocessor through
memory-mapped addresses or I/O ports, can signal interrupts, can
perform actions based on internal timers, and can directly access
processor memory (DMA).  All of these hardware mechanisms can be
easily simulated through appropriate SOTL primitives and events.
Further, the autonomy of threads in SOTL is analogous to the autonomy
of hardware components in an embedded system.  Specifically, it is
typically very convenient to implement a simulator for a given piece
of external hardware as a thread.  If this piece of hardware is
memory-mapped or port-mapped, the thread can subscribe to memory/port
read/write events.  If it has internal timers, the thread can
subscribe to clock events.  SOTL has primitives for interacting with
the microprocessor in all the ways that external hardware components
can: signalling interrupts, reading and writing memory, and
manipulating the DMA controller and timer block.

As a simple example, here is a SOTL procedure which, when run as a
thread, implements an memory-mapped 7-segment LED.  When the program
writes a byte to memory location 0xe000:0x100, the user is informed
that the LED changed value.  Please refer to
Appendix~\ref{app:refman} for a detailed explanation of the language.

\code
    def $seven_segment_simulator ()
    {
                        # loop forever, waiting for events
        while (1) {
                        # wait for a write to location \(0xe000:0x100\)
            blockuntil memorywrite (0xe000:0x100);
                        # we've got one.  Find out the value written,
                        # and whether it was a byte or word write
            if (memorywrite($addr, $value, $size)) {
                if ($size == 2) {
                    print "Word writes to the LED not allowed!";
                    breaknow;
                } else {
                        # If bit 7 is set, then the decimal point is lit
                    $dot = $value & 0x80 ? "." : " ";
                        # Figure out which digit it is from remaining bits
                    $val = $value & 0x7f;
                    if ($val == 0x7f) {
                        $digit = "8";
                    } elsif ($val == 0x77) {
                        $digit = "0";
                    } elsif ... {
                        ...
                    } else {
                        $digit = "unknown";
                    }
                    print "LED changes to ", %b $value, 
                        "(", $digit, $dot, ")";
                }
            }
        }
    }
\code


As a more interesting example, consider this implementation of an
asynchronous 64k-BPS serial port.  The transmit and receive register
is port-mapped at address 0x3f8.  A status register at 0x3f9 indicates
receive ready (bit 0), receive overrun (bit 1), transmit empty (bit
2), and transmit overrun (bit 3).  Reading the status register clears
the overrun bits.  It signals an interrupt when the transmit register
becomes empty or a byte is received.  When the code under test sends a
character, it is printed on the screen.  When the user wants to send a
string of characters in, he calls the fuction
\$serial\_port\_kick\_recv(``thestring...'') defined below.

Here is the SOTL code that implements the simulated serial port:

\code
    $serport_incoming = "";
    $serport_next_xmit = 0;
    $serport_next_recv = 0;
    $serport_xmit_overrun = 0;
    $serport_recv_overrun = 0;
    $serport_data_taken = 0;
    $serport_speed = 1000;  # 1000 clock ticks per byte.  This is a 
                                # rate of 16k per second, or 64 kilobaud, 
                                # for a 16 Mhz processor.

    def $serport_simulator ()
    {
                                # Loop forever, waiting for events
        while (1) {
                                # Wait for write or read to our registers,
                                # or until transmit/receive time is over
            blockuntil portwrite(0x3f8, 0x3f9), 
		       portread(0x3f8, 0x3f9), 
                       breaktime($serport_next_xmit),
                       breaktime($serport_next_recv);
                                # Handle event
            if (portwrite($port, $value, $size)) {
                                # Handle write to transmit port
                if ($size == 2 || $port == 0x3f9) {
                    print "Bad write to serial port!  ", %w $port;
                    breaknow;
                } else {
                    if ($serport_next_xmit) {    # transmitting?
                        print "Serial port transmit overrun",
                            " transmitting ", %b $value;
                        $serport_xmit_overrun = 1;
                    } else {
                        print "Serial port writes ", %b $value;
                    }
                    $serport_next_xmit = clock + $serport_speed;
                }
            } elsif (portread($port, $size)) {
                if ($size == 2) {
                    print "Cannot read words from serial port!";
                    breaknow;
                } elsif ($port == 0x3f8) {
                                # Handle read from receive port
                    if ($serport_data_taken) {
                        print "Repeated serial port data read";
                    }
                    $serport_data_taken = 1;
                                # Make receieved character available
                    outbyte 0x3f8, $serport_incoming[0];
                    print "Serial port data read: ", 
                        %b $serport_incoming[0];
                } elsif ($port == 0x3f9) {
                                # Handle read from status port
                    $status = 0;
                    if (length($serport_incoming) > 0 &&
                        !$serport_data_taken)  { $status |= 1; }
                    if ($serport_recv_overrun) { $status |= 2; }
                    if (!$serport_next_xmit)   { $status |= 4; }
                    if ($serport_xmit_overrun) { $status |= 8; }
                                # Reading the port clears these bits
                    $serport_recv_overrun = 0;
                    $serport_xmit_overrun = 0;
                    outbyte 0x3f9, $status;
                }
            } elsif (breaktime($when)) {
                                # Handle timed event
                if ($when >= $serport_next_xmit) {
                                # Finished sending character
                    $serport_next_xmit = 0;
                                # Send interrupt to processor
                    interrupt 12;
                }
                if ($when >= $serport_next_recv) {
                                # Finished receiving a character
                    $serport_incoming = 
                        substr($serport_incoming, 1);
                    if (length($serport_incoming) == 0) {
                        $serport_next_recv = 0;
                    } else {
                                # Send interrupt to processor
                        $serport_kick_recv(0);
                    }
                }
            }
        }
    }

    def $serport_kick_recv($str)
    {
        if ($str) { $serport_incoming = $str; }
        interrupt 12;
        if (!$serport_data_taken { 
            $serport_recv_overrun = 1; 
        }
        $serport_next_recv = clock + $serport_speed;
        $serport_data_taken = 0;
    }
\code

As this example makes clear, a reasonably sophisticated piece
of hardware can be adequately simulated with very little
effort.

%
%%%%%% Target status monitors
%

Another useful feature of SOTL is the ease with which status monitors
can be written.  A status monitor is a SOTL program, usually running
as a single thread, which gives the engineer a view of some aspect of
the embedded software's execution.  It might, for example, monitor
whenever a certain procedure is called or when a certain variable
changes value.  The event mechanism provides support for status
monitors.  A status monitor can monitor what's being executed by
subscribing to code location events, can poll system status with clock
events, and can find out when variables are changed (or accessed) with
memory read/write events.  Here is an example SOTL command that will
cause a message to be printed every time a variable named ``x'' changes:

\code
    fork {
        while (1) {
		# wait for memory write event at x's address
            blockuntil memorywrite(&x);
		# now write the value through to x
            memorywrite ($addr, $value, $size); 
		# and notify the user
            print "X changed to ", $value;
        }
    };
\code

SOTL actually provides syntactic sugar that makes this sort of device
more convenient to type.  The following code is equivalent to the
above code.

\code
    whenever memorywrite(&x) { memorywrite ($addr, $value, $size);
                               print "X changed to ", $value; }
\code

If the user merely wants a breakpoint when ``x'' is written, the
following is the easiest way:

\code
    break memorywrite(\&x)
\code

%
%%%%%% Test scripts
%

SOTL is also good for writing test scripts.  Test scripts need to
provide some sort of stimulation to the embedded software and then
decide whether or not the software did the right thing.  A script runs
well as a single thread, blocking either until it's time to do the
next step of the script or until some indication is received that the
embedded software has reached a certain state.

%
%%%%%% Interfaces to other things
%

SOTL also interfaces conveniently to other host programs.  A thread
responsible for communicating with another UNIX process, for example,
can subscribe to file descriptor or message queue events.
This ability to connect to external processes is more useful than it
first seems.  Applications include interfacing to regression test
programs and interfacing to other types of simulators.

%
%%%%%%%% C regression script interface
%

Regression test programs are written as standalone programs so they can
run in the target environment or the simulated environment.  In the
laboratory environment, they communicate with various pieces of
specialized hardware that stimulates the whole embedded system and
verifies that its response is correct.  In the workstation
environment, regression test programs communicate with the SOTL
interface, which in turn stimulates the embedded software in an
analogous way.  This way the same tests can be run in both
environments; if a test fails in the target environment, the cause of
the problem can often be tracked down in the simulated environment.




%
%% Field trials
%
%
%%%% SLMR Overload
%
%
%% Conclusion
%

