Orangepath/HPRLS Project: Hardware and Embedded Software Synthesis from Executable Specifications.
GPIB Interface Synthesis Example.

Hardware and Software Interface Synthesis for GPIB using H2.

The specification IS the design!

New H2 Manual.

By today's standards, the IEEE-488 bus is quite simple. Also known as HPIB and GPIB, it is an IEEE standard that connects a number of devices in the local area using sixteen parallel wires (plus grounds). Although USB is a more-recent replacement technology, GPIB is still widely used in industrial control, and it provides fine material for the following examples. An overview can be found in Mullard Technical Comms, No 138, April 78.

Around 20 years ago, DJG implemented a number of devices that connected to each other using IEEE-488. The devices were implemented using discrete TTL chips and small subroutines on 6800's and the like. These designs are now all be generated automatically as seeds for our HPRLS project, starting from a small amount of declarative specification of the protocols to be connected. The designs included:

  • Host interface,

  • A bus controller,

  • A bus bridge to Centronix parallel printer,

  • A bus bridge to RS232,

  • A multi-port bus exchange [DJG81].
As well as regenerating these old hardware designs, we can synthesise device driver code for the host interface and high-level behavioural models of complete sub-systems that can transfer a whole packet in one operation, rather than modelling the low-level hardware details.

An interesting feature of GPIB is its ability for a controller to set up a third party multicast from a single talker to multiple listeners in parallel. The relatively complex 3-wire handshake using DAV, NRFD and NDAC exists to implement this facility, and hence the bus is not totally trivial.

The general Organgepath approach synthesises the required interface hardware and driver software. It needs a definition of the resources available for communication and any mixture of the constraints on the protocols used and code fragments. In the example presented here, we have manually-coded, complete, formal specifications of the interface protocols, and we synthesise interface automata from pairs of these.

Formal Definition of the Bus

Here we manually code a formal definition of IEEE-488 using our toy H2 specification language.

The physical resources are 8 data wires and 8 control wires. These are connected in parallel to every device. Each net implements a wired-nor function, meaning that all stations can read the current value of every wire and this is a logic zero if any device is pulling it low. There is a known, finite, maximum skew between the wires, and a bundled-data asynchronous guard protocol is used, that delays the guard net assertion until all wires have settled under worst-case skew.

The bus is defined to have one fixed controller, and for any transaction, one active talker and some number of active listeners. The controller can be the current talker or one of the current listeners.

The bus specification defines that certain wires are always driven by the controller whereas others are driven by the talker and others are a wired-nor function of the listener outputs. We define the bus as a union of the functions unique to the controller and the remainder, concerned with data transfer between talker and listener:

interface GPIB
{
   GPIB_M;    // Controller part
   GPIB_DC;   // Data transfer part
}

interface GPIB_SLAVE
{
  protocol gpib_slave;

  forward:
   node out !ren;     // Remote enable
   node out !atn;     // Attentions
   node out !ifc;     // Interface clear
   node in  !srq;     // Service Request

}

interface GPIB_DC()
{
  neutral:

  forward:
     node out [7:0] data;  // The data
     node out !dav;        // Data valid.
     node out !eoi;        // Asserted for last bit of data   
     node in !ndac;        // Negative data acknowledge
     node in !nnrf;        // Not ready for data

     protocol gpib_talker_hs;

  reverse:

     protocol gpib_acceptor_hs;

}

The built-in node types include in, out, and inout, but user-defined interfaces can also be used as nodes. The built-in nodes range over a single bit or a scalar. Arrays, sets and lists might be added as built-in types in the future.

H2 provides various forms of specification. In this example, we demonstrate these by using different forms for different parts of the protocol, but a real user generally be more consistent across his specification and so use fewer forms.

Listener Side Protocol

Using the statemachine specification form, a named state is introduced with a colon. Here we only use one named state, called 'start'. The other states are anonymous and their existence is inferred from the presence of wait commands and the blocking write to channel rx.

// IEEE-488 listener-side protocol specified using a state machine
protocol gpib_acceptor_hs()
{
  statemachine(ach)
   {
     start:
      ndac = 1;
      wait (dav);

      nrfd = 1; ndac = 0;

      rx = ?d8;  // Read the input channel

      wait(!dav);

      nrfd = 0;
     goto start;
   }
}

Talker Side Protocol

The talker_hs implements bundled-data guarding with a synchd() command. This models the bus settling delay.

protocol talker_hs
{
  statemachine(ach)
   {
     start:
      wait(nrfd);
      d8!tx
      synchd(1);
      dav = 0;
      wait(ndac);
      d8!X; dav = 1;   // X here denotes don't care
     goto start;
   }
}

Controller/Slave Protocol

To illustrate yet a third form of protocol specification, we specify the slave protocol using just a state variable and a set of assertions. The controller protocol is the slave protocol with the signals reversed. Our specification instantiates the talker and listner sub-protocols, which is not difficult to do inside a tool that converts the statecharts and regular expressions into declarations. However, our ambition for larger designs is not to loose the behavioural hints from these more direct input forms at the point when we are assembling components.

protocol gpib_slave
{
   statevar s enum { idle, talker, listener };

   always ~ifc => s == idle;

   always
      (ifc & ~atn & ~dav) &&  d8[7:5]==1 && d8[4:0]==Addr => s == talker
   /\ (ifc & ~atn & ~dav) && d8[7:5]==2 && d8[4:0]==Addr => s == listner
   /\ (ifc & ~atn & ~dav) && d8[7:5]==1 && d8[4:0]==31 => s == idle
   /\ (ifc & ~atn & ~dav) && d8[7:5]==2 && d8[4:0]==31 => s == idle;
 
   always talker => talker_hs /\ listner => listner_hs; 

}

Centronix Parallel Port

Since we are synthesising bus bridges, we also require formal specifications for the attached interfaces. In this section we define Centronix.

Centronix is a conventional bundled-data four-phase handshake with an additional busy signal to provide flow control backpressure. We could specify the centronix protocol using separate sender and receiver state charts but to illustrate mixing of methodologies, we use an extended regular expression.

interface CENTRONIX_KERNEL
{
  protocol centronix;


  forward:
     node out [7:0] d8;
     node out n_strobe;
     node in n_ack;
     node in busy;
}

protocol centronix
{
  always S;
  S := ~busy . (d8=tx) . synchd(). !n_strobe . !n_ack . (d8=X) . n_strobe . n_ack;
}

The protocol specification is an assertion of a regular expression. This is taken to mean that the idle and starting state of the interface is the starting state for evaluating the regular expression and that it will loop back to this point immediately on after exiting.

The use of the = symbol in this infix form creates a quick assign. For the forward direction of the interface, d8=tx means d8!tx. This is a write to a channel, which for a set of simple wires, such as d8, means to set them to this value until a d8!X occurs, which means they are don't care from then on. For the reverse direction of the interface, d8=tx will cause a read from channel d8. A channel is currently another word for a node, but eventually we can nest all of these concepts.

The centronix behavioural model can be synthesised from nothing more than the following method declarations:

signature centronix()
{
   int8 read() { return tx };
   void write(int8 tx);   
}

Synthesising a netlist-level GPIP to Centronix Bus Bridge.

To create a simplex bus bridge between the two protocols we make the following structural composition that instantiates the two interfaces and then we need to plumb the rx channel of the GPIB to the tx channel of the Centronix and we need to set a GPIB listner address (as set on the DIL switches in the real design below). In this subsection we are only handling centronix output, so the talker side of the GPIB protocol will not be used, despite being present in the tool.


   section SIMPLEX_BUSBRIDGE()
   {
     interface forward GPIB left;
     interface CENTRONIX_KERNEL right;

     connect left.rx, right.tx;
   }  

Note that the two interfaces are not connected to each other via their I/O nodes: these are exposed for outside connection of devices to the bus bridge. Instead, our tool wires the two interfaces to each other by creating an internal datapath and handshake as a result of the connect directive. There is no constraint on the delay of the interface (it could include a large FIFO) but order conservation is implied.

The name `SIMPLEX_BUSBRIDGE' is passed to the H2 tool on its command line as the root to work from. The modifier forward is added to the instance of the GPIB, but this is the default direction anyway.

Note that the dot symbol is used for access to components of a structure as well as sequencing in regular expressions, depending on context.

Extending the example: adding a wait switch.

We extend this example by adding an additional input, designed to be connected to a manual switch, that will take the printer offline. It will do this by causing each interface not to leave its idle state after it next enters it.

TODO ... where is this ?

Extending the example: generating a software implementation.

TODO ... where is this ?

Extending the example: making it duplex.

We extend this example by adding a second centronix port of the other direction and connecting this to the talker function of the GPIB. Owing to the intrinsic nature of GPIB, the resulting design can be implemented in one of two ways:

In the left-hand way, we make two runs of the tool to create two separate bus bridges that can be connected to the bus. In the right-hand way, one run of the tool generates a single composite system.

Comparative Real Design: HPIB to Centronix

Commercial Product

      www.WuT.de      

An illustration of a contemporary commercial product.

Old DJG Design

The schematic shows the core circuit of an implementation of a simplex GPIB to Centronix parallel port converter unit that I made in the late seventies. The design has a number of features:

  • For cost reasons, it only implements part of a full specification.

  • It has partial address decoding, resulting in mirrors.

  • The actual data is not looked at or changed.

  • It is completely asynchronous and relies on gate delays and on two delay stages for timing.

  • It has a mistake: the printer port can hold the GPIB bus in not-ready mode even when the printer is not being used. Also, it assumes that the printer ack occurs before the GPIB talker sends the next byte.

    The circuit operates as follows. When the unit is idle or after being reset with the IFC interface clear signal, the listner flop is clear. When ATN is asserted low and the correct address and command are received, the listner flop is set. We remain in listener mode until an unlisten command. While in listner mode, we execute the acceptor handshake, by alternately driving NRFD and NDAC low in response to the DAV signal. The DAV signal is delayed in the first delay stage in case it should arrive early, although actually it is the talker's duty to delay DAV with respect to the data. The Centronix handshake is implemented with a second RS-latch and a second delay unit delays the assertion of the strobe signal until the data has settled.

    DOT DOT DOT - under construction - last modified 16 Jul 2003 but being finished finally now (Mar 2007).


    (C) January 2003 DJ Greaves. Home.