赞
踩
异步串口通信(UART):通信中发送端与接收端的时钟不一致。
同步串口通信(USART):通信中发送端与接收端的时钟一致。
UART:速率最高2Mbps(基本出现到达极限),2Mbps已经不稳定,1Mbps接收1w个byte误码率为0。
从板上出来的时钟不建议直接使用,建议使用PLL/MMCM来输出稳定时钟。
首先,UART的目标是实现一个稳定的波特率,同时尽可能的降低误码率。uart时序网上很多,这边不进行描述。其中分频,复位等时序就不占用空间了。
tx部分:
tx发送时采用AXI4协议中握手信号,以自身输出的ready信号与上级输入的vaild信号进行握手进行数据传输。此时会产生一个tx时钟的握手信号,进行开始握手。那么接下来可以考虑ready的输出。在握手后,立刻拉低ready,进行数据输出。数据输出完后,拉高ready,ready分有校验位拉高和无校验位拉高。
对于发送数据,采用计数器开始计数。一般情况下以ready拉低后进行计数,结束条件与上述ready类似。
在ready拉低期间,不断右移数据,将最低位数据不断通过tx发送出去。
- module uart_tx#(
- parameter P_SYSTEM_CLK = 50_000_000 , //输入时钟
- parameter P_UART_BUADRATE = 9600 , //波特率
- parameter P_UART_DATA_WIDTH = 8 , //数据宽度
- parameter P_UART_STOP_WIDTH = 1 , //1 or 2
- parameter P_UART_CHECK = 0 //None = 0 ,Event = 1, Odd = 2
- )
- (
- input wire i_clk ,
- input wire i_rst ,
-
- output wire o_uart_tx ,
-
- input wire [P_UART_DATA_WIDTH - 1 : 0] i_user_tx_data , //用户发送的数据
- input wire i_user_tx_data_vaild,
- output wire o_user_tx_data_ready
- );
-
- /***************function**************/
-
- /***************parameter*************/
-
- /***************port******************/
-
- /***************mechine***************/
-
- /***************reg*******************/
- reg ro_uart_tx ;
- reg ro_user_tx_data_ready ;
- reg [15:0] r_cnt ; //计数器位宽高于16 bits时,组合逻辑过高,谨慎使用
- reg [P_UART_DATA_WIDTH - 1 : 0] r_tx_data ;
- reg r_tx_check ;
-
-
- /***************wire******************/
- wire w_tx_active ;
- /***************component*************/
-
- /***************assign****************/
- assign o_uart_tx = ro_uart_tx ;
- assign o_user_tx_data_ready = ro_user_tx_data_ready ;
-
- assign w_tx_active = i_user_tx_data_vaild & o_user_tx_data_ready ;
-
- /***************always****************/
- always @(posedge i_clk or negedge i_rst) begin
- if (i_rst) begin
- ro_user_tx_data_ready <= 'd1;
- end else if (w_tx_active) begin
- ro_user_tx_data_ready <= 'd0;
- end else if (r_cnt == 2 + P_UART_DATA_WIDTH + P_UART_STOP_WIDTH - 3 && P_UART_CHECK == 0 ) begin
- ro_user_tx_data_ready <= 'd1;
- end else if (r_cnt == 2 + P_UART_DATA_WIDTH + P_UART_STOP_WIDTH - 2 && P_UART_CHECK > 0 ) begin
- ro_user_tx_data_ready <= 'd1;
- end else begin
- ro_user_tx_data_ready <= ro_user_tx_data_ready;
- end
- end
-
- always @(posedge i_clk or negedge i_rst) begin
- if (i_rst) begin
- r_cnt <= 'd0;
- end else if (r_cnt == 2 + P_UART_DATA_WIDTH + P_UART_STOP_WIDTH - 3 && P_UART_CHECK == 0) begin //2 表示 开始位 校验位
- r_cnt <= 'd0;
- end else if (r_cnt == 2 + P_UART_DATA_WIDTH + P_UART_STOP_WIDTH - 2 && P_UART_CHECK > 0) begin
- r_cnt <= 'd0;
- end else if (!ro_user_tx_data_ready) begin
- r_cnt <= r_cnt + 1'd1;
- end else begin
- r_cnt <= r_cnt;
- end
- end
-
- always @(posedge i_clk or negedge i_rst) begin
- if (i_rst) begin
- r_tx_data <= 'd0;
- end else if (w_tx_active) begin
- r_tx_data <= i_user_tx_data;
- end else if (!ro_user_tx_data_ready) begin
- r_tx_data <= r_tx_data >> 1;
- end else begin
- r_tx_data <= r_tx_data;
- end
- end
- always @(posedge i_clk or negedge i_rst) begin
- if (i_rst) begin
- ro_uart_tx <= 'd1;
- end else if (w_tx_active) begin
- ro_uart_tx <= 'd0;
- end else if ((r_cnt == 3 + P_UART_DATA_WIDTH - 3) && P_UART_CHECK > 0) begin //开启校验位
- ro_uart_tx <= P_UART_CHECK == 1 ? ~r_tx_check : r_tx_check; //判断是奇还是偶
- end else if ((r_cnt == 3 + P_UART_DATA_WIDTH - 3) && P_UART_CHECK == 0) begin //未开启校验,直接发送停止
- ro_uart_tx <= 'd1;
- end else if ((r_cnt >= 3 + P_UART_DATA_WIDTH - 2) && P_UART_CHECK > 0) begin //开启了校验,发送完校验,直接发送停止
- ro_uart_tx <= 'd1;
- end else if (!ro_user_tx_data_ready) begin
- ro_uart_tx <= r_tx_data[0];
- end else begin
- ro_uart_tx <= 'd1;
- end
- end
-
- always @(posedge i_clk or negedge i_rst) begin
- if (i_rst)begin
- r_tx_check <= 'd0;
- end else if (r_cnt == 3 + P_UART_DATA_WIDTH - 3) begin
- r_tx_check <= 'd0;
- end else begin
- r_tx_check <= r_tx_check ^ r_tx_data[0];
- end
- end
-
- endmodule
rx模块:
输入数据没有采用打两拍,是在顶层以50M时钟进行超采样。降低了亚稳态
- module uart_rx#(
- parameter P_SYSTEM_CLK = 50_000_000 , //输入时钟
- parameter P_UART_BUADRATE = 9600 , //波特率
- parameter P_UART_DATA_WIDTH = 8 , //数据宽度
- parameter P_UART_STOP_WIDTH = 1 , //1 or 2
- parameter P_UART_CHECK = 0 //None = 0 ,Odd = 1, Event = 2
- )
- (
- input wire i_clk ,
- input wire i_rst ,
-
- input wire i_uart_rx ,
-
- output wire [P_UART_DATA_WIDTH - 1 : 0] o_user_rx_data ,
- output wire o_user_rx_data_vaild
- );
-
- /***************function**************/
-
- /***************parameter*************/
-
- /***************port******************/
-
- /***************mechine***************/
-
- /***************reg*******************/
- reg [P_UART_DATA_WIDTH - 1 : 0] ro_user_rx_data ;
- reg ro_user_rx_data_vaild ;
- reg [ 1:0] ri_uart_rx ;
- reg [15:0] r_cnt ;
- reg r_rx_check ;
-
- /***************wire******************/
-
- /***************component*************/
-
- /***************assign****************/
- assign o_user_rx_data = ro_user_rx_data ;
- assign o_user_rx_data_vaild = ro_user_rx_data_vaild ;
- /***************always****************/
- always @(posedge i_clk or negedge i_rst) begin
- if (i_rst)begin
- ri_uart_rx <= 2'b11;
- end else begin
- ri_uart_rx <= {ri_uart_rx[0],i_uart_rx};
- end
- end
- always @(posedge i_clk or negedge i_rst) begin
- if (i_rst)begin
- r_cnt <= 'd0;
- end else if (r_cnt == 2 + P_UART_DATA_WIDTH + P_UART_STOP_WIDTH - 2 && P_UART_CHECK == 0) begin
- r_cnt <= 'd0;
- end else if (r_cnt == 2 + P_UART_DATA_WIDTH + P_UART_STOP_WIDTH - 1 && P_UART_CHECK > 0) begin
- r_cnt <= 'd0;
- end else if (i_uart_rx == 1'b0 || r_cnt > 0) begin
- r_cnt <= r_cnt + 'd1;
- end else begin
- r_cnt <= r_cnt;
- end
- end
-
- always @(posedge i_clk or negedge i_rst) begin
- if (i_rst)begin
- ro_user_rx_data <= 'd0;
- end else if (r_cnt >= 1 && r_cnt <= P_UART_DATA_WIDTH) begin
- ro_user_rx_data <= {i_uart_rx,ro_user_rx_data[P_UART_DATA_WIDTH - 1 :1]};
- end else begin
- ro_user_rx_data <= ro_user_rx_data;
- end
- end
- always @(posedge i_clk or negedge i_rst) begin
- if (i_rst)begin
- ro_user_rx_data_vaild <= 'd0;
- end else if (r_cnt == P_UART_DATA_WIDTH + 0 && P_UART_CHECK == 0) begin
- ro_user_rx_data_vaild <= 'd1;
- end else if (r_cnt == P_UART_DATA_WIDTH + 1 && P_UART_CHECK == 1 && i_uart_rx == !r_rx_check) begin
- ro_user_rx_data_vaild <= 'd1;
- end else if (r_cnt == P_UART_DATA_WIDTH + 1 && P_UART_CHECK == 2 && i_uart_rx == r_rx_check) begin
- ro_user_rx_data_vaild <= 'd1;
- end else begin
- ro_user_rx_data_vaild <= 'd0;
- end
- end
-
- always @(posedge i_clk or negedge i_rst) begin
- if (i_rst)begin
- r_rx_check <= 'd0;
- end else if (r_cnt >= 1 && r_cnt <= P_UART_DATA_WIDTH) begin
- r_rx_check <= r_rx_check ^ i_uart_rx;
- end else begin
- r_rx_check <= 'd0;
- end
- end
最终采用回环输出。采用fifo进行在中间缓存,以115200波特率发送10000bytes误码率为0.以1Mbps发送误码率为0。黑金Spartan 7 开发板跑的结果如下:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。