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;

 

 

  那为什么测试没有发现错误,那是因为写入和读出的速率都很慢,所以测试不出来,如果写入的数据多且快,读出时没控制好,就容易出现丢数据。

 

 

 

posted @ 2021-12-15 17:12  MyBooks  阅读(798)  评论(0编辑  收藏  举报