数字电路模拟

数字电路模拟


基于 SICP 练习 3.28 - 3.32

引言

数字逻辑模拟系统是一个事件驱动的模拟 程序,一些事件引发另一些在随后时间发生的事件,它们又会引发随后的事件,并如此继续下去。

本文用 scheme 实现一个数字电路模拟器,可以估计数字系统的时延。

队列

队列标记一个队首、队尾指针的序对。

(define (front-ptr queue) (car queue))

(define (rear-ptr queue) (cdr queue))

(define (set-front-ptr! queue item) (set-car! queue item))

(define (set-rear-ptr! queue item) (set-cdr! queue item))

(define (empty-queue? queue) (null? (front-ptr queue)))

(define (make-queue) (cons '() '()))

(define (front-queue queue)
  (if (empty-queue? queue)
      (error "FRONT called with an empty queue" queue)
      (car (front-ptr queue))))

(define (insert-queue! queue item)
  (let ((new-pair (cons item '())))
    (cond ((empty-queue? queue)
           (set-front-ptr! queue new-pair)
           (set-rear-ptr! queue new-pair))
          (else
           (begin
             (set-cdr! (rear-ptr queue) new-pair)
             (set-rear-ptr! queue new-pair))))))

(define (delete-queue! queue)
  (if (empty-queue? queue)
      (error "DELETE! called with an empty queue" queue)
      (set-front-ptr! queue (cdr (front-ptr queue)))))

需要注意的是,scheme 解释器往往没有“正确地”显示这个队列。如果有调试需要,可以考虑实现一个合适的打印过程。

导线

当导线上的信号改变时,这种改变应当能引起其他导线的变化。

(define (make-wire)
  
  (define (call-each procedures)
    (if (not (null? procedures))
        (begin
          ((car procedures))
          (call-each (cdr procedures)))))
  
  (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))))

    (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 "Unknow operation -- WIRE" m))))
    
    dispatch))

函数式风格包装:

(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 (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)))


(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)))


(define (inverter input output)
  
  (define (logical-not s)
    (cond ((= s 0) 1)
          ((= s 1) 0)
          (else (error "Invalid signal" s))))
  
  (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))


(define (and-gate a1 a2 output)

  (define (logical-and s1 s2)
    (cond ((and (= s1 1) (= s2 1)) 1)
          ((and (= s1 0) (= s2 1)) 0)
          ((and (= s1 1) (= s2 0)) 0)
          ((and (= s1 0) (= s2 0)) 0)
          (else (error "Invalid signal" s1 s2))))
  
  (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))


(define (or-gate a1 a2 output)

  (define (logical-or s1 s2)
    (cond ((and (= s1 1) (= s2 1)) 1)
          ((and (= s1 0) (= s2 1)) 1)
          ((and (= s1 1) (= s2 0)) 1)
          ((and (= s1 0) (= s2 0)) 0)
          (else (error "Invalid signal" s1 s2))))
  
  (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))

事件的待处理表

时间片

时间是一种设施,发明它就是为了不让所有的事情都立刻发生。

;; time segment
(define (make-time-segment time queue)
  (cons time queue))

(define (segment-time s) (car s))

(define (segment-queue s) (cdr s))

时间片标记了一个时刻,并赋予该时刻的事件发生顺序。

待处理表

表头存放当前时间,表项存放时间片。

;; agenda
(define (make-agenda) (list 0))

(define (current-time agenda) (car agenda))

(define (set-current-time! agenda time)
  (set-car! agenda time))

(define (segments agenda) (cdr agenda))

(define (set-segments! agenda segments)
  (set-cdr! agenda segments))

(define (first-segment agenda) (car (segments agenda)))

(define (rest-segments agenda) (cdr (segments agenda)))

(define (empty-agenda? agenda)
  (null? (segments agenda)))


(define (add-to-agenda! time action agenda)
  
  (define (belongs-before? segments)
    (or (null? segments)
        (< time (segment-time (car segments)))))
  
  (define (make-new-time-segment time action)
    (let ((q (make-queue)))
      (insert-queue! q action)
      (make-time-segment time q)))
  
  (define (add-to-segments! segments)
    (if (= (segment-time (car segments)) time)
        (insert-queue! (segment-queue (car segments))
                       action)
        (let ((rest (cdr segments)))
          (if (belongs-before? rest)
              (set-cdr!
               segments
               (cons (make-new-time-segment time action)
                     (cdr segments)))
              (add-to-segments! rest)))))
  
  (let ((segments (segments agenda)))
    (if (belongs-before? segments)
        (set-segments!
         agenda
         (cons (make-new-time-segment time action)
               segments))
        (add-to-segments! segments))))


(define (remove-first-agenda-item! agenda)
  (let ((q (segment-queue (first-segment agenda))))
    (begin
      (delete-queue! q)
      (if (empty-queue? q)
          (set-segments! agenda (rest-segments agenda))))))


(define (first-agenda-item agenda)
  (if (empty-agenda? agenda)
      (error "Agenda is empty -- FIRST_AGENDA_ITEM")
      (let ((first-seg (first-segment agenda)))
        (begin
          (set-current-time! agenda (segment-time first-seg))
          (front-queue (segment-queue first-seg))))))

按这种方式,当前时间将总是最近处理的动作的时间。

信号传播

;; main loop
(define (after-delay delay action)
  (add-to-agenda! (+ delay (current-time the-agenda))
                  action
                  the-agenda))

(define (propagate)
  (if (not (empty-agenda? the-agenda))
      (let ((first-item (first-agenda-item the-agenda)))
        (begin
          (first-item)
          (remove-first-agenda-item! the-agenda)
          (propagate)))))

测试

监视器

安装监视器后,导线信号的改变不仅会引起未来的对其他导线信号的操作,还会触发监视器打印当前时刻和所监视信号。

(define (probe name wire)
  (add-action! wire
               (lambda ()
                 (begin
                   (newline)
                   (display (current-time the-agenda))
                   (display " ")
                   (display name)
                   (display " New-value = ")
                   (display (get-signal wire))))))

组合逻辑

;; test
(define the-agenda (make-agenda))
(define inverter-delay 10)
(define and-gate-delay 2)
(define or-gate-delay 2)

(define a (make-wire))
(define b (make-wire))
(define c (make-wire))

(define o (make-wire))

(and-gate a c o)
(inverter b c)

(probe 'a a)
(probe 'b b)
(probe 'c c)
(probe 'o o)

;; init event
(set-signal! a 1)
(set-signal! b 0)


(propagate)

0 a New-value = 0
0 b New-value = 0
0 c New-value = 0
0 o New-value = 0
0 a New-value = 1
10 c New-value = 1
12 o New-value = 1

值得注意的是,set-signal!添加了初始事件。整个模拟过程结束在 12 时间片处,是电路中关键路径的耗时。

时序逻辑

;; test
(define max-iteration 100)
(define the-agenda (make-agenda))
(define inverter-delay 1)
(define and-gate-delay 2)
(define or-gate-delay 2)
(define and-not-gate-delay 3)

(define S (make-wire))
(define R (make-wire))
(define Q (make-wire))
(define Q- (make-wire))


(and-not-gate S Q- Q)
(and-not-gate Q R Q-)
 
(probe 'Q Q)
(probe 'Q- Q-)
(probe 'S S)
(probe 'R R)

;; init event

(set-signal! S 1)
(set-signal! R 1)

(propagate)

0 Q New-value = 0
0 Q- New-value = 0
0 S New-value = 0
0 R New-value = 0
0 S New-value = 1
0 R New-value = 1
3 Q New-value = 1
3 Q- New-value = 1
6 Q- New-value = 0
6 Q New-value = 0
9 Q New-value = 1
9 Q- New-value = 1
12 Q- New-value = 0
12 Q New-value = 0
15 Q New-value = 1
15 Q- New-value = 1
18 Q- New-value = 0
18 Q New-value = 0
21 Q New-value = 1
21 Q- New-value = 1
24 Q- New-value = 0
24 Q New-value = 0
27 Q New-value = 1
...

震荡电路的行为也可以模拟,真是十分神奇。

关于同时发生的事件

时间片队列中动作的执行顺序必须和动作的添加顺序一致,这样才能让同一导线上信号的最新的计算结果体现出来(如果存在对同一导线的信号赋值的话)。这里和网络数据包乱序有相似性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值