#lang racket
(require racket/mpair)
;; CONSTANTS
(define inverter-delay 2)
(define and-gate-delay 3)
(define or-gate-delay 5)
;; LOGIC FUNCTIONS
(define (valid-signal? s) (member s '(0 1)))
(define (logical-not s)
(unless (valid-signal? s) (error "Invalid signal" s))
(if (zero? s) 1 0))
(define (logical-and s1 s2)
(unless (valid-signal? s1) (error "Invalid signal" s1))
(unless (valid-signal? s2) (error "Invalid signal" s2))
(* s1 s2))
(define (logical-or s1 s2)
(unless (valid-signal? s1) (error "Invalid signal" s1))
(unless (valid-signal? s2) (error "Invalid signal" s2))
(if (positive? (+ s1 s2)) 1 0))
;; LOGIC GATES
(define (inverter input output)
(define (invert-input)
(define 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)
(define 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)
(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)
;; ADDERS
(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))
;; WIRES
(define (make-wire)
(define signal-value 0)
(define action-procedures empty)
(define (set-my-signal! new-value)
(cond [(not (= signal-value new-value))
(set! signal-value new-value)
(call-each action-procedures)]
[else 'done]))
(define (accept-action-procedure! proc)
(set! action-procedures (mcons 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 (call-each procedures)
(for ([p procedures]) (p))
'done)
(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))
;; QUEUES FROM 3.3.2
(define (front-ptr queue) (mcar queue))
(define (rear-ptr queue) (mcdr queue))
(define (set-front-ptr! queue item) (set-mcar! queue item))
(define (set-rear-ptr! queue item) (set-mcdr! queue item))
(define (empty-queue? queue) (null? (front-ptr queue)))
(define (make-queue) (mcons empty empty))
(define (front-queue queue)
(if (empty-queue? queue)
(error "FRONT called with an empty queue" queue)
(mcar (front-ptr queue))))
(define (insert-queue! queue item)
(define new-pair (mcons item empty))
(cond [(empty-queue? queue)
(set-front-ptr! queue new-pair)
(set-rear-ptr! queue new-pair)
queue]
[else
(set-mcdr! (rear-ptr queue) new-pair)
(set-rear-ptr! queue new-pair)
queue]))
(define (delete-queue! queue)
(cond [(empty-queue? queue)
(error "DELETE! called with an empty queue" queue)]
[else
(set-front-ptr! queue (mcdr (front-ptr queue)))
queue]))
;; AGENDAS
;; Segments are time, queue pairs. The queue holds all the actions to be
;; performed at that time.
(define (make-time-segment time queue)
(mcons time queue))
(define (segment-time s) (mcar s))
(define (segment-queue s) (mcdr s))
;; An agenda is a table of segments sorted by time. The head of the table is the
;; current time.
(define (make-agenda) (mlist 0))
(define (current-time agenda) (mcar agenda))
(define (set-current-time! agenda time) (set-mcar! agenda time))
(define (segments agenda) (mcdr agenda))
(define (set-segments! agenda segments) (set-mcdr! agenda segments))
(define (first-segment agenda) (mcar (segments agenda)))
(define (rest-segments agenda) (mcdr (segments agenda)))
(define (empty-agenda? agenda) (empty? (segments agenda)))
(define (add-to-agenda! time action agenda)
(define (belongs-before? segments)
(or (empty? segments)
(< time (segment-time (mcar segments)))))
(define (make-new-time-segment time action)
(define q (make-queue))
(insert-queue! q action)
(make-time-segment time q))
(define (add-to-segments! segments)
(cond [(= (segment-time (mcar segments)) time)
(insert-queue! (segment-queue (mcar segments))
action)]
[else
(define rest-sgmnts (mcdr segments))
(if (belongs-before? rest-sgmnts)
(set-mcdr! segments
(mcons (make-new-time-segment time action)
(mcdr segments)))
(add-to-segments! rest-sgmnts))]))
(define sgmnts (segments agenda))
(if (belongs-before? sgmnts)
(set-segments! agenda
(mcons (make-new-time-segment time action)
sgmnts))
(add-to-segments! sgmnts)))
(define (remove-first-agenda-item! agenda)
(define q (segment-queue (first-segment agenda)))
(delete-queue! q)
(when (empty-queue? q)
(set-segments! agenda (rest-segments agenda))))
(define (first-agenda-item agenda)
(cond [(empty-agenda? agenda)
(error "Agenda is empty -- FIRST-AGENDA-ITEM")]
[else
(define first-seg (first-segment agenda))
(set-current-time! agenda (segment-time first-seg))
(front-queue (segment-queue first-seg))]))
;; after-delay, propagate and probe
(define (after-delay delay action)
(add-to-agenda! (+ delay (current-time the-agenda))
action
the-agenda))
(define (propagate)
(cond [(empty-agenda? the-agenda)
'done]
[else
(define first-item (first-agenda-item the-agenda))
(first-item)
(remove-first-agenda-item! the-agenda)
(propagate)]))
(define (probe name wire)
(add-action! wire
(lambda ()
(printf "~a ~a New-value = ~a ~n"
name
(current-time the-agenda)
(get-signal wire)))))
;; TEST
;; Use ripple-carry-adder to add 111 to 001 to get 1000.
(define the-agenda (make-agenda))
(define a1 (make-wire))
(define a2 (make-wire))
(define a3 (make-wire))
(set-signal! a1 1)
;; 'done
(set-signal! a2 1)
;; 'done
(set-signal! a3 1)
;; 'done
(define as (list a1 a2 a3))
(define b1 (make-wire))
(define b2 (make-wire))
(define b3 (make-wire))
(set-signal! b1 0)
;; 'done
(set-signal! b2 0)
;; 'done
(set-signal! b3 1)
;; 'done
(define bs (list b1 b2 b3))
(define s1 (make-wire))
(define s2 (make-wire))
(define s3 (make-wire))
(set-signal! s1 0)
;; 'done
(set-signal! s2 0)
;; 'done
(set-signal! s3 0)
;; 'done
(define ss (list s1 s2 s3))
(define c (make-wire))
(probe 's1 s1)
;; s1 0 New-value = 0
(probe 's2 s2)
;; s2 0 New-value = 0
(probe 's3 s3)
;; s3 0 New-value = 0
(probe 'c c)
;; c 0 New-value = 0
(ripple-carry-adder as bs ss c)
;; 'ok
;; Note that all the s wires and the c wire now have probe in their
;; action-procedures lists. So probe will run any time one of them changes state.
(propagate)
;; s3 8 New-value = 1
;; s2 8 New-value = 1
;; s1 8 New-value = 1
;; s3 16 New-value = 0
;; s2 32 New-value = 0
;; c 48 New-value = 1
;; s1 48 New-value = 0
;; 'done
(probe 's1 s1)
;; s1 48 New-value = 0
(probe 's2 s2)
;; s2 48 New-value = 0
(probe 's3 s3)
;; s3 48 New-value = 0
(probe 'c c)
;; c 48 New-value = 1
Comments
0 B
|👍
/👎
0 B
|👍
/👎