下面摘錄我寫的簡單的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