首页 > 技术文章 > FPGA——异步串行通信(UART)发送

cnlntr 2020-03-11 20:43 原文

一、设计思路

两个计数器,一个波特率计数器,一个数据发送计数器,数据一共十位依次为:起始位(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),找到错误更正就行了

 

推荐阅读