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
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
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
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
程序里的这段注释,有特定的含义,综合保持,/* 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;
那为什么测试没有发现错误,那是因为写入和读出的速率都很慢,所以测试不出来,如果写入的数据多且快,读出时没控制好,就容易出现丢数据。