<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
        版權申明:本文為博主窗戶(Colin Cai)原創,歡迎轉帖。如要轉貼,必須注明原文網址
      
        http://www.rzrgm.cn/Colin-Cai/p/12045295.html 
      
        作者:窗戶
      
        QQ/微信:6679072
      
        E-mail:6679072@qq.com

        上一章給出了組合電路的仿真實現,這一章開始思考時序電路的仿真實現。但時序電路遠比組合電路復雜的多,我們先從組成電路的每個元件說起。在程序實現層次,我們可以考慮給每個基礎元件一個自定義描述方式,稱為原語。

       

        Verilog原語

       

        Verilog提供了元件原語建模的方式,說白了,就是用一個表格來體現所有情況下的輸出。Verilog的原語只允許有一個輸出。

        比如and門,用Verilog原語來描述如下

      primitive myand(out,in1,in2);
      output out;
      input in1,in2;
      table
      // in1 in2    out
          0   ?   :  0;
          1   0   :  0;
          1   1   :  1;
      endtable
      endprimitive

          Verilog原語中不能使用高阻(因為除了三態門產生高阻輸出之外,這的確與真實電路不符,而Verilog并無VHDL那般抽象),不能表示三態門。

         對于時序電路,Verilog也一樣可以支持。所謂時序電路,意味著電路的輸出不僅僅與當前電路的輸入有關,還與電路之前的狀態有關,所謂電路之前的狀態也就是電路之前的輸出。

        我們來考慮這樣一個時序元件,稱之為D鎖存器,有兩個輸入en和in,一個輸出out。當en為0時,out和in保持一致;當en為1時,out保持不變。這稱之為電平觸發。用波形圖可以描述其特性:

        D鎖存器波形

        用verilog描述可以如下:

      module dlatch(out, en, in);
      output out;
      input en, in;
      reg out;
      always@(in)
      if(!en)
        out <= in;
      endmodule

        電平觸發的D鎖存器可以用原語描述如下:

      primitive dlatch(out, en, in);
      output out;
      input en, in;
      reg out;
      table
      //en in : out : next out
         0   0 :  ?   :  0;
         0   1 :  ?   :  1;
         1   ? :  ?   :  -;
      endtable
      endprimitive

        狀態表的最后一行next out位置的 - 符號代表狀態保持。

        再來一個我們數字設計時最常用的元件D觸發器,它有兩個輸入信號clk和in,有一個輸出信號out。當clk從0變到1的瞬間(稱之為上升沿),out被賦予in的值,其他時候out保持不變。這種觸發稱之為沿觸發。波形圖可以用以下描述其特性:

        D觸發器波形

        用Verilog描述如下:

      module dff(out, clk, in);
      output out;
      input clk, in;
      reg out;
      always@(posedge clk)
        out <= in;
      endmodule

        而用Verilog原語描述則如下:

      primitive dff(out, clk, in);
      output out;
      input clk, in;
      reg out;
      table
      // clk  in : out : next out 
        (01) 0  :  ?   : 0;
        (01) 1  :  ?   : 1;
      endtable
      endprimitive

        原語沒有寫的部分都是保持。換句話說,之前D鎖存器的原語實現table的最后一行保持是可以不寫的。

        前面的D鎖存器是電平觸發,D觸發器是沿觸發。實際上原語也可以同時支持兩種觸發。比如存在異步復位的D觸發器,多了個觸發的rst信號,在rst為1的時候,out會被賦予0。波形圖如下:

        帶復位D觸發器波形

        Verilog描述可以如下:

      module dff(out, rst, clk, in);
      output out;
      input rst, clk, in;
      reg out;
      always@(posedge rst or posedge clk)
      if(rst)
        out <= 1'b0;
      else
        out <= in;
      endmodule

        用原語描述則為:

      primitive dff(out, rst, clk, in);
      output out;
      input rst, clk, in;
      reg out;
      table
      // rst clk  in : out : next out 
           0 (01) 0  :  ?   : 0;
           0 (01) 1  :  ?   : 1;
           1  ?   ?  :  ?   : 0;
      endtable
      endprimitive

        以上的原語中就同時包含電平觸發和沿觸發。

       

        Scheme建模下的原語

       

        Verilog原語用表來表示,實際上是用表來代表一個函數關系,于是我們要做的,是試著用一個函數來代表基本元件的原語描述。

        比如與門,我們是不是可以用以下函數來描述:

      (define (myand in1 in2)
        (if (and (= in1 1) (= in2 1)) 1 0))

        上述函數方便的表示一個組合邏輯,甚至上述可以延伸到表示任意多輸入的一個與門,描述如下

      (define (myand . in)
          (if (member 0 in) 0 1))

       

        可是上述的描述并未方便的引入時序的概念,最終在仿真的時候無法區分組合邏輯和時序邏輯。從而上述的函數來代表原語描述是失敗的,需要再修改一下。

        于是我們描述函數的參數列表里不僅有當前各輸入信號,還得有當前輸出信號,考慮到沿觸發器件,還得加入沿的信息。于是我們可以定義原語是這樣的一個函數:帶有三個參數,第一個參數是輸入信號值的列表,第二個參數是當前輸出信號值,第三個參數代表沿觸發的信號,簡單起見,就用沿觸發的信號在輸入信號列表中的序號來表示,如果不是沿觸發則此處傳入-1;函數返回即將輸出的信號值。

        那么我們的任意多輸入的與門,描述如下

      (define (myand input current-output edge-)
          (if (member 0 input) 0 1))

        那么D鎖存器的原語描述如下

      (define (dlatch input current-output edge)
       (let ((en (car input)) (in (cadr input)))
        (if (= en 1) current-output
         in)))

        上面的let顯示了輸入列表是[en, in];

        D觸發器的原語描述如下,輸入列表為[clk, in]

      (define (dff
       (let ((clk (car input)) (in (cadr input)))
        (if (and (= edge 0) (= clk 1)) in
         current-output)))

        對于之前帶異步復位的D觸發器,作為一個既有電平觸發又有沿觸發的例子

      (define (dff-with-rst input current-output edge)
       (let ((rst (car input))(clk (cadr input)) (in (caddr input)))
        (cond
         ((= rst 1) 0)
         ((and (= edge 0) (= clk 1)) in)
         (else current-output))))

       

        進一步修改原語

       

        之前的設計已經完備,但未必方便。比如可能一些邏輯可編程器件的編程粒度不會細到門級。Verilog的原語里,只有一個輸出,我們可以考慮這里原語的輸出可以有多個。

        在此我們考慮一位全加器,也就是三個單bit的數相加,得到兩位輸出的組合電路,輸出信號既然可能不止一個,原語函數的輸出當然是一個列表,第二個參數current-output當然也是列表。

      (define (add input current-output edge)
       (let ((a (car input))(b (cadr input)) (c (caddr input)))
        (let* ((sum (+ a b c)) (cout (if (>= sum 2) 1 0)) (s (if (= cout 0) sum (- sum 2))))
         (list cout s))))

       

        最后,我們考慮,原語可以為每一個信號可以加一個位寬。

        在這里,我們來考慮做一個四位計數器,有一個異步復位(rst),有一個時鐘(clk),一個4位的輸出(out),每當clk上升沿,輸出都會加1,注意如果當前輸出如果是1111,下一個輸出將會是0000,描述如下

      (define (counter input current-output edge)
       (define (add1-list lst)
        (cond
         ((null? lst) '())
         ((= (car lst) 0) (cons 1 (cdr lst)))
         (else (cons 0 (add1-list (cdr lst))))))
       (let ((rst (car input)) (clk (cadr input)))
        (cond
         ((= rst 1) '((0 0 0 0)))
         ((and (= edge 1) (= clk 1)) (list (add1-list (car current-output))))
         (else current-output))))

        用0/1的list有一些不方便的地方,我們可以用數來代替,也可以考慮數和list一起支持,那么我們在處理的時候可能需要判斷一下傳入的是數還是list,Scheme里提供了兩個函數來判斷,一個是list?用來判斷是不是list,一個是number?用來判斷是不是數。在上面定義的基礎上加上對于數的支持也很容易。

       

        迭代

       

        以上雖然用函數來定義了原語,但是從函數卻沒有定義任何表示原語信號接口的東西,不看原語定義無法知道原語怎么使用,并且在仿真的時候,上述原語本身并不提供各個信號當前的值。

        本來會在后面的章節提到解決方案,在此也給個方案。

        我們可以用閉包解決這個問題,閉包中包含著輸入、輸出信號的信息。Scheme的閉包可以有多種方式,可以采用上一章中局部作用域變量的方法(這種方法并不是所有的語言都支持,比如Python則只能用class建立類了),另一種方式則是用不變量了,也就是純函數式編程方式。本章就來說說第二種方式,雖然在我之前的其他文章中說到的閉包主要是采取這種方式。

        我們先看一個簡單的例子,我們希望有這樣的需求:

        定義一個變量x

        (define x (make-sum 0))

        (set! x (x 1))

        (set! x (x 2))

        (set! x (x 3))

        (x)得到6

        這樣,每次x都是一個閉包,現在要看如何定義make-sum。

        我們先這樣定義:

      (define (make-sum n)
       (lambda (m)
        (make-sum (+ n m))))

        但是,我們馬上發現,我們要求的值變的不可提取,閉包返回的這個函數,不僅僅可以帶一個參數用來再度返回閉包,還應該可以不帶參數,以支持上面(x)這樣的提取。

        上面的實現需要一點修改,需要判斷一下參數個數:

      (define (make-sum n)
       (lambda s
        (if (null? s) n
         (make-sum (+ n (car s))))))

        測試一下,OK了,最后得到了6,說明make-sum是可行的。

        

        然后,我們可以抽象加法這個符號,繼續做算子f-step。

      (define (f-step step n)
       (lambda s
        (if (null? s) n
         (f-step step (step n (car s))))))

        這樣,make-sum可以由上述算子定義而得

      (define make-sum
       (lambda (n) (f-step + n))

       

        定義f-step算子有什么好處呢?實際上,它是為迭代的每一步動作進行建模。

        于是我們可以用f-step為零件,構建所有的迭代。

        比如對于輾轉相除法(歐幾里得算法)求最大公約數,描述如下

      (define (gcd a b)
       (if (zero? b) a
        (gcd b (remainder a b))))

        如果要用f-step,則首先要把迭代的內容表示成一個對象,可以用cons對來對gcd的兩個參數a,b打包。

        f-step的第二個參數是一個函數,我們稱之為step,step函數有兩個參數,一個是用于迭代的數據,在這里就是這個cons對,而第二個參數可以看成是外界激勵,這里是不需要的,傳任意值即可。

        我們清楚輾轉相除法的這一步,應該描述如下

      (define (step pair none)
       (cons (cdr pair) (remainder (car pair) (cdr pair))))

        反復的迭代,其終止條件是判斷pair的第二個成員是否為0,如果是0則返回pair的第一個成員,否則繼續迭代

      (define (continue-gcd-do f)
       (let ((x (f)))
        (if (zero? (cdr x)) (car x)
         (continue-gcd-do (f '())))))

        于是,我們的gcd就被重新設計了

      (define (gcd a b)
       (continue-gcd-do (f-step step (cons a b))))

        雖然看起來的確比最開始的實現復雜了不少,但是可以實現統一的設計,以便更復雜的情況下的應用。 

       

        反柯里化

       

        f-step還可以用來設計fold-left算子,我們回憶一下fold-left

        (fold-left cons 'a '(b c d))

        得到

        '(((a . b) . c) . d)

        我們可以看成是一個迭代,

        最開始是'a

        然后通過函數cons和'b,得到

        '(a . b)

        然后再通過函數cons和'c,得到

        '((a . b) . c)

        最后再通過函數cons和'd,得到

        '(((a . b) . c) . d)

        顯然,我們可以使用f-step,定義以下

        (define func (f-step cons 'a))

        那么

        (((func 'b) 'c) 'd)

        則是最后的結果。

        但這樣似乎不太好用,假如我們有這么一個函數,暫且稱為F

        ((F func) 'b 'c 'd)

        也就是

        (apply (F func) '(b c d))

        那么就容易實現了。

        F這個過程正好和我之前的文章《map的實現和柯里化(Curring)》里的柯里化過程相反,稱之為反柯里化,重新給個合適的名字叫uncurry

      (define (uncurry f)
       (lambda s
        (if (null? s) (f)
         (apply (uncurry (f (car s))) (cdr s)))))

        于是fold-left就可以如下實現

      (define (my-fold-left f init lst)
       (apply (uncurry (f-step f init)) lst))

        

        封裝

       

        繞了一圈,似乎與主題有點遠了。一個原語所表示的電路,實際上也是隨著外界輸入,在不斷的變化輸出,也可以用f-step算子來模擬。

        電路的狀態包含了電路的輸出,同時也包含著電路的輸入,因為需要判斷沿變化,當然我們只需要關注沿觸發的信號就行了,其他輸入信號不需要在狀態里。

        我們就以之前的帶復位的D觸發器為例,我們重新給出它的原語描述,并按第三節里修改之后的來,

      (define (dff-with-rst input current-output edge)
       (let ((rst (caar input))(clk (caadr input)) (in (caaddr input)))
        (cond
         ((= rst 1) '((0)))
         ((and (= edge 0) (= clk 1)) (list (list in)))
         (else current-output))))

        我們的初始狀態可以設置為

        '((z) . (z))

        之所以用z來表示,而不是0/1,在于初始的時候,我們認為都是一種渾沌的狀態,當然,也可以設為用0/1,這完全可以按仿真意愿來。

        前面第一個'(z)表示所有可以帶來沿觸發的信號列表,這里可以帶來沿觸發的是第二個信號clk,序號從0開始算為1,而輸出信號初始也先設置為'(z)

        于是狀態轉換函數則為

      (define step
      (lambda (stat input)
      (cons (cadr input) (dff
      -with-rst input (cdr stat) (if (eq? (caar stat) (caadr input)) -1 1)))))

        于是

        (f-step step '(() . ()))則是一個原語的實例封裝,里面包含著狀態,可以用來在仿真中反復迭代。

      posted on 2020-01-20 11:21  窗戶  閱讀(932)  評論(0)    收藏  舉報

      主站蜘蛛池模板: 国产亚洲精品VA片在线播放| 99re热视频这里只精品 | 美腿丝袜亚洲综合第一页| 日韩av中文字幕有码| 99久久精品国产免费看| 国产午夜福利不卡在线观看| 一级片一区二区中文字幕| 九九热精品免费视频| 国产精品白丝久久AV网站| 日韩永久永久永久黄色大片| 久久久精品2019中文字幕之3| 99精品国产一区二区三| 国产午夜福利在线视频| 国产一区二区三区黄色片| 国产女人18毛片水真多1| 国产亚洲一区二区三区av| A级毛片100部免费看| 亚洲性日韩精品一区二区| 精品国产一区二区三区性色| 国产精品久久久久无码网站| 亚洲国产在一区二区三区| 亚洲一区二区三区黄色片| 亚洲欧美人成人让影院| xxxxbbbb欧美残疾人| 日韩亚洲国产激情一区二区| 亚洲精品成人7777在线观看| 色综合久久久久综合体桃花网| 日韩一区二区三区女优丝袜| 老司机性色福利精品视频| 亚洲国产精品综合久久20| 亚洲激情一区二区三区视频| 亚洲中文字幕av天堂| 亚洲精品国偷自产在线| 人妻蜜臀久久av不卡| 人人综合亚洲无线码另类| 久久精品道一区二区三区 | 国产精品久久久久aaaa| 欧美老少配性行为| 日韩精品亚洲精品第一页| 亚洲国产成人精品无码区在线观看| 嫩草欧美曰韩国产大片|