简单UART的verilog实现
下面摘录我写的简单的UART代码,对于灵活性和健壮性做了如下设计:
1、系统时钟及串口波特率以参数形式输入,例化时可以灵活设置
2、接受模块在起始位会检测中点电平是否仍然为低,否则判定为抖动
接收机代码
1 `timescale 1ns/1ps 2 3 // 系统时钟200MHz,波特率115200 4 module uart_rx #( 5 parameter BAUDRATE = 115200, 6 parameter FREQ = 200_000_000)( 7 input clk, nrst, 8 input rx, 9 output reg [7:0] rdata, 10 output reg vld 11 ); 12 13 localparam T = FREQ / BAUDRATE; 14 15 // flag接受处理标志位,为1表明当前处于接受状态 16 reg flag; 17 always @(posedge clk or negedge nrst) begin 18 if(nrst == 0) 19 flag <= 0; 20 else if(flag == 0 && rx == 0) 21 flag <= 1; 22 else if(cnt_bit == 1 - 1 && cnt_clk == T / 2 - 1 && rx == 1) 23 flag <= 0; 24 else if(end_cnt_bit) 25 flag <= 0; 26 end 27 28 // 两层计数结构,cnt_clk计数每一位所占的时钟数,cnt_bit计数1个开始位,8个数据位,一个停止位,共10位 29 reg [3:0] cnt_bit; 30 reg [31:0] cnt_clk; 31 assign end_cnt_clk = cnt_clk == T - 1; 32 assign end_cnt_bit = end_cnt_clk && cnt_bit == 10 - 1; 33 34 always @(posedge clk or negedge nrst) begin 35 if(nrst == 0) 36 cnt_clk <= 0; 37 else if(flag) begin 38 if(end_cnt_clk) 39 cnt_clk <= 0; 40 else 41 cnt_clk <= cnt_clk + 1'b1; 42 end 43 else 44 cnt_clk <= 0; 45 end 46 47 always @(posedge clk or negedge nrst) begin 48 if(nrst == 0) 49 cnt_bit <= 0; 50 else if(end_cnt_clk) begin 51 if(end_cnt_bit) 52 cnt_bit <= 0; 53 else 54 cnt_bit <= cnt_bit + 1'b1; 55 end 56 end 57 58 // 读数据及数据有效指示信号 59 always @(posedge clk or negedge nrst) begin 60 if(nrst == 0) 61 rdata <= 0; 62 else if(cnt_clk == T / 2 - 1 && cnt_bit != 1 - 1 && cnt_bit != 10 - 1) 63 rdata[cnt_bit - 1] <= rx; 64 end 65 66 always @(posedge clk or negedge nrst) begin 67 if(nrst == 0) 68 vld <= 0; 69 else if(end_cnt_bit) 70 vld <= 1; 71 else 72 vld <= 0; 73 end 74 75 endmodule
发送机代码
1 `timescale 1ns/1ps 2 3 // 系统时钟200MHz,波特率115200,带忙闲指示信号rdy 4 module uart_tx #( 5 parameter BAUDRATE = 115200, 6 parameter FREQ = 200_000_000)( 7 input clk, nrst, 8 input wrreq, 9 input [7:0] wdata, 10 output reg tx, 11 output reg rdy 12 ); 13 14 reg [3:0] cnt_bit; 15 reg [31:0] cnt_clk; 16 17 localparam T = FREQ / BAUDRATE; 18 19 // 有写请求时将rdy信号拉底,待到数据发送完毕再将信号拉 20 always @(posedge clk or negedge nrst) begin 21 if(nrst == 0) 22 rdy <= 1; 23 else if(wrreq) 24 rdy <= 0; 25 else if(end_cnt_bit) 26 rdy <= 1; 27 end 28 29 // 两层计数结构,cnt_clk计数每一位所占的时钟数,cnt_bit计数1个开始位,8个数据位,一个停止位,共10位 30 wire end_cnt_clk; 31 wire end_cnt_bit; 32 assign end_cnt_clk = cnt_clk == T - 1; 33 assign end_cnt_bit = end_cnt_clk && cnt_bit == 10 - 1; 34 35 always @(posedge clk or negedge nrst) begin 36 if(nrst == 0) 37 cnt_clk <= 0; 38 else if(rdy == 0) begin 39 if(end_cnt_clk) 40 cnt_clk <= 0; 41 else 42 cnt_clk <= cnt_clk + 1'b1; 43 end 44 end 45 46 always @(posedge clk or negedge nrst) begin 47 if(nrst == 0) 48 cnt_bit <= 0; 49 else if(end_cnt_clk) begin 50 if(end_cnt_bit) 51 cnt_bit <= 0; 52 else 53 cnt_bit <= cnt_bit + 1'b1; 54 end 55 end 56 57 // 先发送一个起始位0,然后8位数据位,最后是停止位1 58 always @(posedge clk or negedge nrst) begin 59 if(nrst == 0) 60 tx <= 1; 61 else if(rdy == 0 && cnt_clk == 0) begin 62 if(cnt_bit == 1 - 1) 63 tx <= 0; 64 else if(cnt_bit == 10 - 1) 65 tx <= 1; 66 else 67 tx <= wdata[cnt_bit - 1]; 68 end 69 end 70 71 endmodule
在Xilinx Artix-7平台上验证的顶层代码
1 `timescale 1ns / 1ps 2 3 module uart_top( 4 input clk_p, clk_n, nrst, 5 input rx, 6 output tx 7 ); 8 9 localparam BAUDRATE = 115200; 10 localparam FREQ = 200_000_000; 11 12 // 差分时钟信号转为单端信号 13 IBUFGDS #( 14 .DIFF_TERM("FALSE"), 15 .IBUF_LOW_PWR("TRUE"), 16 .IOSTANDARD("DEFAULT") 17 ) IBUFGDS_inst( 18 .O(clk), 19 .I(clk_p), 20 .IB(clk_n) 21 ); 22 23 wire [7:0] data; 24 wire vld; 25 26 uart_rx #(BAUDRATE, FREQ) uart_rx_u( 27 .clk (clk ), 28 .nrst (nrst ), 29 .rx (rx ), 30 .rdata (data ), 31 .vld (vld ) 32 ); 33 34 uart_tx #(BAUDRATE, FREQ) uart_tx_u( 35 .clk (clk ), 36 .nrst (nrst ), 37 .wrreq (vld ), 38 .wdata (data ), 39 .tx (tx ), 40 .rdy ( ) 41 ); 42 43 ila_0 ila_0_u( 44 .clk (clk ), 45 .probe0 (nrst ), 46 .probe1 ({tx,rx}), 47 .probe2 (data ), 48 .probe3 (vld ) 49 ); 50 51 endmodule