#lang racket
(require racket/mpair)
;;;;;;;;;;
;; 3.28 ;;
;;;;;;;;;;
(define (or-gate a1 a2 output)
(define (or-action-procedure)
(define 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)
;;;;;;;;;;
;; 3.29 ;;
;;;;;;;;;;
;; Defining an or-gate this way, the or-gate-delay would equal one and-gate-delay
;; plus two inverter-delay's because the first two inverters can run
;; simultaneously, but then the other gates must run in sequence.
(define (alternate-or-gate a1 a2 output)
(define b1 (make-wire))
(define b2 (make-wire))
(define c (make-wire))
(inverter a1 b1)
(inverter a2 b2)
(and-gate b1 b2 c)
(inverter c output)
'ok)
;;;;;;;;;;
;; 3.30 ;;
;;;;;;;;;;
(define (half-adder a b s c)
(define d (make-wire))
(define 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)
(define s (make-wire))
(define c1 (make-wire))
(define c2 (make-wire))
(half-adder b c-in s c1)
(half-adder a s sum c2)
(or-gate c2 c2 c-out)
'ok)
(define (ripple-carry-adder as bs ss c)
; The book goes from index n to index 1; we go from n - 1 to 0.
(unless (= (length as)
(length bs)
(length ss))
(error "Lists must be same length -- RIPPLE-CARRY-ADDER" as bs ss))
(define zero-wire (make-wire))
(set-signal! zero-wire 0)
(define (loop i carry)
(cond [(< i 0) 'ok]
[else
; use c for the last carry
(define next-carry (if (zero? i) c (make-wire)))
(full-adder (list-ref as i)
(list-ref bs i)
carry
(list-ref ss i)
next-carry)
(loop (sub1 i) next-carry)]))
(loop (sub1 (length as))
zero-wire))
;; How much is one ripple-carry-delay in terms of inverter-delay, or-gate-delay,
;; and and-gate-delay?
;; Look at the sicp-3-3-4-test-cases file to see these test runs.
;; For the given delay times:
;; inverter-delay = 2
;; and-gate-delay = 3
;; or-gate-delay = 5
;; we had:
;; 1 half-adder-delay = 8 = 1 and-gate-delay + 1 or-gate-delay
;; Surprisingly, one full-adder-delay was also 8, and the ripple-carry-adder-delay,
;; for n = 3, was 48 = 2 * n * (or-gate-delay + and-gate-delay).
;; I would not have gotten this right just by looking at the formulas. The
;; function boxes trigger whenever an input wire changes state. They do not
;; necessarily run in sequence. For me that makes it hard to see.
;; Also I think the answer depends on the exact delay times for the primitive
;; gates. If one primitive gate were a bottleneck, that would change the function
;; box delay times in terms of the primitive gate delay times. For example, if
;; the inverter delay were huge compared to the others, the half-adder-delay would
;; be one and-gate-delay plus one inverter-delay, instead of one and-gate-delay
;; plus one or-gate-delay.
;;;;;;;;;;
;; 3.31 ;;
;;;;;;;;;;
;; Here is some half-adder test code:
;; TRY A HALF-ADDER
(displayln 'HALF-ADDER)
(define in5 (make-wire))
(set-signal! in5 1)
(define in6 (make-wire))
(define sum (make-wire))
(define carry (make-wire))
(displayln 'BEFORE)
(probe 'in5 in5)
(probe 'in6 in6)
(probe 'sum sum)
(probe 'carry carry)
(displayln 'DURING)
(half-adder in5 in6 sum carry)
(propagate)
(displayln 'AFTER)
(probe 'in5 in5)
(probe 'in6 in6)
(probe 'sum sum)
(probe 'carry carry)
(printf "~n")
;; When you run this "with proc", you get:
;; HALF-ADDER
;; 'done
;; BEFORE
;; in5 0 New-value = 1
;; in6 0 New-value = 0
;; sum 0 New-value = 0
;; carry 0 New-value = 0
;; DURING
;; 'ok
;; sum 8 New-value = 1
;; 'done
;; AFTER
;; in5 8 New-value = 1
;; in6 8 New-value = 0
;; sum 8 New-value = 1
;; carry 8 New-value = 0
;; When you run this "without proc", you get:
;; HALF-ADDER
;; 'done
;; BEFORE
;; DURING
;; 'ok
;; 'done
;; AFTER
;; So even though running add-action! without proc will add the proc to the wire's
;; action-procedures list, it is not getting put on the agenda. So when we later
;; call propagate, nothing happens. This is because after-delay is the function
;; that adds actions to the agenda, and after-delay is not called until we run the
;; proc.
;; For example, if you look at inverter, the proc that gets added to the
;; action-procedures list for the input wire is invert-input. But invert-input
;; calls after-delay, and after-delay calls add-to-agenda!. So you have to call
;; the proc to put it on the agenda.
;;;;;;;;;;
;; 3.32 ;;
;;;;;;;;;;
;; Even though all the actions in a given segment occur at the same time, their
;; order still matters, some come before others. If the segment used a LIFO
;; container like a stack, then the actions would be performed in the wrong order.
;; We need a FIFO container like a queue to preserve the correct ordering.
;; To get LIFO behavior, I used the deque construction from 3.3.2. Then you just
;; had to change insert-queue! to front-insert-deque! etc. This was easier than
;; trying to rewrite the book program to use a list.
;; I couldn't get the exact book example to work. But I did get this example:
;; TRY AN AND-GATE
(define in1 (make-wire))
(set-signal! in1 1)
(define in2 (make-wire))
(set-signal! in2 0)
(define out (make-wire))
(and-gate in1 in2 out)
(set-signal! in2 1)
(probe 'in1 in1)
(probe 'in2 in2)
(probe 'out out)
(propagate)
;; Here we have two wires, in1 = 1 and in2 = 0, then we apply an and, then we
;; change the 0 to a 1, and then we propagate. So the out wire should be 1, which
;; it is with the FIFO queue:
;; 'done
;; 'done
;; 'ok
;; 'done
;; in1 0 New-value = 1
;; in2 0 New-value = 1
;; out 0 New-value = 0
;; out 3 New-value = 1
;; 'done
;; However, with the LIFO deque, out first becomes 1 at time 3 but then reverts to
;; 0 as the original set-signal! of in2 to 0 is hit coming out of the segment:
;; 'done
;; 'done
;; 'ok
;; 'done
;; in1 0 New-value = 1
;; in2 0 New-value = 1
;; out 0 New-value = 0
;; out 3 New-value = 1
;; out 3 New-value = 0
;; 'done
;; So the book is right. Order within segments matters. We need a FIFO
;; container.
Comments
0 B
|👍
/👎
0 B
|👍
/👎