(load "util.scm") (load "misc/agenda.scm") (load "misc/queue.scm") (display "\nexample - simulator of digital circuits\n") (define (call-each procedures) (if (null? procedures) 'done (begin ((car procedures)) (call-each (cdr procedures))))) (define (make-wire) (let ((signal-value 0) (action-procedures '())) (define (set-my-signal! new-value) (if (not (= signal-value new-value)) (begin (set! signal-value new-value) (call-each action-procedures)) 'done)) (define (accept-action-procedure! proc) (set! action-procedures (cons proc action-procedures)) (proc)) (define (dispatch m) (cond ((eq? m 'get-signal) signal-value) ((eq? m 'set-signal!) set-my-signal!) ((eq? m 'add-action!) accept-action-procedure!) (else (error "Unknown operation -- WIRE" m)))) dispatch)) (define (probe name wire) (add-action! wire (lambda () (newline) (display name) (display " ") (display (current-time the-agenda)) (display " New-value = ") (display (get-signal wire))))) (define (get-signal wire) (wire 'get-signal)) (define (set-signal! wire new-value) ((wire 'set-signal!) new-value)) (define (add-action! wire action-procedure) ((wire 'add-action!) action-procedure)) (define the-agenda (make-agenda)) (define inverter-delay 2) (define and-gate-delay 3) (define or-gate-delay 5) (define (logical-not s) (cond ((= s 0) 1) ((= s 1) 0) (else (error "NOT - invalid signal" s)))) (define (logical-and s1 s2) (cond ((and (= s1 0) (= s2 0)) 0) ((and (= s1 0) (= s2 1)) 0) ((and (= s1 1) (= s2 0)) 0) ((and (= s1 1) (= s2 1)) 1) (else (error "AND - invalid signal")))) (define (logical-or s1 s2) (cond ((and (= s1 0) (= s2 0)) 0) ((and (= s1 0) (= s2 1)) 1) ((and (= s1 1) (= s2 0)) 1) ((and (= s1 1) (= s2 1)) 1) (else (error "OR - invalid signal")))) (define (half-adder a b s c) (let ((d (make-wire)) (e (make-wire))) (or-gate a b d) (and-gate a b c) (inverter c e) (and-gate d e s) 'ok)) (define (full-adder a b c-in sum c-out) (let ((s (make-wire)) (c1 (make-wire)) (c2 (make-wire))) (half-adder b c-in s c1) (half-adder a s sum c2) (or-gate c1 c2 c-out) 'ok)) (define (inverter input output) (define (invert-input) (let ((new-value (logical-not (get-signal input)))) (after-delay inverter-delay (lambda () (set-signal! output new-value))))) (add-action! input invert-input) 'ok) (define (and-gate a1 a2 output) (define (and-action-procedure) (let ((new-value (logical-and (get-signal a1) (get-signal a2)))) (after-delay and-gate-delay (lambda () (set-signal! output new-value))))) (add-action! a1 and-action-procedure) (add-action! a2 and-action-procedure) 'ok) (assert (logical-and 1 0) 0) (assert (logical-and 1 1) 1) (display "\nex-3.28 - or-gate\n") (define (or-gate a1 a2 output) (define (or-action-procedure) (let ((new-value (logical-or (get-signal a1) (get-signal a2)))) (after-delay or-gate-delay (lambda () (set-signal! output new-value))))) (add-action! a1 or-action-procedure) (add-action! a2 or-action-procedure) 'ok) (assert (logical-or 0 1) 1) (assert (logical-or 1 1) 1) (display "\nex-3.29 - or-gate via and-gate and inverter\n") (define (nand-gate a1 a2 output) (let ((b (make-wire))) (and-gate a1 a2 b) (inverter b output) 'ok)) (define (or-gate-via a1 a2 output) (let ((na1 (make-wire)) (na2 (make-wire))) (inverter a1 na1) (inverter a2 na2) (nand-gate na1 na2 output) 'ok)) ; The or-gate has three inverter delays and one and-gate delay. The first two ; inverters can run in parallel so if we implement after-delay correct the ; total delay should only be two inverter delays and one and-gate delay. (define a (make-wire)) (define b (make-wire)) (define out (make-wire)) ;(probe 'out out) (or-gate-via a b out) (set-signal! a 1) (set-signal! b 1) (propagate) (assert (get-signal out) 1) (set-signal! a 0) (set-signal! b 0) (propagate) (assert (get-signal out) 0) (display "\nex-3.30 - ripple-carry-adder\n") ; Each full-adder has a delay of three or-gates, four and-gates and two ; inverters. Again, some of chips in the half-adder could run in parallel, so ; if after-delay is implemented accordingly the time will be shorter. The delay ; of the ripple-carry-adder is the delay of the full-adder times the number of ; input signals n. (define (ripple-carry-adder as bs ss c) (define c-in (make-wire)) (define (wire-ripple-carry-adder as bs ss c-in) (let ((ak (car as)) (bk (car bs)) (sk (car ss)) (ck (make-wire))) (if (null? (cdr ss)) (full-adder ak bk c-in sk c) (begin (full-adder ak bk c-in sk ck) (wire-ripple-carry-adder (cdr as) (cdr bs) (cdr ss) ck))))) (wire-ripple-carry-adder as bs ss c-in)) (let ((a1 (make-wire)) (a2 (make-wire)) (a3 (make-wire)) (b1 (make-wire)) (b2 (make-wire)) (b3 (make-wire)) (s1 (make-wire)) (s2 (make-wire)) (s3 (make-wire)) (co (make-wire))) (ripple-carry-adder (list a1 a2 a3) (list b1 b2 b3) (list s1 s2 s3) co) (set-signal! a1 1) (set-signal! a2 1) (set-signal! a3 1) (set-signal! b1 1) (set-signal! b2 1) (set-signal! b3 0) (propagate) (assert (get-signal s1) 0) (assert (get-signal s2) 1) (assert (get-signal s3) 0) (assert (get-signal co) 1)) (display "\nex-3.31 - accept-action-procedure!\n") (display "[see comment]\n") (define input-1 (make-wire)) (define input-2 (make-wire)) (define sum (make-wire)) (define carry (make-wire)) ; (probe 'sum sum) ; (probe 'carry carry) (set-signal! input-1 1) (set-signal! input-2 1) (half-adder input-1 input-2 sum carry) (propagate) (assert (get-signal sum) 0) (assert (get-signal carry) 1) ; It is necessary to call the action procedure upon the initialization of the ; gate to make sure that all wires are set to their correct value initially. ; For the example with the half-adder the carry-bit stays at zero because the ; AND-gate is not initialized to the correct value for two 1s as input. To ; avoid the issue we would have to make sure that each signal changes at least ; one. (display "\nex-3.32\n") (display "[see comment]\n") (define input-1 (make-wire)) (define input-2 (make-wire)) (define out (make-wire)) (and-gate input-1 input-2 out) ; (probe 'out out) (set-signal! input-1 0) (set-signal! input-2 1) (propagate) (assert (get-signal out) 0) (set-signal! input-1 1) (set-signal! input-2 0) (propagate) (assert (get-signal out) 0) ; Executing the queued action items out of order could lead to unexpected ; states. In the example above, if we set input-1 to 1 first, and then input-2 ; to 0, we would expect the output to change to 1 for one and-gate-delay. If we ; probe the output signal we can see that this is indeed what happens. Now, if ; the actions would not be processed in FIFO order, the output would never ; switch to 1.