[sci.electronics] schem88 proposed standard for posting schematics

hanley@cmcl2.UUCP (05/13/88)

I am considering implementing the following 'proposed standard' as a summer
project.  Please send comments directly to me at either mancol!jh
(..!CMCL2.nyu.edu!manhat!jh is forwarded to mancol!jh) or hanley@nyu.edu
(which I log on to less often).  If you feel you really must post (please
avoid posting), be sure to include the keyword "schem88" in your subject
line so people can ignore this thread.  By the time I'm through I'm sure
I will have implemented object rotation, and an editor for at least one
machine, probably Sun.  I am particularly interested in comments on
a) the usefulness of my IC model and suggestions of alternatives
b) the importance of making it run under VMS (looks like it's going to lean
    heavily on Unix pipelines!)
c) how to implement the schematics editor in a machine-independent way (will
    porting be painless if I just call a few generic clear-the-screen,
    draw-a-line, and get-mouse/pointing_device-xy routines and assume I'll be
    able to implement that handful of routines on each machine, or are there
    subtle issues that would be better considered now than later?)




                T H E   S C H E M 8 8   S T A N D A R D

                      Copyright 1988 by John Hanley.
                      Permission is granted to store and redistribute unedited
                      copies of this document, and to make excerpts for
                      purposes of preliminary discussion of the standard.
                      We don't multiple versions of a "standard."


A long time ago when this whole "posting schematics" discussion began, I
started working on something that would combine SPICE netlists with (x,y)
information, using the following article as a starting point:
In well-reasoned article <2349@vice.TEK.COM>, keithl@vice.TEK.COM (Keith
Lofstrom) writes:
> We are not going to all be able to directly use the same format.  Most of
> us will need translators.  Lots of people are volunteering formats, but NOBODY
> is volunteering TRANSLATORS.  So, whatever format is used must be easy to
> translate with hack programs written by dummies like myself.  I would propose
> we select a format that fits the following criteria:
>
>  1)  ALPHANUMERIC, no special characters, no binary           [Agreed.  --jh]
>  2)  FIXED drawing window    ( how about 1024*768? ;-) )         [I disagree]
>  3)  AS SIMPLE AS POSSIBLE - just MOVEs and DRAWs and unscaled text
>      (you can build scaled text with moves and draws if necessary).    [yes!]
>  4)  No dashes, filled polygons, rotated text, or hierarchy (the last
>      is hard ... how many times do you want to send the same resistor
>      shape over USENET?  Nonetheless, simplicity counts for much!)  See #3.
        [I agree with all but the hierarchy business... more on macros below.]
> [...]
> I *DO KNOW* that I can build translators TO all of the above from moves and
> draws and unscaled text with simple AWK scripts or BASIC programs or
> whatever.  I can build translators FROM my favorite drawing tools to
> moves and draws.

I am doing so for UN*X plot(1), on the assumptions that  a) UN*X is pretty
much the native OS for Usenet, and  b) since UN*X runs on so many machines,
right down to the lowly '286, even if for some reason you got hold of the
posted schematic on a machine running a different OS, you should still be
able to track down a target machine running UN*X.  I chose to support
plot(1) because it's simple, readily available, and it already supports
lots of diverse devices.  If it doesn't support your favorite hardware,
write the short filter to cvt plot-codes to your-codes, and post the source.

The basic scheme is to post SPICE source that includes (x,y) information,
pipe this through a filter that extracts this information and outputs only
"draw a line from here to there" information, and pipe that to a display
program.

> Perhaps a primitive subset of some existing format would do, so at least
> SOMEONE could imbed it.  If nothing else, how about:

>    NNNxNNNy    for absolute coordinate
>    m           for move to prefixed coordinate
>    d           for draw to prefixed coordinate
>    (string)    for text to prefixed coordinate
>    \* comment *\
>    all other characters ignored
> [...]  Does anyone have any other suggestions that fit the above criteria? 
> -- 
> Keith Lofstrom   ...!tektronix!vice!keithl   keithl@vice.TEK.COM
> MS 59-316, Tektronix, PO 500, Beaverton OR 97077  (503)-627-4052

I like this as a starting point, but (after resisting the strong urge to
support primitives like triangles and IC's) I decided that a few more
commands are needed to support macros (resistors, op amps, etc):
relative moves and scaling.

My design goals were to provide precisely enough power to allow any machine
to do a good job of rendering a schematic while making implementation fairly
easy.  Text is a sticky point.  The exact set of drawing commands I use is:
	NNN,NNN for scaled relative coordinate
	m	for move to prefixed coordinate
	d	for draw to prefixed coordinate
	NNNs	to change scale factor, pushing the old one onto a stack
	S	to pop the old scale factor
	o	to move (absolute) to the origin, so a following relative
		  move will actually be absolute (assuming unity scale factor)
	"text"	for text at prefixed coordinate sized to current scale

Text is not handled directly, but is inserted with the label macro, e.g.,
label(size, `some text').  It is illegal to directly insert "some text" into
a schematics file; you must use the label macro.  A distinct and replaceable
stage of the pipeline handles expansion of label macros to provide some
flexibility for displaying text on devices that can only do it if you give
them lots of vectors, devices that can plot text by themselves, and devices
like VT100's and printers that don't "plot" text at all.  For the moment, only
upper case letters, digits, and a few punctuation marks will be fully supported.

Also, coordinates are expressed as floats rather than integers.  Fixed
resolution is unacceptable in a standard that is to be broadly applicable to
many machines, so I leave each poster free to draw schematics using his
favorite resolution.  Screen coordinates are mapped to numbers in the range
0 to 1, inclusive, with (0,0) being the lower left corner and (1,1) the
upper right.  Individual implementations are responsible for getting the
aspect ratio right; connecting the points (0,0)-(1,0)-(1,1)-(0,1)-(0,0)
should result in a perfect square being rendered on the display device.
Similarly, individual implementations are responsible for zooming in on
portions of the display and performing clipping.  This is an important
capability, even for hosts supporting high resolution, since the decimal
coordinates can theoretically express arbitrarily fine detail.  Since
plot(1) supports line printer output and VT100's, anyone with a VT100 should
be able to (laboriously) view any schematic by examining portions of it
piecemeal.  Incidentally, VT100's are capable of medium resolution that
people seldom use: there are 16 graphic characters that let you display any
2*2 combination of pixels in any of the 132*24 character cells, giving a
resolution of 264*48, a resolution at which it would be somewhat practical
to pan over a medium-size schematic.  So who's going to write the filter and
post it?

Following a mandatory header line (to identify revision level of the
standard) is a file describing the schematic, essentially a SPICE
description of the ckt with annotations describing where components are
located (x,y) and how wires are routed.  I don't know how SPICE deals with
IC's; there is some minimal support for IC's in schem88a to make it a little
easier to create the netlist.  I am open to discussion on this -- send mail
to ..!CMCL2.nyu.edu!manhat!jh.  No attempt has been made to support rotated
components; if you want to rotate a resistor 90 degrees, you must come up
with the new vectors yourself.  If you want to automate this, fine, but
schem88 doesn't support it directly at the the moment (so you need macros
like horizontal_resistor, vertical_resistor, etc.).  It's not that rotating
is hard, just that m4 isn't good at arithmetic and I'm more interested in
getting the other things working first.

In schem88 files, comments can always be inserted between curly braces;
the first stage of the pipeline is always a filter to nuke {comments}.
Position information always appears after semi-colons; anything between
";" and new-line (after {comment} removal) should be something describing
(x,y)/labeling information.  Anything else is valid SPICE input; this way
you can always do something like
	cat schematic | strip_braces | strip_semicolons | spice
while developing.

The SPICE information is always left untouched, but is used to find
connectivity information.  The stuff hiding behind semi-colons is
transformed from a "high-level" form to one containing strictly
"draw line from (x,y) to (x,y)" type information  This is then then
read by a device-specific program which does the actual drawing.  Output
is to a "device" that understands plot(1)-type commands, so a wide variety
of devices are already supported.

Format A schematics are the "high-level" description, and are converted to
format B (x,y only), using m4 and awk.  M4 was selected because it provides
nested macros so that complex library symbols may be easily built out of
simpler ones, and because it has an "include(lib_file)" facility.  There
are potential problems with m4 doing macro expansion on things that shouldn't
be expanded (including "*" comments), and I am open to comments on this.
The router (an awk script) tries to connect components based on the SPICE
netlist, but doesn't make any special effort to be an amazingly intelligent
router, so it provides for easy over-riding of its default connections if
you want to route things yourself.  A pair of examples should help to
clarify matters:

  ; schem88a
  * Parallel Caps
  ; ports cap 500,0 500,1000
  ; define(cap, ``$0'' $1s`
  ;500,0m 500,400d 0,400d 1000,400d    { library definition for cap symbol }
  ;500,1000m 500,600d 0,600d 100,600d
  ;S' )
  C1 0 1 50pF ; 250,500 cap(100)  {Amazing example shows how to make a 100pF}
  C2 0 1 50pF ; 450,500 cap(100)  { capacitor by putting 2 50's in parallel.}
  .end

The first 2 lines and the .end are mandatory. The first line _must_ start with
the characters "; schem88a" and may optionally be followed by a blank and
any comments you like.  This is to facilitate automatic extraction of
circuit descriptions from posted articles, and to permit version
compatibility when the standard is (inevitably) revised -- translators for
schem88b and following will at least be able to identify circuit
descriptions written for the original standard.  The second line _must_
be a comment describing the overall circuit -- this is a requirement imposed
by SPICE (actually, I think the 1st character doesn't even have to be "*" --
SPICE just ignores it's 1st line of input, using it to title all pages of
SPICE output).  The last line of the schematic _must_ be ".end" on a line by
itself, not only because SPICE requires it, but because the schem88 standard
requires it to facilitate extraction of schematics from news articles.

For m4 fans, the double quotes in ``$0'' are needed to prevent a recursive
macro expansion, the open quote in "$1s`" is to keep m4 from interpretting
commas in coordinates as being commas separating arguments, and the "S'" is
a close quote.

Note that the coordinate system was previously described as being a unit
square, yet the example gives coordinates like 500,400.  This should be
interpreted as the coordinate (0.5,0.4) on the unit square.  Since this
standard is intended to be read and written by humans as well as machines,
all coordinates are scaled by 1000 for convenience.  (I started writing the
example in decimal notation and found that repeatedly typing the extraneous
decimal point became quite exasperating.)  Note that coordinates are still
DECIMALS of theoretically unlimited precision; the coordinate 271.828,314.59
is perfectly valid.  I don't care if you think of coordinates as being on a
unit square or a 1000-unit square.  Just remember that precision is NOT
limited to 1 part in 1000.  Also, I am open to suggestions if anyone thinks
a 100-unit square would be more convenient.  I even have doubts about the
value of scaling for human convenience at all, since editors are likely to
output hairy decimals anyway.  I'm not overly concerned about large decimals
making schematic files unduly large, but perhaps I should be.  Send in your
votes, together with sample schematics to prove that you used the standard
enough to have some basis for deciding which was more convenient, to
CMCL2!manhat!jh.  Like Kermit or FTP, schem88 is both a standard and a
program (OK, a collection of programs).  The standard specifies that
coordinates are _decimals_.  This is especially important for handling
scaled macros.

While it is perfectly possible to develop a schematic description by
iteratively going through an edit-then-display cycle, it is hoped that
someone will write an X-windows front-end to allow generating symbols and
whole schematics by simply pointing with a mouse to where you want your
lines to be drawn.  There is no real reason why humans should ever have to
worry about exactly what 2 numbers specify a particular point.

The definition of a graphic symbol macro is pretty straightforward.  The
definition of "cap" and the symbol it draws are given below:
; define(cap, ``$0'' $1s`                      |
;500,0m 500,400d 0,400d 1000,400d            -----
;500,1000m 500,600d 0,600d 100,600d          -----
;S' )                                          |
The $0 is the name of the macro (`cap') and is for reference by the router.
The $1 expands to argument #1, and followed by "s" it sets the scale factor
for relative line-drawing commands.  The capital "S" at the end of the macro
is a "pop-your-stack" command and restores the scale and absolute (x,y)
position to whatever they were just before the "$1s" command.

The 500,0m command Moves to (0.500,0.000) on the unit square without
drawing, and the 500,400d command Draws from that point to (0.5,0.4).
Similarly for the other commands, so that the first line of commands
draws the lower capacitor plate the second line draws the upper plate.

Note that macros by convention are drawn to fill the unit square.  This is
to make it easier to piece them together with the overall drawing.  It is
not mandatory that they fit the unit square, but it will make your life much
easier if you draw them that way.  Long and skinny shapes need only span the
unit square in one dimension.

The line
; ports cap 500,0 500,1000
defines where connections to our graphic symbol may be made.  In this case,
there are leads at top and bottom, so we define port 1 to be at the bottom
of our symbol in the middle, and port 2 to be at the top in the middle.
If you tell SPICE that you have a bypass cap connecting nodes 15 and 16
using the line "CBYPASS 15 16 10uF" and then draw it using the "cap" macro,
it is assumed that you want node 15 to be connected to port #1 (bottom) and
node 16 to be connected to port #2 (top).  If this is not what you wanted,
you may reverse the nodes ("CBYPASS 16 15 10uF") without changing the SPICE
meaning.  For things like transistors, it is important that you label your
"ports" in the right order.  If you find you will run into the problem of
wires crossing right over your device, you can specify an explicit routing,
or create a version of your device in a different orientation (NPN1 and NPN2
might be necessary in a diff-amp because the base lead of the BJT has a
different orientation in the 2 transistors).  I encourage others to write
the software that will extract a macro definition and create a new macro
that is the mirror image or rotation of the original.  (The mathematics is
extremely trivial, but the user interface requires some thought.)

Ports should almost always appear at the edges of the unit square, as in the
above example, to aid routing.

It is essential that the "ports" line _precede_ the corresponding
definition, so m4 doesn't do a macro expansion on the "ports" line.

As an aid to writing consistency checkers, if any of the 6 characters [iobnpg]
is found preceding a port coordinate, it is stripped and the coordinate is
processed normally.  For example,
; port IC1 i0,100 o0,200 b0,300 n0,400 p0,800 g0,900
would represent an IC with an input pin, and output pin, a bi-directional
pin, one N/C, a power (+5V) pin, and ground.  At the moment, no effort is
made to check for consistency (input pin connected to an input pin, &c.).


Freely intermixed with macro definitions are SPICE lines like these:
C1 0 1 50pF ; 250,500 cap(100)  {Amazing example shows how to make a 100pF}
C2 0 1 50pF ; 450,500 cap(100)  { capacitor by putting 2 50's in parallel.}
		      {scale cap to 10% of size of drawing}
M4 requires that macros be defined (usually with include(`file')) before
they are used.
The router requires that position information come immediately after you
define a component to SPICE. The component's position does not need to be on
the same line as the SPICE definition (you might have a lot of comments to
insert), but it must come before any drawing commands.  Note that by the
time the router sees the schematic, m4 has already expanded macros and
comments have been removed, so that the above 2 lines look something like:

C1 0 1 50pF ; 250,500 cap 100s  500,0m 500,400d 0,400d 1000,400d [etc.] S
C2 0 1 50pF ; 450,500 cap 100s  500,0m 500,400d 0,400d 1000,400d [etc.] S

For capacitor C1 above, the router finds that the component is a cap with
its lower left corner at (0.250,0.500) on the unit square, and that it is
scaled to fit a 100x100 square on the 1000-unit system or a 0.100x0.100
square on the unit square system.  Since the router has already seen a line
saying:
; ports cap 500,0 500,1000
it knows that port 1 (which is connected to node 0) is at the coordinate
(0.250 + 0.1*0.5,  0.5 + 0.1*0.0).  (Lower-left corner of the screen plus a
relative move scaled by 10%.)  Similarly for port 1 of C2.  Also, the router
knows that the only two connections to node 0 are the ones specified by C1
and C2.  So it just connects them.  A few heuristics guide the router in
drawing wires:
  -  If a wire is coming out of the top or bottom of a symbol, it is
      initially extended vertically.  Wires coming out of the left or right
      sides are initially extended horizontally.  Ports on corners are
      considered to be on the left or right side.
  -  A node's location is considered to be the average (x,y) of the ports
      which connect to it, all other things being equal.  However,
      top/bottom ports are considered to provide "better" information on
      x-coordinates, and left/right ports provide "better" y-information.
      If "better" x-information is available, the inferior information
      (provided by left/right ports) is disregarded and has no effect on
      the average.  Similarly for y-information.
      Ex: suppose the left-hand lead of a capacitor is being connected to
          the top lead of a resistor.  The x-coord of the connecting node
          is determined by the cap, the y-coord by the resistor.
  -  If all y-information was provided by "inferior" sources, i.e., only
      top/bottom ports connect to a node, we have to "average" their
      y-values to come up with a location for this node.  However, this
      can result in dismal failures, like wires going right through the
      device.  Consider 3 chips with distinct x-coordinates: chip1 near
      the bottom of the drawing (top-connection), chip2 slightly above
      it (bottom-connection), and chip3 near the top of the drawing
      (bottom-connection).  In this case, the y-coordinate of their
      common node must nestle between chips 1 & 2 near the bottom of the
      drawing, but chip3 would pull the average up too high.  So the bounds
      "node must be above y1" and "node must be below y2" are imposed, and
      since y2<y3, y3 is discarded (doesn't contribute to the average).
      If only one kind of bound is found to apply, e.g., "node must be right
      of x1" and "node must be right of x2", rather than put it on the edge
      of the device, it is put slightly to the right of the right-most device,
      spaced away from it by 50% of that device's width.  Note that this
      procedure covers, for example, the case of connecting pins 1 & 3 on a
      14-pin IC, but does nothing to resolve the ambiguous drawing that results
      when pins 2 & 4 on the same IC are also connected; this must be routed by
      hand.  If anyone has an easy solution to the "allocate space for wires"
      problem (without using storage proportional to screen resolution), I'm
all ears.
      Or, implement it as a front-end, as "node" directives may appear anywhere.
  -  A dot is always drawn to mark nodes with 3 or more connections.
  -  A node's location may always be explicitly specified with the directive
	   ; node NN xx,yy
      (except for node 0)
  -  Explicit routing information on how to get from a port to a node may
      always be given with the directive
	   ; route particular_device port_number x,y x,y x,y...
      For example,
	   ; route C1 1 300,500 300,600
      This would draw a line from port 1 of C1 to (0.3,0.5), then to (0.3,0.6),
      then to the calculated position of the node that port 1 connects to.
      This last connection will be suppressed if the last x,y specified is 0,0.
      This would be useful, for example, for showing power connections.

  -  Node zero is always treated as a special case:  the wire is extended
      horizontally or vertically away from the device by 50% of the width
      or height of the device, and then a GND symbol is drawn.  This can be
      overriden with the "route" directive -- a GND is affixed to the last x,y.

  -  [none of this stuff is implemented yet]
     [In fact, I don't even think I'll bother to figure out the width of
      skinny devices or the height of short squat ones; I'll just use the
      overall size.]



All of these pieces are organized using the following pipeline:
cat schematic | strip_braces | do_labels | m4 | router | cvt2plot | plot -T4014

You can, of course, give plot any arguments you like, depending on what
device you're using.  The strip_braces command will need a -t argument if
you want text to be output todevices capable of handling text; ordinarily
the input to cvt2plot consists strictly of vectors.

The strip_braces, m4, and router steps can each be thought of as removing
something: comments, macros, and SPICE commands.
The router stage winds up creating a temp file so the router can make a
first pass to build the connectivity graph and a second pass to output
scaled graphic vectors.


           --John Hanley
             System Programmer, Manhattan College
             ..!cmcl2.nyu.edu!manhat!jh  or  hanley@nyu.edu   (CMCL2<=>NYU.EDU)

doug-merritt@cup.portal.com (05/17/88)

I suggest that you start off each schematic with "schem v88a"
as a keyword to allow such documents to be automatically recognized
as such by software. Note that EDIF does this; each file starts
with "edif version 88989" or some such.
   Doug
---
      Doug Merritt        ucbvax!sun.com!cup.portal.com!doug-merritt
                      or  ucbvax!eris!doug (doug@eris.berkeley.edu)
                      or  ucbvax!unisoft!certes!doug