1. 程式人生 > >【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗十三:串列埠模組② — 接收

【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗十三:串列埠模組② — 接收

實驗十三:串列埠模組② — 接收

我們在實驗十二實現了串列埠傳送,然而這章實驗則要實現串列埠接收 ... 在此,筆者也會使用其它思路實現串列埠接收。

clip_image002

圖13.1 模組之間的資料傳輸。

假設我們不考慮波特率,而且一幀資料之間的傳輸也只是發生在FPGA之間,即兩隻模組之間互轉,並且兩塊模組都使用相同的時鐘頻率,結果如圖13.1所示。只要成立上述的假設成立,串列埠傳輸不過是簡單的資料傳輸活動而已,圖中的傳送模組經由TXD將一幀11位的資料傳送至接收模組。

clip_image004

圖13.2 傳送與接收一幀資料。

至於兩者之間的時序過程,則如圖13.2所示 ... 傳送方經由TXD,從T0~T10總共傳送一幀為11位的資料,反之接收方則從T2~T9讀取其中的8位資料而已(D為暫存器的暫存內容)。從圖13.2當中,我們可以看見傳送方,即TXD都是經由上升沿傳送未來值,接收方D則是經由上升沿讀取過去值。對此,Verilog可以這樣描述,結果如程式碼13.1所示:

      //傳送方
      reg [10:0]rTXD;
     always @(posedge CLOCK)
         case(i)
            0,1,2,3,4,5,6,7,8,9,10:      
            begin TXD <= rTXD[i]; i <= i + 1'b1; end
            ......
         endcase
      //接收方
      reg [7:0]D1;
     always @(posedge CLOCK)
         case(i)
           2,3,4,5,6,7,8,9:      
            begin D1[i] <= TXD; i <= i + 1'b1; end
            ......
         endcase

程式碼13.1

如程式碼13.1所示,傳送方在步驟0~10一共傳送一幀為11位的資料 ... 反之接收方,則在步驟2~9讀取其中的資料[7:0]。心機重的朋友的一定會疑惑道,為什麼筆者要換個角度去思考串列埠怎樣接收呢?原因其實很簡單,目的就是為了簡化理解,腦補時序,實現精密控制。

對此,FPGA與其它裝置互轉資料,其實可以反映成兩隻模組正在互轉資料,然而理想時序就是關鍵。因為Verilog無法描述理想以外的時序,對此所有時序活動都必須看成理想時序。

clip_image006

圖13.3 FPGA接收一幀波特率為115200的資料。

當FPGA接收一幀資料為波特率115200之際,情況差不多如圖13.3所示。50Mhz是FPGA的時鐘源,也是一幀資料的採集時鐘,RXD則是一幀資料的輸入端。波特率為115200的一位資料經過50Mhz的時鐘量化以後,每一位資料大約保持8.68us,即434個時鐘。

串列埠傳輸沒有自己的時鐘訊號,所以我們必須利用FPGA的時鐘源“跟蹤”每一位資料。對此,FPGA只能借用計數器“同步跟蹤”而已,至於Verilog則可以這樣描述,結果如程式碼13.2所示:

    0,1,2,3,4,5,6,7,8,9,10:  //同步跟蹤中 ...        
    if( C1 == 434 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end
    else C1 <= C1 + 1'b1; 

程式碼13.2

如程式碼13.2所示,所謂同步跟蹤,就是利用計數器估計每一位資料 ... 期間,步驟0~10表示每一位資料,至於C1計數434個時鐘則是同步跟蹤中。其中 -1 考慮了步驟之間的跳轉所耗掉的時鐘。

clip_image008

圖13.4 讀取起始位。

我們都知道串列埠的一幀資料都是從拉低的起始位開始,然而為了完美尾行,亦即實現精密控時,起始位的讀取往往都是關鍵。如圖13.4所示,當我們在第一個時鐘讀取(採集)起始位的時候,由於Verilog的讀取只能經過讀取過去值而已,餘下起始位還有433個時鐘需要我們跟蹤,為此Verilog可以這樣描述,結果如程式碼13.3所示:

    0: 
    if( RXD == 1'b0 ) begin i <= i + 1'b1; C1 <= C1 + 4'd1; end         
    1: // stalk start bit
    if( C1 == BPS115K2 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end 
    else C1 <= C1 + 1'b1;

程式碼13.3

如程式碼13.3所示,步驟0用來檢測起始位,如果RXD的電平為拉低狀態,C1立即遞增以示同步跟蹤已經用掉一個時鐘,同樣也可以看成i進入下一個步驟用掉一個時鐘。然而步驟1是用來跟蹤餘下的433個時鐘,但是計數器C1不是從0開始計數,而是從1開始計算,因為C1在步驟已經遞增的緣故。

clip_image010

圖13.5 讀取一幀資料當中的資料位。

一幀資料的跟蹤結果與讀取結果如圖13.5所示 ... 除了起始位,我們使用了兩個步驟採集並跟蹤之餘,接下來便用8個步驟資料一邊跟蹤一邊採集所有資料位,然而採集的時候則是1/4週期,即每位資料的第108個時鐘。最後的校驗位及結束位則是跟蹤而已。對此,Verilog 可以這樣表示,結果如程式碼13.4所示:

    0: 
    if( RXD == 1'b0 ) begin i <= i + 1'b1; C1 <= C1 + 4'd1; end 
    1: // start bit
    if( C1 == 434 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end 
    else C1 <= C1 + 1'b1;
    2,3,4,5,6,7,8,9: 
    begin
        if( C1 == 108 ) D1[i-2] <= RXD;
        if( C1 == 434 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end
        else C1 <= C1 + 1'b1;                           
    end
    10,11: // parity bit & stop bit
    if( C1 == 434 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end
    else C1 <= C1 + 1'b1;

程式碼13.4

如程式碼13.4所示,步驟0~1用來採集與跟蹤起始位,步驟2~9則用來跟蹤資料位,並且採集為1/4週期。步驟10~11則用來跟蹤校驗位於結束位。理解完畢以後,我們便可以開始建模了。

clip_image012

圖13.6 實驗十三的建模圖。

圖13.6是實驗十三的建模圖,rx_demo組合模組包含RX功能模組與核心操作。RX功能模組的左方連結至RXD頂層訊號,它主要是負責一幀資料的接收,然後反饋給核心操作。核心操作則負責RX功能模組的使能工作,當它領取完成訊號以後,變槳回收回來的資料再經由TXD頂層訊號傳送出去。

rx_funcmod.v

clip_image014

圖13.7 RX功能模組的建模圖。

圖13.7是RX功能模組的建模圖,左方連結至頂層訊號RXD,右方則是問答訊號還有8位的oData。

1.    module rx_funcmod
2.    (
3.         input CLOCK, RESET, 
4.         input RXD,
5.         input iCall,
6.         output oDone, 
7.         output [7:0]oData
8.    );
9.        parameter BPS115K2 = 9'd434, SAMPLE = 9'd108;
10.          

以上內容是相關的出入端宣告,第9行則是波特率為115200的常量宣告,其外還有采集的週期。

11.         reg [3:0]i;
12.         reg [8:0]C1;
13.         reg [7:0]D1;
14.         reg isDone;
15.         
16.         always @ ( posedge CLOCK or negedge RESET )
17.             if( !RESET )
18.                  begin
19.                         i <= 4'd0;
20.                         C1 <= 9'd0;
21.                         D1 <= 8'd0;
22.                         isDone <= 1'b0;
23.                    end

以上內容行是相關的暫存器宣告,第17~22行則是這些暫存器的復位操作。

24.              else if( iCall )
25.                  case( i )
26.                         
27.                         0: 
28.                         if( RXD == 1'b0 ) begin i <= i + 1'b1; C1 <= C1 + 4'd1; end 
29.                         
30.                         1: // start bit
31.                         if( C1 == BPS115K2 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end 
32.                         else C1 <= C1 + 1'b1;
33.                         
34.                         2,3,4,5,6,7,8,9: //stalk and count 1~8 data's bit , sample data at 1/2 for bps
35.                         begin
36.                            if( C1 == SAMPLE ) D1[i-2] <= RXD;
37.                            if( C1 == BPS115K2 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end
38.                            else C1 <= C1 + 1'b1;          
39.                         end
40.                         
41.                         10,11: // parity bit & stop bit
42.                         if( C1 == BPS115K2 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end
43.                         else C1 <= C1 + 1'b1;
44.                         
45.                         12:
46.                         begin isDone <= 1'b1; i <= i + 1'b1; end
47.                         
48.                         13:
49.                         begin isDone <= 1'b0; i <= 4'd0; end
50.                    
51.                    endcase
52.                    

以上內容是核心操作。第24行的 if( iCall ) 表示該模組不使能便不工作。步驟0~1用來判斷與跟蹤起始位;步驟2~9用來跟蹤並且讀取當中的資料位;步驟10至11則是用來跟蹤校驗位與停止位而已。步驟12~13則用來反饋完成訊號,以示一次性的接收工作已經完成。

53.        assign oDone = isDone;
54.        assign oData = D1;
55.        
56.    endmodule

以上內容是輸出驅動宣告。

rx_demo.v

rx_demo的連線佈局請瀏覽回圖13.6,至於核心操作的內容請瀏覽程式碼。

1.    module rx_demo
2.    (
3.         input CLOCK, RESET, 
4.         input RXD,
5.         output TXD
6.    );

以上內容是相關的出入端宣告。

7.         wire DoneU1;
8.         wire [7:0]DataU1;
9.        
10.        rx_funcmod U1
11.         (
12.             .CLOCK( CLOCK ),
13.              .RESET( RESET ),
14.              .RXD( RXD ),    // < top
15.              .iCall( isRX ),    // < core
16.              .oDone( DoneU1 ),  // > core
17.              .oData( DataU1 )   // > core
18.         );
19.         

以上內容為是RX功能模組的例項化,第7~8是連線宣告。

20.         parameter B115K2 = 9'd434, TXFUNC = 5'd16;
21.         
22.         reg [4:0]i,Go;
23.         reg [8:0]C1;
24.         reg [10:0]D1;
25.         reg rTXD;
26.         reg isRX;
27.         
28.         always @ ( posedge CLOCK or negedge RESET )
29.             if( !RESET )
30.                  begin 
31.                         i <= 5'd0;
32.                         C1 <= 9'd0;
33.                         D1 <= 11'd0;
34.                         rTXD <= 1'b1;
35.                         isRX<= 1'b0;
36.                    end

以上內容為相關的暫存器宣告以及復位操作。第20行是波特率為115200常量宣告之餘還有偽函式的入口地址。第22~26行是相關的暫存器宣告,第29~33行則是這些暫存器的復位操作。

37.              else
38.                  case( i )
39.                    
40.                         0:
41.                         if( DoneU1 ) begin isRX <= 1'b0; D1 <= { 2'b11,DataU1,1'b0 }; i <= TXFUNC; Go <= 5'd0; end
42.                         else isRX <= 1'b1; 
43.                         
44.                         /**********/
45.                         
46.                         16,17,18,19,20,21,22,23,24,25,26:
47.                         if( C1 == B115K2 -1 ) begin C1 <= 8'd0; i <= i + 1'b1; end
48.                         else begin rTXD <= D1[i - 16]; C1 <= C1 + 1'b1; end
49.                         
50.                         27:
51.                         i <= Go;
52.                        
53.                    endcase
54.                    

以上內容為核心操作。步驟16~27是傳送一幀資料的偽函式,筆者直接將TX功能整合進來。步驟0則是用來接收完成反饋,並且準備好傳送輸數,然後i指向偽函式。

55.        assign TXD = rTXD;
56.                               
57.    endmodule

以上內容是相關的輸出驅動宣告。編譯完畢便下載程式,串列埠除錯助手設定為 115200 波特率,8位資料位,奇偶校驗位隨便,停止位1。事後,每當串列埠除錯助手想FPGA傳送什麼資料,FPGA也會回饋串列埠除錯助手,不過僅限於一幀又有間隔的資料而已。目前是實驗十三還不能支援資料流的接收,因為實驗十三沒有空間緩衝資料流 ... 此外,核心操作沒傳送一幀資料也有一定的時間耽誤。

細節一:完整的個體模組

實驗十三的RX功能模組已經是完整的個體模組,可以直接拿來呼叫。

相關推薦

黑金原創教程FPGA那些事兒-驅動I 實驗流水燈模組

實驗一:流水燈模組 對於發展商而言,動土儀式無疑是最重要的任務。為此,流水燈實驗作為低階建模II的動土儀式再適合不過了。廢話少說,我們還是開始實驗吧。 圖1.1 實驗一建模圖。 如圖1.1 所示,實驗一有名為 led_funcmod的功能模組。如果無視環境訊號(時鐘訊號還有復位訊號),該功能模組只有

黑金原創教程FPGA那些事兒-驅動I 連載導讀

前言: 無數晝夜的來回輪替以後,這本《驅動篇I》終於編輯完畢了,筆者真的感動到連鼻涕也流下來。所謂驅動就是認識硬體,還有前期建模。雖然《驅動篇I》的硬體都是我們熟悉的老友記,例如UART,VGA等,但是《驅動篇I》貴就貴在建模技巧的昇華,亦即低階建模II。 話說低階建模II,讀過《建模篇》的朋友多少也會面

黑金原創教程FPGA那些事兒-驅動I 實驗按鍵模組② — 點選與長點選

實驗三:按鍵模組② — 點選與長點選 實驗二我們學過按鍵功能模組的基礎內容,其中我們知道按鍵功能模組有如下操作: l 電平變化檢測; l 過濾抖動; l 產生有效按鍵。 實驗三我們也會z執行同樣的事情,不過卻是產生不一樣的有效按鍵: l 按下有效(點選); l 長按下有效(長點選)。 圖3

黑金原創教程FPGA那些事兒-驅動I 實驗按鍵模組

實驗二:按鍵模組① - 消抖 按鍵消抖實驗可謂是經典中的經典,按鍵消抖實驗雖曾在《建模篇》出現過,而且還惹來一堆麻煩。事實上,筆者這是在刁難各位同學,好讓對方的慣性思維短路一下,但是慘遭口水攻擊 ... 面對它,筆者宛如被甩的男人,對它又愛又恨。不管怎麼樣,如今 I’ll be back,筆者再也不會重複一

黑金原創教程FPGA那些事兒-驅動I 實驗數碼管模組

實驗六:數碼管模組 有關數碼管的驅動,想必讀者已經學爛了 ... 不過,作為學習的新儀式,再爛的東西也要溫故知新,不然學習就會不健全。黑金開發板上的數碼管資源,由始至終都沒有改變過,筆者因此由身懷念。為了點亮多位數碼管從而顯示數字,一般都會採用動態掃描,然而有關動態掃描的資訊請怒筆者不再重複。在此,同樣也是

黑金原創教程FPGA那些事兒-驅動I 實驗按鍵模組③ — 單擊與雙擊

實驗四:按鍵模組③ — 單擊與雙擊 實驗三我們建立了“點選”還有“長點選”等有效按鍵的多功能按鍵模組。在此,實驗四同樣也是建立多功能按鍵模組,不過卻有不同的有效按鍵。實驗四的按鍵功能模組有以下兩項有效按鍵: l 單擊(按下有效); l 雙擊(連續按下兩下有效)。 圖4.1 單擊有效按鍵,時序示意圖

黑金原創教程FPGA那些事兒-驅動I 實驗按鍵模組④ — 點選,長點選,雙擊

實驗五:按鍵模組④ — 點選,長點選,雙擊 實驗二至實驗四,我們一共完成如下有效按鍵: l 點選(按下有效) l 點選(釋放有效) l 長擊(長按下有效) l 雙擊(連續按下有效) 然而,不管哪個實驗都是隻有兩項“功能”的按鍵模組而已,如今我們要建立三項“功能”的按鍵模組,亦即點選(按下有效),長

黑金原創教程FPGA那些事兒-驅動I 原創教程連載導讀連載完成,共二十九章

前言: 無數晝夜的來回輪替以後,這本《驅動篇I》終於編輯完畢了,筆者真的感動到連鼻涕也流下來。所謂驅動就是認識硬體,還有前期建模。雖然《驅動篇I》的硬體都是我們熟悉的老友記,例如UART,VGA等,但是《驅動篇I》貴就貴在建模技巧的昇華,亦即低階建模II。 話說低階建模II,讀過《建模篇》的朋友多少也會面

黑金原創教程FPGA那些事兒-驅動I 實驗二十一SDRAM模組④ — 頁讀寫 β

實驗二十一:SDRAM模組④ — 頁讀寫 β 未進入主題之前,讓我們先來談談一些重要的體外話。《整合篇》之際,筆者曾經比擬Verilog如何模仿for迴圈,我們知道for迴圈是順序語言的產物,如果Verilog要實現屬於自己的for迴圈,那麼它要考慮的東西除了步驟以外,還有非常關鍵的時鐘。 for(

黑金原創教程FPGA那些事兒-驅動I 實驗PS/2模組④ — 普通滑鼠

實驗十:PS/2模組④ — 普通滑鼠 學習PS/2鍵盤以後,接下來就要學習 PS/2 滑鼠。PS/2滑鼠相較PS/2鍵盤,驅動難度稍微高了一點點,因為FPGA(從機)不僅僅是從PS/2滑鼠哪裡讀取資料,FPGA還要往滑鼠裡寫資料 ... 反之,FPGA只要對PS/2鍵盤讀取資料即可。然而,最傷腦筋的地方就在

黑金原創教程FPGA那些事兒-驅動I 實驗十八SDRAM模組① — 單字讀寫

實驗十八:SDRAM模組① — 單字讀寫 筆者與SDRAM有段不短的孽緣,它作為冤魂日夜不斷糾纏筆者。筆者嘗試過許多方法將其退散,不過屢試屢敗的筆者,最終心情像橘子一樣橙。《整合篇》之際,筆者曾經大戰幾回兒,不過內容都是點到即止。最近它破蠱而出,日夜不停:“好~痛苦!好~痛苦!”地呻吟著,嚇得筆者不敢半夜如

黑金原創教程FPGA那些事兒-驅動I 實驗二十七TFT模組

實驗二十七:TFT模組 - 顯示 所謂TFT(Thin Film Transistor)就是眾多LCD當中,其中一種支援顏色的LCD,相較古老的點陣LCD(12864笑),它可謂高階了。黑金的TFT LCD除了320×240大小以外,內建SSD1289控制器,同時也是獨立模組。事實上,無論是驅動點陣LCD還

黑金原創教程FPGA那些事兒-驅動I 實驗十三串列模組② — 接收

實驗十三:串列埠模組② — 接收 我們在實驗十二實現了串列埠傳送,然而這章實驗則要實現串列埠接收 ... 在此,筆者也會使用其它思路實現串列埠接收。 圖13.1 模組之間的資料傳輸。 假設我們不考慮波特率,而且一幀資料之間的傳輸也只是發生在FPGA之間,即兩隻模組之間互轉,並且兩塊模組都使用相同的時

黑金原創教程FPGA那些事兒-驅動I 實驗PS/2模組① — 鍵盤

實驗七:PS/2模組① — 鍵盤 實驗七依然也是熟爛的PS/2鍵盤。相較《建模篇》的PS/2鍵盤實驗,實驗七實除了實現基本的驅動以外,我們還要深入解PS/2時序,還有PS/2鍵盤的行為。不過,為了節省珍貴的頁數,怒筆者不再重複有關PS/2的基礎內容,那些不曉得的讀者請複習《建模篇》或者自行谷歌一下。 市場

黑金原創教程FPGA那些事兒-驅動I 實驗PS/2模組③ — 鍵盤與多組合鍵

實驗九:PS/2模組③ — 鍵盤與多組合鍵 筆者曾經說過,通碼除了單位元組以外,也有雙位元組通碼,而且雙位元組通碼都是 8’hE0開頭,別名又是 E0按鍵。常見的的E0按鍵有,<↑>,<↓>,<←>,<→>,<HOME>,<PRTSC>

黑金原創教程FPGA那些事兒-驅動I 實驗二十五SDHC模組

實驗二十五:SDHC模組 筆者曾經說過,SD卡發展至今已經衍生許多版本,實驗二十四就是針對版本SDV1.×的SD卡。實驗二十四也說過,CMD24還有CMD17會故意偏移地址29,讓原本範圍指向從原本的232 變成 223,原因是SD卡讀寫一次都有512個位元組。為此我們可以這樣計算: SDV1.x = 2

黑金原創教程FPGA那些事兒-驅動I 實驗二十SDRAM模組③ — 頁讀寫 α

實驗二十:SDRAM模組③ — 頁讀寫 α 完成單字讀寫與多字讀寫以後,接下來我們要實驗頁讀寫。醜話當前,實驗二十的頁讀寫只是實驗性質的東西,其中不存在任何實用價值,筆者希望讀者可以把它當成頁讀寫的熱身運動。 表示20.1 Mode Register的內容。 Mode Register

黑金原創教程FPGA那些事兒-驅動I 實驗二十八TFT模組

實驗二十八:TFT模組 - 觸屏 讀者在上一個實驗所玩弄過的 TFT LCD模組,除了顯示大小為 320 × 240,顏色為16位RGB的影象資訊以外,它還支援觸屏。所謂觸屏就是滑鼠還有鍵盤以外的輸入手段,例如現在流行平板還有智慧手機,觸屏輸入對我們來說,已經成為日常的一部分。描述語言一門偏向硬體的語言

黑金原創教程FPGA那些事兒-驅動I 實驗二十二SDRAM模組⑤ — FIFO讀寫

經過漫長的戰鬥以後,我們終於來到最後。對於普通人而言,頁讀寫就是一名戰士的墓碑(最終戰役) ... 然而,怕死的筆者想透過這個實驗告訴讀者,旅程的終點就是旅程的起點。一直以來,筆者都在煩惱“SDRAM是否應該成為儲存類?”SDRAM作為一介儲存資源(儲存器),它的好處就是大容量空間,壞處則就是麻煩的控制規

黑金原創教程FPGA那些事兒-驅動I 實驗十四儲存模組

實驗十四比起動手筆者更加註重原理,因為實驗十四要討論的東西,不是其它而是低階建模II之一的模組類,即儲存模組。接觸順序語言之際,“儲存”不禁讓人聯想到變數或者陣列,結果它們好比資料的暫存空間。 1. int main() 2. { 3. int VarA; 4.