Verilog UART通信实现
此代码实现了波特率低至300pbs,且速率可调的UART 8N1通信协议,顶层模块为UART回环测试
综合后的RTL图
1. UART接收模块
1 //UART接收模块 2 module uart_rx #( 3 parameter SYS_CLK = 50_000_000, //50MHz系统时钟,用于计数器 4 parameter UART_BPS = 115200 //波特率 5 )( 6 input clk, //系统输入时钟 7 input rst_n, //系统复位信号,低电平有效 8 input rxd, //UART数据接收信号 9 10 output reg [7:0] rxd_data, //UART数据接收存储器 11 output reg rxd_done //UART一帧数据接收完成标志,高表示接收完成 12 ); 13 14 localparam BPS_CNT_MAX = SYS_CLK / UART_BPS; //UART 1bit说需要的时钟周期数 15 16 reg rxd_d1; //对UART rxd数据信号打第一拍存储器 17 reg rxd_d2; //对UART rxd数据信号打第二拍存储器 18 reg rxd_flag; //UART接收数据采集标志 19 reg [17:0] bps_cnt; //UART 1bit计数器,可支持波特率300bps计数 20 reg [3:0] rxd_bit_cnt; //UART数据接收位数标志计数器,即标志现在第几位开始接收 21 reg [7:0] data; //UART数据接收暂存器 22 23 wire rxd_start_flag; //UART一帧数据开始标识 24 25 //下降沿检测,即检测到低为一帧的开始,如果在传输过程中怎么识别?如前一位是1,后面是0, 26 //所以增加了一个rxd_flag接收标志信号,只要一帧开始,则此信号就为高,后面的操作也是基于此信号,从而避开这个开始信号的低电平 27 assign rxd_start_flag = ((~rxd_d1) && rxd_d2); 28 29 //对UART rxd数据信号打第一拍 30 always @(posedge clk or negedge rst_n) 31 if(!rst_n) 32 rxd_d1 <= 1'b0; 33 else 34 rxd_d1 <= rxd; 35 36 //对UART rxd数据信号打第二拍 37 always @(posedge clk or negedge rst_n) 38 if(!rst_n) 39 rxd_d2 <= 1'b0; 40 else 41 rxd_d2 <= rxd_d1; 42 43 //接收数据采集标志信号处理 44 always @(posedge clk or negedge rst_n) 45 if(!rst_n) 46 rxd_flag <= 1'b0; 47 else if(rxd_start_flag) 48 rxd_flag <= 1'b1; 49 else if((rxd_bit_cnt == 4'd9) && (bps_cnt == BPS_CNT_MAX / 2)) //在停止位半位时间时,将接收数据采集标志信号设为失效 50 rxd_flag <= 1'b0; 51 else 52 rxd_flag <= rxd_flag; 53 54 //UART 1bit计时 55 always @(posedge clk or negedge rst_n) 56 if(!rst_n) 57 bps_cnt <= 18'b0; 58 else if(rxd_flag) 59 begin 60 if(bps_cnt == BPS_CNT_MAX - 1'b1) 61 bps_cnt <= 18'b0; 62 else 63 bps_cnt <= bps_cnt + 1'b1; 64 end 65 else 66 bps_cnt <= 18'b0; 67 68 //UART接收位标志,即标志现在第几位开始接收 69 always @(posedge clk or negedge rst_n) 70 if(!rst_n) 71 rxd_bit_cnt <= 4'b0; 72 else if(rxd_flag) 73 begin 74 if(bps_cnt == BPS_CNT_MAX - 1'b1) 75 rxd_bit_cnt <= rxd_bit_cnt + 1'b1; 76 else 77 rxd_bit_cnt <= rxd_bit_cnt; 78 end 79 else 80 rxd_bit_cnt <= 4'b0; 81 82 83 //接收完成信号标志 84 //其实可以一接受完数据就可以将rxd_done置1,即去掉bps_cnt == BPS_CNT_MAX / 2 85 always @(posedge clk or negedge rst_n) 86 if(!rst_n) 87 rxd_done <= 1'b0; 88 else if(rxd_bit_cnt == 4'd9) 89 rxd_done <= 1'b1; 90 else 91 rxd_done <= 1'b0; 92 93 //接收数据,从低位开始接收 94 always @(posedge clk or negedge rst_n) 95 if(!rst_n) 96 data <= 8'b0; 97 else if(rxd_flag) 98 if(bps_cnt == BPS_CNT_MAX / 2) 99 case(rxd_bit_cnt) 100 4'd1: data[0] <= rxd_d2; 101 4'd2: data[1] <= rxd_d2; 102 4'd3: data[2] <= rxd_d2; 103 4'd4: data[3] <= rxd_d2; 104 4'd5: data[4] <= rxd_d2; 105 4'd6: data[5] <= rxd_d2; 106 4'd7: data[6] <= rxd_d2; 107 4'd8: data[7] <= rxd_d2; 108 default: 109 data <= data; 110 endcase 111 else 112 data <= data; 113 else 114 data <= 8'b0; 115 116 //接收完成标志为高时,则表示数据接收完成,将数据暂存器中的数据给到输出 117 always @(posedge clk or negedge rst_n) 118 if(!rst_n) 119 rxd_data <= 8'b0; 120 else if(rxd_done) 121 rxd_data <= data; 122 else 123 rxd_data <= 8'b0; 124 125 endmodule
2. UART发送模块
1 //UART发送模块 2 module uart_tx #( 3 parameter SYS_CLK = 50_000_000, //50MHz系统时钟,用于计数器 4 parameter UART_BPS = 115200 //波特率 5 )( 6 input clk, //系统输入时钟 7 input rst_n, //系统复位信号,低电平有效 8 input txd_en, //UART数据发送使能信号 9 input [7:0] txd_data, //UART待发送数据存储器 10 output reg txd //UART数据发送信号 11 ); 12 13 localparam BPS_CNT_MAX = SYS_CLK / UART_BPS; //UART 1bit说需要的时钟周期数 14 15 reg txd_d1; //对UART txd数据信号打第一拍存储器 16 reg txd_d2; //对UART txd数据信号打第二拍存储器 17 reg txd_flag; //UART发送数据采集标志 18 reg [17:0] bps_cnt; //UART 1bit计数器,可支持波特率300bps计数 19 reg [3:0] txd_bit_cnt; //UART数据发送位数标志计数器,即标志现在发送第几位数据 20 reg [7:0] data; //UART数据发送暂存器 21 22 wire txd_en_flag; //数据开始传输的标志 23 24 //边沿检测,上升沿,一帧的开始标志;txd_en一帧发送过程中一直为高,所以这里只有一帧开始时检测为高, 25 //其余时间均为低,所以后面的txd_flag、data均在一检测到上升沿时,进行赋值 26 assign txd_en_flag = (txd_d1 && (~txd_d2)); 27 28 //对UART txd_en数据发送使能信号打第一拍 29 always @(posedge clk or negedge rst_n) 30 if(!rst_n) 31 txd_d1 <= 1'b0; 32 else 33 txd_d1 <= txd_en; 34 35 //对UART txd_en数据发送使能信号打第二拍 36 always @(posedge clk or negedge rst_n) 37 if(!rst_n) 38 txd_d2 <= 1'b0; 39 else 40 txd_d2 <= txd_d1; 41 42 //发送数据采集标志信号处理 43 always @(posedge clk or negedge rst_n) 44 if(!rst_n) 45 txd_flag <= 1'b0; 46 else if(txd_en_flag) 47 txd_flag <= 1'b1; 48 else if((txd_bit_cnt == 4'd9) && (bps_cnt == BPS_CNT_MAX / 2)) 49 txd_flag <= 1'b0; 50 else 51 txd_flag <= txd_flag; 52 53 //UART 1bit计时,在txd_flag有效时,开始计时 54 always @(posedge clk or negedge rst_n) 55 if(!rst_n) 56 bps_cnt <= 18'b0; 57 else if(txd_flag) 58 begin 59 if(bps_cnt == BPS_CNT_MAX - 1'b1) 60 bps_cnt <= 18'b0; 61 else 62 bps_cnt <= bps_cnt + 1'b1; 63 end 64 else 65 bps_cnt <= 18'b0; 66 67 //发送完成信号标志 68 //其实可以一发送完数据就可以将rxd_done置1,即去掉bps_cnt == BPS_CNT_MAX / 2 69 always @(posedge clk or negedge rst_n) 70 if(!rst_n) 71 txd_bit_cnt <= 4'b0; 72 else if(txd_flag) 73 begin 74 if(bps_cnt == BPS_CNT_MAX - 1'b1) 75 txd_bit_cnt <= txd_bit_cnt + 1'b1; 76 else 77 txd_bit_cnt <= txd_bit_cnt; 78 end 79 else 80 txd_bit_cnt <= 4'b0; 81 82 //一检测到发送指令,就将待发送数据暂存到data存储器中;即一检测到上升沿信号,就将数据存入暂存器 83 always @(posedge clk or negedge rst_n) 84 if(!rst_n) 85 data <= 8'b0; 86 else if(txd_en_flag) 87 data <= txd_data; 88 else if((txd_bit_cnt == 4'd9) && (bps_cnt == BPS_CNT_MAX / 2)) 89 data <= 8'b0; 90 else 91 data <= data; //不能data <= txd_data; 92 93 //发送数据,数据从低位开始发送 94 always @(posedge clk or negedge rst_n) 95 if(!rst_n) 96 txd <= 1'b1; 97 else if(txd_flag) 98 case(txd_bit_cnt) 99 4'd0: txd <= 1'b0; //一帧数据的开始信号 100 4'd1: txd <= data[0]; 101 4'd2: txd <= data[1]; 102 4'd3: txd <= data[2]; 103 4'd4: txd <= data[3]; 104 4'd5: txd <= data[4]; 105 4'd6: txd <= data[5]; 106 4'd7: txd <= data[6]; 107 4'd8: txd <= data[7]; 108 4'd9: txd <= 1'b1; //一帧数据的结束信号 109 default: 110 txd <= 1'b1; 111 endcase 112 else 113 txd <= 1'b1; 114 115 endmodule
3. UART顶层模块,回环测试
1 //UART顶层模块,回环测试 2 module uart #( 3 parameter SYS_CLK = 50_000_000, //50MHz系统时钟,用于计数器 4 parameter UART_BPS = 115200 //波特率,可支持波特率低至300bps的传输速率 5 )( 6 input clk, //系统输入时钟 7 input rst_n, //系统复位信号,低电平有效 8 input rxd, //UART数据接收信号 9 output txd //UART数据发送信号 10 ); 11 12 wire txd_rxd_flag; //用于连接数据接收模块的一帧数据接收完成信号rxd_done,与发送模块的发送使能信号txd_en 13 wire [7:0]data; //用于连接数接收模块、发送模块的数据 14 15 //UART接收模块例化 16 uart_rx #( 17 .SYS_CLK(SYS_CLK), 18 .UART_BPS(UART_BPS) 19 ) 20 rx1( 21 .clk(clk), 22 .rst_n(rst_n), 23 .rxd(rxd), 24 .rxd_data(data), 25 .rxd_done(txd_rxd_flag) 26 ); 27 28 //UART发送模块例化 29 uart_tx #( 30 .SYS_CLK(SYS_CLK), 31 .UART_BPS(UART_BPS) 32 ) 33 tx1( 34 .clk(clk), 35 .rst_n(rst_n), 36 .txd_en(txd_rxd_flag), 37 .txd_data(data), 38 .txd(txd) 39 ); 40 41 endmodule
总结:
1. 注意一帧开始的标志,对于接收模块,使用的是边沿检测中的下降沿;发送模块使用边沿检测的上升沿
2. 接收模块中的数据接收过程标志,是基于下降沿的,后面的具体工作就基于这个过程标志rxd_flag;这么做的原因是因为避开数据传输过程中出现的下降沿,避免误判一帧开始
3. 发送模块中的数据暂存寄存器data、发送过程标志txd_flag,要在一开始检测到一帧开始时,就要给其赋值,否则后面一帧开始传输使能信号txd_en会一直为高,则边沿检测的上升沿随后将一直为低,后面的数据将无法基于此操作
4. UART传输协议,数据从低位到高位开始传输
5. UART空闲状态,处于高电平,所以接收模块,开始标志使用下降沿检测,即发送端发送一个低电平表示一个开始位