FPGA 串口FIFO回環實驗(show ahead模式)


RTL視圖

 

 

 工作流程:

(1)、當uart_rxd模塊檢測到rxd_din信號上有下降沿時,啟動uart_rxd計數器器,並准備接收數據,當收完一個完整字節時,產生data_out_vld ,用於通知FIFO准備開始寫入FIFO

(2)、當FIFO收到din_vld有效信號時,先檢測FIFO是否滿,不滿的就開始寫使能,當FIFO成功寫入數據之后, empty置0,表示FIFO不為空,可以利用這個條件來控制讀使能。

    assign wrreq = full? 1'b0 : din_vld;   //檢測FIFO是否滿,如果不滿,就將din_vld信號給寫使能

(3)、當FIFO寫入數據之后,且empty 不為空了,這個時候可以考慮去讀fifo中的數據,讀出的數據在發送出去之前,還得檢查發送模塊是否就緒txd_rdy

    assign rdreq = (empty == 0) && (din_rdy == 1); //讀使能之前,同時判斷fifo 是否為空, 且發送模塊是否准備就緒

(4)、FIFO讀使能之后,數據傳給uart_txd模塊,在發送期間,禁止讀下一個數據,否則發送數據紊亂。 只能發送完一個字節后,再繼續讀下一個字節,這樣循環操作

時序:

(1)、uart_rxd模塊檢測下降沿

 

 

 (2)、寫FIFO 讀FIFO時序

注意幾個地方同步:

(1)、寫使能和有待寫入的有效數據必須保持在同一拍,如果不在同一拍,可以將其中一個在打一拍,這樣就能保持同步

(2)、由於用的是show - ahead模式,所以讀使能和讀出的有效數據也是在同一拍,此時應該將數據取走。也就是后面 “在發送期間,進行數據鎖存,確保在發送期間數據不改變”

 

 

(3)串口發送時序

 

 

 

normal 和lshow-ahead模式:

  (1)、normal模式,在讀使能后,下一拍才把數據送到q上。

  (2)、show-ahead模式,在讀使能有效期間,FIFO就已經把第一個數據送到了q上,也就是說“讀使能”和第一個效數據“q” 保持在同一拍

 

 

 uart_rxd.v

  1 module uart_rxd(
  2                     clk,
  3                     rst_n,
  4                     rxd_din,
  5 //                    rxd_din_vld,
  6                     rxd_data_out,
  7                     data_out_vld
  8                 );
  9         
 10 parameter DATA_W        = 8;
 11 parameter BAUD_RATE     = 434;
 12 
 13 input clk;
 14 input rst_n;
 15 input rxd_din;
 16 wire  rxd_din_vld;
 17 
 18 output [DATA_W-1: 0]    rxd_data_out /* synthesis keep*/;
 19 output data_out_vld;
 20 
 21 wire add_cnt0;
 22 wire end_cnt0;
 23 
 24 wire add_cnt1;
 25 wire end_cnt1;
 26 
 27 wire rxd_sig_neg;
 28 
 29 reg rxd_din_0;
 30 reg rxd_din_1;
 31 reg rxd_din_2;
 32 reg rxd_din_3;
 33 always @(posedge clk or negedge rst_n)begin
 34     if(!rst_n)begin
 35         rxd_din_0 <= 1;
 36         rxd_din_1 <= 1;
 37         rxd_din_2 <= 1;
 38         rxd_din_3 <= 1;
 39     end
 40     else begin
 41         rxd_din_0 <= rxd_din;
 42         rxd_din_1 <= rxd_din_0;
 43         rxd_din_2 <= rxd_din_1;
 44         rxd_din_3 <= rxd_din_2;
 45     end
 46 end
 47 
 48 assign rxd_sig_neg = (rxd_din_2 == 0) && (rxd_din_3 == 1);
 49 
 50 assign rxd_din_vld = 1;
 51 
 52 reg cnt0_vld;
 53 always @(posedge clk or negedge rst_n)begin
 54     if(!rst_n)begin
 55         cnt0_vld <= 0;
 56     end
 57     else if(rxd_sig_neg && rxd_din_vld)begin
 58         cnt0_vld <= 1;
 59     end
 60     else if(end_cnt1)begin
 61         cnt0_vld <= 0;
 62     end
 63 end
 64 
 65 reg [8:0] cnt0;
 66 always @(posedge clk or negedge rst_n)begin
 67     if(!rst_n)begin
 68         cnt0 <= 0;
 69     end
 70     else if(add_cnt0)begin
 71         if(end_cnt0)begin
 72             cnt0 <= 0;
 73         end
 74         else begin
 75             cnt0 <= cnt0 + 1;
 76         end
 77     end
 78 end
 79 
 80 assign add_cnt0 = cnt0_vld == 1;
 81 assign end_cnt0 = add_cnt0 && cnt0 == BAUD_RATE - 1;
 82 
 83 reg [3:0] cnt1;
 84 always @(posedge clk or negedge rst_n)begin
 85     if(!rst_n)begin
 86         cnt1 <= 0;
 87     end
 88     else if(add_cnt1)begin
 89         if(end_cnt1)begin
 90             cnt1 <= 0;
 91         end
 92         else begin
 93             cnt1 <= cnt1 + 1;
 94         end
 95     end
 96 end
 97 
 98 assign add_cnt1 = end_cnt0;
 99 assign end_cnt1 = add_cnt1 && cnt1 == (DATA_W + 1 + 1) - 1;  //數據位寬+起始位+停止位
100 
101 reg [DATA_W-1:0] data_temp;
102 always @(posedge clk or negedge rst_n)begin
103     if(!rst_n)begin
104         data_temp <= 0;
105     end
106     else if(add_cnt0 && cnt0 == ((BAUD_RATE>>1)-1) && cnt1 >= 1 && cnt1 < DATA_W+1)begin
107         data_temp[cnt1-1] <= rxd_din_2;
108     end
109 end
110 
111 reg [DATA_W-1:0] rxd_data_out;
112 always @(posedge clk or negedge rst_n)begin
113     if(!rst_n)begin
114         rxd_data_out <= 1;
115     end
116     else if(end_cnt1)begin
117         rxd_data_out <= data_temp;
118     end
119 end
120 
121 reg data_out_vld;
122 always @(posedge clk or negedge rst_n)begin
123     if(!rst_n)begin
124         data_out_vld <= 0;
125     end
126     else if(end_cnt1)begin
127         data_out_vld <= 1;  //收滿一個字節后,表示一個有效的完整字節
128     end
129     else begin
130         data_out_vld <= 0;
131     end
132 end
133 
134 endmodule
View Code

control_fifo.v

 1 module control_fifo(
 2                         clk,
 3                         rst_n,
 4                         din_vld,
 5                         fifo_data_din,
 6                         din_rdy,//下游模塊准備好信號
 7                         dout_vld, //通知下游模塊准備收數據
 8                         fifo_data_dout
 9 );
10 parameter DATA_WRW    = 8;
11 input clk;
12 input rst_n;
13 input din_vld;
14 input [DATA_WRW-1:0] fifo_data_din;
15 input din_rdy;
16 
17 output dout_vld;
18 output[DATA_WRW-1:0] fifo_data_dout;
19 
20 wire rdreq;
21 wire wrreq;
22 wire [DATA_WRW-1:0] q/* synthesis keep*/;
23 wire [7:0]usedw;
24 my_fifo    my_fifo_inst (
25                         .clock ( clk ),
26                         .data  ( fifo_data_din ),
27                         .rdreq ( rdreq ),
28                         .wrreq ( wrreq ),
29                         .empty ( empty ),
30                         .full  ( full),
31                         .q     ( q ),
32                         .usedw ( usedw)
33     );
34     
35 assign wrreq = full? 1'b0 : din_vld;
36 
37 assign rdreq = (empty == 0) && (din_rdy == 1);
38 
39 reg [DATA_WRW-1:0] fifo_data_dout;
40 always @(posedge clk or negedge rst_n)begin
41     if(!rst_n)begin
42         fifo_data_dout <= 0;
43     end
44     else begin
45         fifo_data_dout <= q;
46     end
47 end
48 
49 reg dout_vld;
50 always @(posedge clk or negedge rst_n)begin
51     if(!rst_n)begin
52         dout_vld <= 0;
53     end
54     else begin
55         dout_vld <= rdreq;
56     end
57 end
58 
59 endmodule
View Code

uart_txd.v

  1 module uart_txd(
  2                     clk,
  3                     rst_n,
  4                     txd_din_vld,
  5                     data_din,
  6                     txd_rdy,
  7                     txd_dout
  8                 );
  9                 
 10 parameter DATA_W    = 8;
 11 parameter BAUD_RATE    = 54;
 12 
 13 input clk;
 14 input rst_n;
 15 input txd_din_vld;
 16 input [DATA_W-1:0]data_din;
 17 
 18 output txd_rdy;
 19 output txd_dout;
 20 
 21 wire add_cnt0/* synthesis keep*/;
 22 wire end_cnt0/* synthesis keep*/;
 23 
 24 wire add_cnt1;
 25 wire end_cnt1;
 26 
 27 wire [10-1:0]data_temp;
 28 
 29 reg cnt0_vld;
 30 always @(posedge clk or negedge rst_n)begin
 31     if(!rst_n)begin
 32         cnt0_vld <= 0;
 33     end
 34     else if(txd_din_vld)begin
 35         cnt0_vld <= 1;
 36     end
 37     else if(end_cnt1)begin
 38         cnt0_vld <= 0;
 39     end
 40 end
 41 
 42 reg [8:0] cnt0;
 43 always @(posedge clk or negedge rst_n)begin
 44     if(!rst_n)begin
 45         cnt0 <= 0;
 46     end
 47     else if(add_cnt0)begin
 48         if(end_cnt0)begin
 49             cnt0 <= 0;
 50         end
 51         else begin
 52             cnt0 <= cnt0 + 1;
 53         end
 54     end
 55 end
 56 
 57 assign add_cnt0 = cnt0_vld == 1;
 58 assign end_cnt0 = add_cnt0 && cnt0 == BAUD_RATE - 1;
 59 
 60 reg [3:0] cnt1;
 61 always @(posedge clk or negedge rst_n)begin
 62     if(!rst_n)begin
 63         cnt1 <= 0;
 64     end
 65     else if(add_cnt1)begin
 66         if(end_cnt1)begin
 67             cnt1 <= 0;
 68         end
 69         else begin
 70             cnt1 <= cnt1 + 1;
 71         end
 72     end
 73 end
 74 
 75 assign add_cnt1 = end_cnt0;
 76 assign end_cnt1 = add_cnt1 && cnt1 == (DATA_W + 1 + 1) - 1;  //數據位寬+起始位+停止位
 77 
 78 reg[DATA_W-1 : 0] data_buf;
 79 always @(posedge clk or negedge rst_n)begin
 80     if(!rst_n)begin
 81         data_buf <= 0;
 82     end
 83     else if(txd_din_vld)begin  //在檢測FIFO輸出的有效信號時,把數據進行鎖存,避免在發送過程中,data_buf 數據發生變化
 84         data_buf <= data_din;
 85     end
 86 end
 87 
 88 assign data_temp = {1'b1, data_buf, 1'b0};  // 停止位 + 8bit數據 + 起始位, 低位先發
 89 
 90 reg txd_dout;
 91 always @(posedge clk or negedge rst_n)begin
 92     if(!rst_n)begin
 93         txd_dout <= 1;
 94     end
 95     else if(add_cnt0 && cnt0 == 0 && cnt1 >=0 && cnt1 < (DATA_W + 1 + 1))begin
 96         txd_dout <= data_temp[cnt1];
 97     end
 98 end
 99 
100 assign txd_rdy = cnt0_vld ?  1'b0: 1'b1;
101 
102 endmodule
View Code

fifo_top.v

 1 module fifo_top(
 2                     clk,
 3                     rst_n,
 4                     rxd_din,
 5                     txd_dout
 6 );
 7 
 8 parameter DATA_W        = 8;
 9 parameter BAUD_RATE     = 54; //54 = 921600  ; 434 = 115200
10 
11 input clk;
12 input rst_n;
13 input rxd_din;
14 
15 output txd_dout;
16 
17 wire [DATA_W-1:0]    rxd_data_out;
18 wire [DATA_W-1:0]    fifo_data_dout;
19 wire                data_out_vld;
20 wire                txd_rdy;
21 wire                 txd_dout;
22 
23 uart_rxd #(.DATA_W(DATA_W),.BAUD_RATE(BAUD_RATE))    u1_rxd(
24                                                             .clk            (clk),
25                                                             .rst_n            (rst_n),
26                                                             .rxd_din        (rxd_din),
27 //                                                            .rxd_din_vld    (txd_rdy),
28                                                             .rxd_data_out    (rxd_data_out),
29                                                             .data_out_vld    (data_out_vld)
30                                                         );
31                                                         
32 uart_txd #(.DATA_W(DATA_W),.BAUD_RATE(BAUD_RATE))     u2_txd(
33                                                             .clk(clk),
34                                                             .rst_n(rst_n),
35                                                             .txd_din_vld(txd_din_vld),
36                                                             .data_din(fifo_data_dout),
37                                                             .txd_rdy(txd_rdy),
38                                                             .txd_dout(txd_dout)
39                                                     );
40 
41 
42 control_fifo #(.DATA_WRW(DATA_W))    u3_fifo(
43                                                 .clk(clk),
44                                                 .rst_n(rst_n),
45                                                 .din_vld(data_out_vld),
46                                                 .fifo_data_din(rxd_data_out),
47                                                 .din_rdy(txd_rdy),                    //下游模塊准備好信號
48                                                 .dout_vld(txd_din_vld),             //通知下游模塊准備收數據
49                                                 .fifo_data_dout(fifo_data_dout)
50                                             );
51 
52 endmodule
View Code

 

程序里的這段注釋,有特定的含義,綜合保持,/* synthesis keep*/:

就是在SignalTap II中,一些信號被優化,導致添加到觸發列表中顯示的是紅色狀態,就沒法准確查看仿真波形,所以一個這樣的注釋可以避免顯示紅色

 

 

 如果加了這個/* synthesis keep*/ 還是不好使,那么就在module ()信號列表里添加一個信號,

比如

 module   control_fifo(

          .  

          .          

          data_temp,

          .

      );

output   data_temp;

wire  data_temp /* synthesis keep*/ ;

不需要分配管腳,不影響結果,這樣方便仿真抓時序

 

 如下圖,列表中添加的txd_rdy信號處於 紅色 ,這種狀態出來的波形有可能是不對的,添加的信號必須保持 黑色 狀態才行

 

注意:

txd_rdy控制信號有點問題,txd_rdy相對rdreq信號延遲了兩拍,如果FIFO中一開始就有超過兩個或兩個以上的數據時,rdreq信號脈沖可能就不是保持一個時鍾,有可能保持兩個時鍾周期,就意味着FIFO要讀出兩個數據,沒有及時處理好的話,

很可能就會丟一個數據。 rdreq信號是組合邏輯得到的

assign rdreq = (empty == 0) && (din_rdy == 1);

assign txd_rdy = cnt0_vld ?  1'b0: 1'b1; //這里寫的條件不足,才導致延遲兩拍

 

 

 正確確時序,txd_rdy應該讀使能后下一拍就要拉低,如下圖,這樣才能保證每次讀都是讀一個字節

在uart_txd.v里,修改這句代碼,添加一個條件(cnt0_vld  || txd_din_vld )

assign txd_rdy = (cnt0_vld  || txd_din_vld )?  1'b0: 1'b1;

 

 

  那為什么測試沒有發現錯誤,那是因為寫入和讀出的速率都很慢,所以測試不出來,如果寫入的數據多且快,讀出時沒控制好,就容易出現丟數據。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM