Subject: Re: a "dispatch"-ing object system in CL
From: rpw3@rpw3.org (Rob Warnock)
Date: Thu, 04 Oct 2007 21:26:55 -0500
Newsgroups: comp.lang.lisp
Message-ID: <OeKdnVTeOPByPZjanZ2dnUVZ_oCvnZ2d@speakeasy.net>
Eli Bendersky  <eliben@gmail.com> wrote:
+---------------
| OK, heeding the advice I received in this thread, I implemented "wire"
| as a CLOS class and defined the appropriate generics and methods for
| it. I chose a class eventually (and not a struct with closures) to
| allow extension, because it appears to me that such a "wire" class
| might be extended with special wire types. ...
+---------------

Great! Here's a hopefully-simple exercise for you: Redfine your WIRE
type so that the SIGNAL-VALUE is constrained to exactly four values:
0, 1, Z, & X (or in CL, maybe 0, 1, nil, & T would be better choices,
you decide), where the SIGNAL-VALUE of a WIRE is:

   0 if the wire is connected to one or more drivers which are
     actively driving a "0", and is connected to no drivers which
     are actively driving a "1". Any number of connected drivers
     may also be driving with "Z"; they have no effect as long as
     at least one driver is driving a "0".

   1 if the wire is connected to one or more drivers which are
     actively driving a "1", and is connected to no drivers which
     are actively driving a "0". Any number of connected drivers
     may also be driving with "Z"; they have no effect as long as
     at least one driver is driving a "1".

   Z if all of the drivers connected to the wire are driving with "Z".

   X in any other case. That is, if the wire is being driven to
     "1" by some driver(s) and "0" by other(s), or if *any* driver
     is driving an "X". [I.e., a "bus collision" or otherwise
     indeterminate state.]

Now also redefine all of your gates to implement their logic in
a similar fashion, e.g., the tables for NOT, TRI, OR, & AND become
as follows [note that any "Z" as an input must be treated as an
"X" value, since it's level is arbitrary & unknown]:

     NOT (inverter)     TRI (non-inverting driver with output enable)
    Input | Output     Input | Enable | Output
    ------+-------     ------+--------+-------
      0   |   1          0   |   1    |   0
      1   |   0          1   |   1    |   1
      Z   |   X          Z   |   1    |   X
      X   |   X          X   |   1    |   X
                        (any)|   0    |   Z

     OR     Input1         AND     Input1
        \  0  1  Z  X          \  0  1  Z  X
     I   +-----------       I   +-----------
     n 0 | 0  1  X  X       n 0 | 0  0  X  X
     p 1 | 1  1  X  X       p 1 | 0  1  X  X
     u Z | X  X  X  X       u Z | X  X  X  X
     t X | X  X  X  X       t X | X  X  X  X
     2                      2

When the outputs of several gates are connected to the same wire,
the result is adjusted according to the SIGNAL-VALUE definitions
given above.

This is not a pointless exercise, since this is actually how hardware
simulation languages such as Verilog represent values [well, at least
for the "behavioral" parts of a design, as well as in the C FFI].


Advanced exercise: Now that you've got all *that* working, add
a subclass of WIRE called, say, WIRE/LIGHT-PULLUP, which never
resolves to the value of "Z". Instead, if the resulting SIGNAL-VALUE
would be "Z", let it be "1" instead. Verify that this doesn't
break anything else.


*Really* advanced exercise: Add floating-point "strengths" to all
wires and gate outputs, something like:

    (declare-strength (TRI (0 3.4) (1 0.75) (Z .005))
		      (NOT (0 3) (1 3))
		      (AND (0 3) (1 3))
		      (AND (0 3) (1 3))
		      (WIRE)
		      (WIRE/LIGHT-PULLUP (1 .05)))

plus some way [not shown here, 'cuz I'm not sure what syntax I'd use]
for declaring how/when to map a collection of drive strengths into
an "X" or a "Z". That is, you should be able to say something like,
"When the sum of all drive strengths is less than .1, the result
is a Z" or "When the sum of drive-to-1 strengths is greater than 1.0
*and* the sum of drive-to-{0,Z} strengths is less than 0.2, then it's
a 1", or "When at least any two of the sums of drive-to-{0,1,X} is
greater than 0.3, then the result is an X". That sort of thing.

These rules should be properties of the respective circuit element
classes, and overrideable in subclasses [hmmm... or maybe using mixins,
that might be better], so you don't have to individually declare the
strengths of each WIRE or TRI instance.

Now you're starting to get close to how a *real* hardware simulation
language works...  ;-}


-Rob

p.s. Oops! I forgot to mention: Semi-orthogonally to drive strengths,
add the concept of gate and/or wire "delays", which delay the effect
of changes in inputs on outputs. Advanced version: Make each delay come
in two parts: the delay from an input change until the output *can*
change, and the delay from an input change until the output *must*
change. Between those two times, the output must drive "X".

[Delays are only semi-orthogonal to drive strengths, since if you
want to get *really* advanced you put delay parameters *on* drive
strengths! That is, during the first "t0" ns the output doesn't
change; then until "t1" it drives an "X" with strength "s1", and
then at "t2" it drives the correct output (computed from the inputs),
but only at strength "s2". And finally, at "t3" it drives the same
value as at "t2", but now with strength "s3". And of course, each
of those strengths is actually a vector of drive-{0,1,Z,X} strengths,
depending upon what the computed output of the component is.]

-----
Rob Warnock			<rpw3@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607