               bridge-tcl (libtclapi.la :: tcl_component_library)

Synopsis:

   This family of components implements a bridge between the C++ SID API and
   Tcl, in an embedded Tcl/Tk/Tk+blt 8.0 interpreter.

   +---------------------------------------------------+
   | Pins:          | !event !event-control (+BRIDGED) |
   |----------------+----------------------------------|
   | Attributes:    | load! eval! (+BRIDGED)           |
   |----------------+----------------------------------|
   | Buses:         | (BRIDGED)                        |
   |----------------+----------------------------------|
   | Accessors:     | (BRIDGED)                        |
   |----------------+----------------------------------|
   | Relationships: | (BRIDGED)                        |
   +---------------------------------------------------+

     ----------------------------------------------------------------------

Functionality:

  Modelling:

   As this family of components is just a bridge, the nature of modelling
   performed is up to the script on the other side of the bridge. This
   component merely funnels SID API calls between the outer C++ system and
   each bridge component's isolated embedded tcl interpreter.

   Low Level

   Note that the pin and attribute lists include only those low level
   interfaces that are actually provided by the bridge component. In order to
   act transparent, these are excluded from the pin_names and attribute_names
   inquiry functions. All visible low level interfaces actually come from the
   Tcl scripting code loaded into the bridge.

   Theory of Operation for bridge-tcl Component

   A bridge-tcl component is a shell that hooks all sid API calls to an
   embedded tcl interpreter so that they can be handled as tcl procedure
   calls. In addition, sid API calls are exposed to that interpreter, so the
   tcl procedures can call back out to the C++ system. With these two
   capabilities, a user-provided tcl package may become a first class sid
   component.

   Objects such as bus, component, and pin pointers may be passed through tcl
   scripts safely, because the bridging calls represent these as unique
   strings, and convert them back to C++ pointers automatically. Any pointers
   seen through incoming call arguments, or outgoing call return values, are
   transparently converted into unique long-lived opaque strings. This way,
   C++ pointers can safely pass through the tcl bridge in both directions.

   Unlike C++ components, tcl scripts that run in a bridge-tcl do not have
   access to the sidutil:: group of utility classes. This means that only low
   level operations are directly provided, and sidutil:: abstractions would
   need to be rewritten (if needed) in tcl.

   Incoming sid API Calls

   Almost all incoming sid API calls are passed through verbatim to the
   embedded tcl interpreter. (Exceptions are parameterized and noted below.)
   Plain types are mapped according to the table below: C++ object to tcl for
   arguments, and tcl to C++ for return values. If tcl procedures by the
   appropriate names are not loaded into the interpreter by the time they are
   invoked from another sid component, a "TCL ERROR" message is printed to
   cerr, and a function-specific error indication is returned.

   Calls belonging to sid::pin and sid::bus are similarly mapped to tcl
   procedure calls. The C++ pin/bus object on which they are called is passed
   to the procedures as an extra argument. (C++ pin/bus objects may be
   constructed for a tcl component through special callback functions, listed
   below.)

   Functions with multiple outputs, like the sid::bus::read reference
   arguments, map to tcl procedures returning a list with the mapped C++
   return type as first element, and the output reference argument as second
   element.

   +------------------------------------------------------------------------+
   | C++ type                        | tcl type                             |
   |---------------------------------+--------------------------------------|
   | string                          | string                               |
   |---------------------------------+--------------------------------------|
   | vector<string>                  | list of strings                      |
   |---------------------------------+--------------------------------------|
   | component, bus, or pin pointer  | opaque string                        |
   |---------------------------------+--------------------------------------|
   | {little,big,host}_int_{1,2,4,8} | numeric integer - care with 64-bit   |
   |                                 | ints!                                |
   |---------------------------------+--------------------------------------|
   | component::status               | string: "ok", "bad_value",           |
   |                                 | "not_found"                          |
   |---------------------------------+--------------------------------------|
   |                                 | list: {code latency}. code is one of |
   | bus::status                     | "ok", "misaligned", "unmapped",      |
   |                                 | "unpermitted" and latency is a       |
   |                                 | numeric value                        |
   |---------------------------------+--------------------------------------|
   | vector<component*>              | list of opaque strings               |
   |---------------------------------+--------------------------------------|
   | vector<pin*>                    | list of opaque strings               |
   |---------------------------------+--------------------------------------|
   | 0 (null pointer)                | ""                                   |
   +------------------------------------------------------------------------+

   +------------------------------------------------------------------------+
   | Incoming C++ Call                     | Outgoing tcl Call              |
   |---------------------------------------+--------------------------------|
   | In sid::component                     |                                |
   |---------------------------------------+--------------------------------|
   | attribute_names()                     | attribute_names                |
   |---------------------------------------+--------------------------------|
   | attribute_names(category)             | attribute_names_in_category    |
   |                                       | $category                      |
   |---------------------------------------+--------------------------------|
   | attribute_value(name)                 | attribute_value $name          |
   |---------------------------------------+--------------------------------|
   | set_attribute_value(name,value)       | set_attribute_value $name      |
   |                                       | $value                         |
   |---------------------------------------+--------------------------------|
   | pin_names                             | pin_names                      |
   |---------------------------------------+--------------------------------|
   | find_pin(name)                        | find_pin $name                 |
   |---------------------------------------+--------------------------------|
   | connect_pin(name, pin)                | connect_pin $name $pin         |
   |---------------------------------------+--------------------------------|
   | disconnect_pin(name, pin)             | disconnect_pin $name $pin      |
   |---------------------------------------+--------------------------------|
   | connected_pins(name)                  | connected_pins $name           |
   |---------------------------------------+--------------------------------|
   | bus_names                             | bus_names                      |
   |---------------------------------------+--------------------------------|
   | find_bus(name)                        | find_bus $name                 |
   |---------------------------------------+--------------------------------|
   | accessor_names                        | accessor_names                 |
   |---------------------------------------+--------------------------------|
   | connect_accessor(name,bus)            | connect_accessor $name $bus    |
   |---------------------------------------+--------------------------------|
   | disconnect_accessor(name,bus)         | disconnect_accessor $name $bus |
   |---------------------------------------+--------------------------------|
   | connected_bus(name)                   | connected_bus $name            |
   |---------------------------------------+--------------------------------|
   | relationship_names()                  | relationship_names             |
   |---------------------------------------+--------------------------------|
   | relate(rel,comp)                      | relate $rel $comp              |
   |---------------------------------------+--------------------------------|
   | unrelate(rel,comp)                    | unrelate $rel $comp            |
   |---------------------------------------+--------------------------------|
   | related_components(rel)               | related_components $rel        |
   |---------------------------------------+--------------------------------|
   | In sid::pin                           |                                |
   |---------------------------------------+--------------------------------|
   | driven(value)                         | driven_h4 $pin $value          |
   |---------------------------------------+--------------------------------|
   | In sid::bus, for host_int_4 address   |                                |
   | and {big,little}_int_Y data types     |                                |
   |---------------------------------------+--------------------------------|
   | read(address,data)                    | read_h4_{l,b}Y $address **     |
   |                                       | return [list $status $data] ** |
   |---------------------------------------+--------------------------------|
   | write(address,data)                   | write_h4_{l,b}Y $address $data |
   |                                       | ** return $status **           |
   +------------------------------------------------------------------------+

   Outgoing sid API Calls

   Once a tcl program is loaded into the interpreter, it is able to make
   outgoing sid API calls, not merely respond to incoming ones. All sid API
   functions are exposed to tcl as procedure hooks, in a very symmetric way
   to the incoming calls. Simply, each function in the incoming set has a
   shadow: "sid::component::FUNCTION", "sid::pin::FUNCTION" or
   "sid::bus::FUNCTION", as appropriate. Each outgoing procedure takes a
   receiver handle (the same opaque string passed in an incoming call) as its
   first argument.

   There is no checking that would prevent an outgoing sid API call from
   becoming recursive and referring to the originating component, either
   directly or indirectly. As for all other components, infinite recursion
   prevention is the responsibility of the component author.

   +------------------------------------------------------------------------+
   | Incoming              | Outgoing                                       |
   |-----------------------+------------------------------------------------|
   | attribute_value $name | sid::component::attribute_value $component     |
   |                       | $name                                          |
   |-----------------------+------------------------------------------------|
   | driven_h4 $pin $value | sid::pin::driven_h4 $pin $value                |
   |-----------------------+------------------------------------------------|
   | ... etc ...           | ... etc ...                                    |
   +------------------------------------------------------------------------+

   There are some special outgoing functions that function as constructors
   for local object handles.

   +------------------------------------------------------------------------+
   | sid::component::this | returns an opaque string handle to this         |
   |                      | component                                       |
   |----------------------+-------------------------------------------------|
   |                      | returns an opaque string handle to a new        |
   | sid::pin::new        | private C++ pin, usable as a return value to    |
   |                      | `find_pin'                                      |
   |----------------------+-------------------------------------------------|
   |                      | returns an opaque string handle to a new        |
   | sid::bus::new        | private C++ bus, usable as a return value to    |
   |                      | `find_bus'                                      |
   +------------------------------------------------------------------------+

   +-------------------------------------------------+
   |                    Behaviors                    |
   |-------------------------------------------------|
   |  configuration | You must configure the         |
   |                | embedded Tcl interpreter with  |
   |                | the Tcl scripting code that    |
   |                | will receive bridged C++ SID   |
   |                | API calls. This can be done in |
   |                | two ways. First, if the load!  |
   |                | attribute is written to, the   |
   |                | value is interpreted as a file |
   |                | name, and loaded into the Tcl  |
   |                | interpreter as if with the Tcl |
   |                | "source" command. This file    |
   |                | may contain procedure          |
   |                | definitions and any Tcl code   |
   |                | to be evaluated right away.    |
   |                | Second, if the eval! attribute |
   |                | is written to, the value is    |
   |                | interpreted as a Tcl           |
   |                | expression as if the           |
   |                | Tcl_Eval() function had been   |
   |                | used.                          |
   |                |                                |
   |                | A bridge-tk type component is  |
   |                | automatically initialized with |
   |                | the usual bindings to the Tk   |
   |                | windowing toolkit. A           |
   |                | bridge-blt type component,     |
   |                | where available, includes the  |
   |                | same Tk bindings, in addition  |
   |                | to the usual bindings to the   |
   |                | BLT Tcl extension library.     |
   |----------------+--------------------------------|
   | event handling | The embedded Tcl interpreter   |
   |                | requires regular-event polling |
   |                | in order to operate,           |
   |                | especially if the tk or tk+blt |
   |                | extensions are in use.         |
   |                | Whenever the !event input pin  |
   |                | is driven, the                 |
   |                | Tcl_DoOneEvent() function is   |
   |                | called repeatedly for all      |
   |                | pending Tcl events.            |
   |                |                                |
   |                | In response, the               |
   |                | !event-control output pin is   |
   |                | driven with a number between 1 |
   |                | and 1000. The number           |
   |                | represents the component's     |
   |                | suggestion about the time      |
   |                | interval to the next !event    |
   |                | signal, and is meant to be     |
   |                | compatible with the sid-sched  |
   |                | components' NNN-control        |
   |                | inputs. This way, the Tcl      |
   |                | bridge component attempts to   |
   |                | adaptively regulate its own    |
   |                | event polling rate, balancing  |
   |                | good response time in busy     |
   |                | periods and low overhead       |
   |                | during idle periods.           |
   |----------------+--------------------------------|
   |       bridging | With only the exceptions noted |
   |                | above, all incoming C++ SID    |
   |                | API calls are bridged by       |
   |                | making appropriate calls to an |
   |                | embedded Tcl interpreter. One  |
   |                | Tcl procedure call is made per |
   |                | C++ call. Types and function   |
   |                | names are mapped as specified  |
   |                | in the Incoming sid API calls  |
   |                | section.                       |
   |                |                                |
   |                | The invoked Tcl procedure may  |
   |                | perform any necessary          |
   |                | processing and return a value. |
   |                | It may make further SID API    |
   |                | calls outward to other         |
   |                | components through the         |
   |                | symmetrical Tcl-to-C++         |
   |                | bridging described in the      |
   |                | Outgoing sid API calls         |
   |                | section.                       |
   |                |                                |
   |                | Tcl interpreter errors are     |
   |                | caught during the bridging     |
   |                | process. In case the           |
   |                | interpreter fails to run the   |
   |                | appropriate Tcl procedure to   |
   |                | completion, a "TCL ERROR"      |
   |                | message is printed to stderr,  |
   |                | and some error-suggesting      |
   |                | return value is made up for    |
   |                | completing the C++ call. This  |
   |                | situation is analogous to a    |
   |                | C++ component throwing an      |
   |                | exception during its execution |
   |                | of an incoming SID API call,   |
   |                | though in the pure C++ case,   |
   |                | uncaught exceptions cause the  |
   |                | SID process to terminate.      |
   +-------------------------------------------------+

   +-------------------------------------------------+
   |                 SID Conventions                 |
   |-------------------------------------------------|
   | - | supported | This component bridges an API.  |
   |   |           | Any conventions supported by    |
   |   |           | the loaded script on the Tcl    |
   |   |           | side will be supported on the   |
   |   |           | C++ side of the bridge. The     |
   |   |           | bridge does not implement any   |
   |   |           | SID conventions on its own.     |
   +-------------------------------------------------+

     ----------------------------------------------------------------------

Environment:

   Related Components
     * As suggested in the "Event handling" behavior section above, this
       component performs best if the !event and !event-control pins are
       connected to a scheduler. If the bridge component is to carry out any
       work, it is necessary to load Tcl script fragments into it. Here is a
       script fragment that does both.

         new bridge-tcl tracer
         new sid-sched-host sched
         set tracer load! "/path/my-component.tcl"
         connect-pin sched 0-event -> tracer !event
         connect-pin sched 0-control <- tracer !event-control

     * A more sophisticated way to use a bridge component is to associate SID
       triggerpoint hits with scripted actions. For example, the following
       fragment activates a triggerpoint on a register of a peripheral, and
       prints a message whenever it hits. Note how the Tcl script is fed in
       piece by piece using the eval! attribute.

         new bridge-tcl watcher
         new some-type victim
         # Configure bare minimum tcl code to make an input pin and
         # respond to it being driven.  Note how find_pin does not
         # check the pin name, so any name will be accepted in the
         # later connect-pin line.
         set watcher eval! "set p [sid::pin::new]"
         set watcher eval! "proc find_pin {name} {global p ; return $p}"
         set watcher eval! "proc driven_h4 {pin value} {puts \"triggerpoint hit v=$value!\"}"
         # triggerpoint: watch victim component's register "r0" for value 0xAA
         connect-pin victim watch:r0:value:0xAA -> watcher anyname

   Host System
     * The bridge-tcl and bridge-tk components are available on all
       platforms. The bridge-blt component is available only if the BLT Tcl
       extension library was compiled into this component.

     * Each instance of a bridge-tk or bridge-blt component creates a
       separate new top-level tk window.

     ----------------------------------------------------------------------

Component Reference:

  Component: bridge-tcl

   +-------------------------------------------------+
   |                      pins                       |
   |-------------------------------------------------|
   |     name     |direction|legalvalues| behaviors  |
   |--------------+---------+-----------+------------|
   |!event        |in       |any        |event       |
   |              |         |           |handling    |
   |--------------+---------+-----------+------------|
   |!event-control|out      |1..1000    |event       |
   |              |         |           |handling    |
   +-------------------------------------------------+

   +-------------------------------------------------+
   |                   attributes                    |
   |-------------------------------------------------|
   |name |category|  legal  | default |  behaviors  ||
   |     |        | values  |  value  |             ||
   |-----+--------+---------+---------+-------------||
   |load!|-       |file name|-        |configuration||
   |-----+--------+---------+---------+-------------||
   |eval!|-       |string   |-        |configuration||
   +-------------------------------------------------+

  Variant: bridge-tk

   Same as bridge-tcl

  Variant: bridge-blt

   Same as bridge-tcl
