FPGA——异步串行通信(UART)发送
一、设计思路
两个计数器,一个波特率计数器,一个数据发送计数器,数据一共十位依次为:起始位(1'b0),数据位[8bit],终值位(1'b1)
数据是从低位开始发送
二、串口发送代码
1 module my_uart_tx( 2 clk , //时钟 3 rst_n , //复位 4 data , //发送数据 5 send_en , //发送使能 6 baud_set , //波特率设置 7 uart_tx , //串口发送 8 tx_done //发送完成标志位 9 ); 10 parameter DATA_W = 8; 11 parameter SET_W = 3; 12 parameter BYTE_D = 10; 13 parameter BAUT_W = 17; 14 parameter BYTE_W = 4; 15 16 17 input clk; 18 input rst_n; 19 input [DATA_W-1:0] data; 20 input send_en; 21 input [SET_W-1:0] baud_set; 22 output uart_tx; 23 output tx_done; 24 25 reg uart_tx; 26 reg tx_done; 27 28 reg [BAUT_W-1:0] cnt_baud; 29 reg [BAUT_W-1:0] baud; 30 wire add_cnt_baud; 31 wire end_cnt_baud; 32 33 reg [BYTE_W-1:0] cnt_byte; 34 wire add_cnt_byte; 35 wire end_cnt_byte; 36 37 reg [BYTE_D-1:0] uart_data; 38 reg cnt_baud_flag; 39 40 41 always @(posedge clk or negedge rst_n)begin 42 if(!rst_n) 43 cnt_baud <= 0; 44 else if(add_cnt_baud)begin 45 if(end_cnt_baud) 46 cnt_baud <= 0; 47 else 48 cnt_baud <= cnt_baud + 1'b1; 49 end 50 end 51 assign add_cnt_baud = cnt_baud_flag; 52 assign end_cnt_baud = add_cnt_baud && cnt_baud == baud - 1; 53 54 always @(posedge clk or negedge rst_n)begin 55 if(!rst_n) 56 cnt_byte <= 0; 57 else if(add_cnt_byte)begin 58 if(end_cnt_byte) 59 cnt_byte <= 0; 60 else 61 cnt_byte <= cnt_byte + 1'b1; 62 end 63 end 64 assign add_cnt_byte = end_cnt_baud; 65 assign end_cnt_byte = add_cnt_byte && cnt_byte == BYTE_D - 1; 66 67 always @(posedge clk or negedge rst_n)begin 68 if(!rst_n) 69 uart_tx <= 1; 70 else if(cnt_baud == 0 && add_cnt_baud) 71 uart_tx <= uart_data[cnt_byte]; 72 end 73 74 always @(posedge clk or negedge rst_n)begin 75 if(!rst_n) 76 uart_data <= 10'b0; 77 else if(!cnt_baud_flag && send_en) 78 uart_data <= {1'b1,data,1'b0}; 79 end 80 81 always @(posedge clk or negedge rst_n)begin 82 if(!rst_n) 83 cnt_baud_flag <= 0; 84 else if(send_en) 85 cnt_baud_flag <= 1; 86 else if(end_cnt_byte) 87 cnt_baud_flag <= 0; 88 end 89 90 always @(*)begin 91 case(baud_set) 92 3'd0:baud = 17'd83333;//600bps 93 3'd1:baud = 17'd41666;//1200bps 94 3'd2:baud = 17'd20833;//2400bps 95 3'd3:baud = 17'd10416;//4800bps 96 3'd4:baud = 17'd5208 ;//9600bps 97 3'd5:baud = 17'd2604 ;//19200bps 98 3'd6:baud = 17'd1302 ;//38400bps 99 3'd7:baud = 17'd868 ;//57600bps 100 default: 101 baud = 0; 102 endcase 103 end 104 105 always @(posedge clk or negedge rst_n)begin 106 if(!rst_n) 107 tx_done <= 0; 108 else if(end_cnt_byte) 109 tx_done <= 1; 110 else 111 tx_done <= 0; 112 end 113 114 endmodule
三、仿真文件
1 `timescale 1ns/1ns 2 module my_uart_tx_tb(); 3 4 parameter DATA_W = 8; 5 parameter SET_W = 3; 6 parameter CYCLE = 20; 7 parameter RST_TIME = 3; 8 9 reg clk; 10 reg rst_n; 11 reg [DATA_W-1:0] data; 12 reg send_en; 13 reg [SET_W-1:0] baud_set; 14 wire uart_tx; 15 wire tx_done; 16 17 my_uart_tx my_uart_tx( 18 clk , //时钟 19 rst_n , //复位 20 data , //发送数据 21 send_en , //发送使能 22 baud_set , //波特率设置 23 uart_tx , //串口发送 24 tx_done //发送完成标志位 25 ); 26 27 28 initial begin 29 clk = 1; 30 forever begin 31 #(CYCLE/2) 32 clk = ~clk; 33 end 34 end 35 36 initial begin 37 rst_n = 1; 38 #3; 39 rst_n = 0; 40 #(RST_TIME*CYCLE); 41 rst_n = 1; 42 end 43 44 initial begin 45 #3; 46 data = 0; 47 baud_set = 0; 48 #(10*CYCLE); 49 data = 8'b01011010; 50 baud_set = 3'd5; 51 end 52 53 initial begin 54 #3; 55 send_en = 0; 56 #(10*CYCLE); 57 send_en = 1; 58 #(CYCLE); 59 send_en = 0; 60 #600000; 61 send_en = 1; 62 #(CYCLE) 63 send_en = 0; 64 end 65 66 endmodule
四、板级调试代码
1 `timescale 1ns/1ns 2 module uart_tx_test( 3 clk , 4 rst_n , 5 uart_tx 6 ); 7 parameter CNT_W = 19; 8 parameter CNT_D = 500_000; 9 parameter DATA_W = 8; 10 parameter SET_W = 3; 11 12 input clk; 13 input rst_n; 14 output uart_tx; 15 16 wire uart_tx; 17 reg [CNT_W-1:0] cnt; 18 19 wire end_cnt; 20 21 reg send_en; 22 reg [DATA_W-1:0] data; 23 // reg [SET_W-1:0] baud_set; 24 25 26 27 my_uart_tx my_uart_tx( 28 .clk (clk), //时钟 29 .rst_n (rst_n), //复位 30 .data (data), //发送数据 31 .send_en (send_en), //发送使能 32 .baud_set (3'd4), //波特率设置 33 .uart_tx (uart_tx), //串口发送 34 .tx_done () //发送完成标志位 35 ); 36 //设置波特率 37 // always @(posedge clk or negedge rst_n)begin 38 // if(!rst_n) 39 // baud_set <= 0; 40 // else 41 // baud_set <= 4'd4; 42 // end 43 44 //10ms计数器 45 always @(posedge clk or negedge rst_n)begin 46 if(!rst_n) 47 cnt <= 0; 48 else if(end_cnt) 49 cnt <= 0; 50 else 51 cnt <= cnt + 1; 52 end 53 assign end_cnt = cnt == CNT_D - 1; 54 55 always @(posedge clk or negedge rst_n)begin 56 if(!rst_n) 57 data <= 0; 58 else if(add_cnt_data)begin 59 if(end_cnt_data) 60 data <= 0; 61 else 62 data <= data +1'b1; 63 end 64 end 65 assign add_cnt_data = end_cnt; 66 assign end_cnt_data = add_cnt_data && data == 256 - 1; 67 68 always @(posedge clk or negedge rst_n)begin 69 if(!rst_n) 70 send_en <= 0; 71 else if(end_cnt) 72 send_en <= 1; 73 else 74 send_en <= 0; 75 end 76 77 78 endmodule
五、板级调试仿真文件
`timescale 1ns / 1ns module uart_tx_test_tb(); reg clk; reg rst_n; wire uart_tx; uart_tx_test uart_tx_test( clk , rst_n , uart_tx ); parameter CYCLE = 20; parameter RST_TIME = 3; initial begin clk = 1; forever begin #(CYCLE/2) clk = ~clk; end end initial begin rst_n = 1; #3; rst_n = 0; #(RST_TIME * CYCLE); rst_n = 1; end initial begin #40000000 $stop; end endmodule
六、出现的问题
在板级调试代码仿真过程中,使用modelsim+vivado进行“run behavioral simulation”,“run simulation”会一直转,仿真不出来(如下图)
但是使用vivado自带的仿真工具是可以仿真出来的
综合后,使用“run post-synthesis functional simulation”,modelsim是可以出结果的
原因:modelsim对verilog语法要求较高,在vivado上没有报错的,可能会在modelsim上报错,例如某一个wire型的信号,没有定义,在vivado上是不报错的,而modelsim上是会报错的
解决方法是:打开modelsim原程序,将源文件加入modelsim编译(compile),找到错误更正就行了