Re: a "dispatch"-ing object system in CL



SCNR. (Will the modeling approach I outline be sufficient ?)

* (Rob Warnock) <OeKdnVTeOPByPZjanZ2dnUVZ_oCvnZ2d@xxxxxxxxxxxxx> :
| Eli Bendersky <eliben@xxxxxxxxx> 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:


;; Use a GATE class that wraps up the the bare thunk abstraction
;; presented in SICP: a GATE has upto two input wires going in an and an
;; output wire coming out. The values are driven by a generic function
;; DRIVER.

(deftype signal-value () '(member 1 0 Z X))

(defclass wire ()
((signal-value :initform 0 :type signal-value :reader get-signal
:writer set-signal)
(gates :initform nil :accessor gates)))

(defclass gate ()
((output :type wire :initarg :output :reader output)))

(defclass gate1 (gate)
((input1 :type wire :initarg :input1 :reader input1)))

(defclass gate2 (gate1)
((input2 :type wire :initarg :input2 :reader input2)))

(defgeneric inputs (gate)
(:method ((gate gate1)) (list (input1 gate)))
(:method ((gate gate2)) (list (input1 gate) (input2 gate))))

(defgeneric driver (gate))

(defmethod set-signal :around (new-value (wire wire))
(let ((old-value (get-signal wire)))
(prog1 (call-next-method)
(unless (eql old-value new-value)
(map nil #'driver (gates wire))))))

(defun %findgate (gate-type gates output &rest inputs)
(find-if (lambda (gate)
(and (typep gate gate-type)
(eql output (output gate))
(endp (set-difference (inputs gate) inputs))))
gates))

;;;
;;;
(defclass and-gate (gate2) ())

(defvar *and-gate-delay* 3)

(defmethod driver ((and-gate and-gate))
(with-slots (input1 input2 output) and-gate
(let ((new-value (logical-and (get-signal input1) (get-signal input2)))
(output output))
(after-delay *and-gate-delay*
(lambda () (set-signal new-value output))))))
(defun and-gate (a1 a2 output)
(or (%findgate 'and-gate (gates a1) output a1 a2)
(let ((gate (make-instance 'and-gate :input1 a1 :input2 a2
:output output)))
(push gate (gates a1))
(push gate (gates a2))
(driver gate)
gate)))

;;;
;;;
(defclass inverter (gate1) ())

(defvar *inverter-delay* 2)

(defmethod driver ((not-gate inverter))
(let ((new-value (logical-not (get-signal (input1 not-gate))))
(output (output not-gate)))
(after-delay *inverter-delay* (lambda () (set-signal new-value
output)))))

(defun inverter (input output)
(or (%findgate 'inverter (gates input) output input)
(let ((gate (make-instance 'inverter :input1 input :output output)))
(push gate (gates input))
(driver gate)
gate)))

;;;
;;;
(defclass or-gate (gate2) ())

(defvar *or-gate-delay* 5)

(defmethod driver ((or-gate or-gate))
(let ((new-value (logical-or (get-signal (input1 or-gate))
(get-signal (input2 or-gate))))
(output (output or-gate)))
(after-delay *or-gate-delay* (lambda () (set-signal new-value
output)))))

(defun or-gate (a1 a2 output)
(or (%findgate 'or-gate (gates a1) output a1 a2)
(let ((gate (make-instance 'or-gate :input1 a1 :input2 a2
:output output)))
(push gate (gates a1))
(push gate (gates a2))
(driver gate)
gate)))

;;; Now Model the situation where ``output of several-gates are
;;; connected to the same wire'' using a CONNECTOR object. This has One
;;; output wire and several iinput wires. (defclass connector (gate)
;;; ((inputs :initform nil :initarg :inputs)))

(defmethod inputs ((connector connector))
(with-slots (inputs) connector
inputs))

(defun connect-outputs-of-gates-with-wire (gates wire)
(make-instance 'connector :inputs (mapcar #'output gates) :output wire))

#||
| 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.]
||#

;; These rules would go into a driver method:
(defmethod driver ((gate connector))
(let* ((inputs (inputs gate))
(output (output gate))
(new-value
(cond ((and (some (lambda (s) (eql s 0)) inputs)
(notany (lambda (s) (eql s 1)) inputs)
(notany (lambda (s) (eql s 'X)) inputs)) 0)
((and (some (lambda (s) (eql s 1)) inputs)
(notany (lambda (s) (eql s 0)) inputs)
(notany (lambda (s) (eql s 'X)) inputs)) 1)
((every (lambda (s) (eql s 'Z)) inputs) 'Z)
(t 'X))))
(after-delay *and-gate-delay* (lambda () (set-signal new-value output)))))

#||
| 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.
||#

;; XXX How would TRI be used in this framework? For the other functions
;; we used above:

(defun logical-not (a)
(ecase a (0 1) (1 0) (Z 'X) (X 'Z)))

(defun logical-or (a b)
(ecase a
(0 (ecase b (0 0) (1 1) (Z 'X) (X 'X)))
(1 (ecase b (0 1) (1 1) (Z 'X) (Z 'X)))
(Z (ecase b (0 'X) (1 'X) (Z 'X) (X 'X)))
(X (ecase b (0 'X) (1 'X) (Z 'X) (X 'X)))))

(defun logical-and (a b)
(ecase a
(0 (ecase b (0 0) (1 0) (Z 'X) (X 'X)))
(1 (ecase b (0 0) (1 1) (Z 'X) (Z 'X)))
(Z (ecase b (0 'X) (1 'X) (Z 'X) (X 'X)))
(X (ecase b (0 'X) (1 'X) (Z 'X) (X 'X)))))

;; IRL we would use a priority queue but for testing this we can add
;; simple framework bits:

(defstruct queue front tail-cons)

(defun dequeue (queue)
"Remove and return the first item of QUEUE's list"
(let ((tail-cons (queue-tail-cons queue))
(old-queue (queue-front queue)))
(cond ((endp old-queue) (cerror "Return NIL" "Queue Empty"))
(T (prog1 (car old-queue)
(setf (queue-front queue) (cdr old-queue))
(when (eq old-queue tail-cons)
(setf (queue-tail-cons queue) nil)))))))

(defstruct event (timeout 0) thunk)

(defvar *event-queue* (make-queue))
(defvar *global-clock* 0)

(defun after-delay (delay thunk &key (event-queue *event-queue*)
(global-clock *global-clock*))
(let* ((event-timeout (+ global-clock delay))
(item (make-event :timeout event-timeout :thunk thunk))
(tail-cons (queue-tail-cons event-queue))
(list (queue-front event-queue)))
(if (or (endp list) (<= event-timeout (event-timeout (car list))))
(let ((new-cons (cons item list))) ; add at front of event queue
(setf (queue-front event-queue) new-cons)
(unless tail-cons (setf (queue-tail-cons event-queue) new-cons)))
(loop for prev-cons = list then head ; insert sorted
for head on (cdr list)
if (<= event-timeout (event-timeout (car head)))
do (loop-finish) finally
(let ((new-cons (cons item head)))
(setf (cdr prev-cons) new-cons)
(unless head (setf (queue-tail-cons event-queue) new-cons)))))))

(defun propagate (&key (event-queue *event-queue*))
(loop for x = (queue-front event-queue) while x do
(let* ((event (dequeue event-queue))
(thunk (event-thunk event)))
(assert (>= (event-timeout event) *global-clock*))
(setq *global-clock* (event-timeout event))
(when thunk (funcall thunk))))
*global-clock*)

;; and test the half adder like this:
(defun half-adder (a b s c)
(let ((d (make-instance 'wire :name 'd))
(e (make-instance 'wire :name 'e)))
(or-gate a b d)
(and-gate a b c)
(inverter c e)
(and-gate d e s)
(values d e)))

#||
(setq *global-clock* 0 *event-queue* (make-queue))
(defvar $input-1 (make-instance 'wire))
(defvar $input-2 (make-instance 'wire))
(defvar $sum (make-instance 'wire))
(defvar $carry (make-instance 'wire))
(half-adder $input-1 $input-2 $sum $carry)
(set-signal 1 $input-2)
(propagate)
(get-signal $sum)
||#

#:END

| 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.

I'm not sure what this means. Do you think the stuff above models your
requirements correctly so far?

I havent risked hurting my head reading below this :)
<snip>

--
Madhu
.



Relevant Pages

  • Re: a "dispatch"-ing object system in CL
    ... | | OK, heeding the advice I received in this thread, I implemented "wire" ... ;; Use a GATE class that wraps up the the bare thunk abstraction ... (defgeneric driver (gate)) ... ;;; connected to the same wire'' using a CONNECTOR object. ...
    (comp.lang.lisp)
  • Re: TECH roadshow (reds eyes)
    ... All you need is the starting point (coil lug) and connector on the driver ... This will step you through wire ...
    (rec.games.pinball)
  • My Cab Ride to Beirut (from Naomi Ragen)
    ... OK, truth be told, I didn't actually take a taxi to the capital ... Within a few minutes a taxi would pull up and the driver would ... I pointed at an electric gate in a chain-link fence that was less ... "Yossi, you have to say Tehilim for my mother at the ...
    (soc.culture.jewish.moderated)
  • Re: 3.3v logic level mosfets
    ... Isn't that 20W of dissipation? ... operation to less than 4amp as id rather use 5v for the inverter. ... the 2nc gate charge of the vn2222 quick enough. ... I had to use an IGBT and a gate driver: ...
    (sci.electronics.design)
  • Re: Capacitor Discharging with CMOS Gate
    ... John Larkin wrote: ... connector on one of our VME modules. ... your module and the customer is applying a 3dBm clock to it? ... with a JFET compound follower with hardwired 1MR pulldown from gate to gnd for a daisy chained drive, and a switchable 56R for proper relatively broadband termination as required. ...
    (sci.electronics.design)