uart协议--Verilog及仿真

1、协议原理:

UART(universal asynchronous receiver-transmitter)通用异步收发传输器。

uart串口通信需要两根信号线来实现,一根用于串口发送,一根用于串口接收。一开始高电平,然后拉低表示开始位,接着8个数据位,最后拉高表示停止位,并且进入空闲状态,等待下一次的数据传输。

 因为uart通信没有时钟,因此只能规定多少时间发送一个二进制位来保证数据收发不会出错,这就是波特率。这里时钟频率50MHz,波特率9600bps,因此发送一个二进制位需要(50_000_000/9600=5208)个时钟,换言之,计数到5208就发送一位二进制。


2、协议代码:

代码由顶层模块、接收模块和发送模块构成。其中仿真的代码是仿真通过接收模块接收数据,再发送出来。同样串口调试助手也是通过接收模块接收数据,然后再发出来。

顶层模块:

 1 module uart_top(
 2 input sys_clk,
 3 input sys_rst_n,
 4 input uart_recv,
 5 output uart_send
 6 );
 7     
 8 wire [7:0]data;
 9 wire data_en;
10 
11 uart_recv  
12 #(
13 .BPS_CNT  (52)           //仿真用的,串口调试不用直接删掉!
14 )
15 u_uart_recv
16 (
17     .sys_clk          (sys_clk),
18     .sys_rst_n        (sys_rst_n),
19     .data_in_recv     (uart_recv),
20     .data_out_recv    (data),
21     .data_en          (data_en)
22 );
23 
24 uart_send 
25 #(
26 .BPS_CNT  (52)          //仿真用的,串口调试不用直接删掉!
27 )
28 u_uart_send
29 (
30     .sys_clk          (sys_clk),
31     .sys_rst_n        (sys_rst_n),
32     .data_en          (data_en),
33     .data_in_send     (data),
34     .data_out_send    (uart_send)
35 );
36 endmodule 

接收模块:

  1 module uart_recv
  2 (
  3 input sys_clk,
  4 input sys_rst_n,
  5 input data_in_recv,
  6 output reg[7:0]data_out_recv,
  7 output reg data_en
  8 );
  9 parameter CLK=50_000_000;
 10 parameter UART_BPS=9600;
 11 parameter BPS_CNT=CLK/UART_BPS;
 12 reg data_in_recv_d0;
 13 reg data_in_recv_d1;
 14 reg [15:0]bps_cnt;
 15 reg [3:0]bit_cnt;
 16 reg [7:0]data;
 17 reg flag;
 18 wire start_flag;
 19 
 20 always @(posedge sys_clk or negedge sys_rst_n)begin
 21 if(!sys_rst_n)begin
 22 data_in_recv_d0<=1'b0;
 23 data_in_recv_d1<=1'b0;
 24 end
 25 else begin
 26 data_in_recv_d0<=data_in_recv;
 27 data_in_recv_d1<=data_in_recv_d0;
 28 end
 29 end
 30 assign start_flag=data_in_recv_d1 & (~data_in_recv_d0);
 31 
 32 
 33 always @(posedge sys_clk or negedge sys_rst_n)begin
 34 if(!sys_rst_n)
 35 flag<=1'b0;
 36 else if(start_flag)
 37 flag<=1'b1;
 38 else if((bit_cnt==9)&&(bps_cnt==BPS_CNT/2))
 39 flag<=1'b0;
 40 else
 41 flag<=flag;
 42 end
 43 
 44 always @(posedge sys_clk or negedge sys_rst_n)begin
 45 if(!sys_rst_n)begin
 46 bps_cnt<=16'd0;
 47 bit_cnt<=4'd0;
 48 end
 49 else if(flag)begin
 50 if(bps_cnt<BPS_CNT-1)begin
 51 bps_cnt<=bps_cnt+1'b1;
 52 bit_cnt<=bit_cnt;
 53 end
 54 else begin
 55 bps_cnt<=16'd0;
 56 bit_cnt<=bit_cnt+1;
 57 end
 58 end
 59 else begin
 60 bps_cnt<=16'd0;
 61 bit_cnt<=4'd0;
 62 end
 63 end
 64 
 65 always @(posedge sys_clk or negedge sys_rst_n) begin 
 66     if ( !sys_rst_n)  
 67         data <= 8'd0;                                     
 68     else if(flag)                            
 69         if (bps_cnt == BPS_CNT/2) begin         
 70             case ( bit_cnt )
 71              4'd1 : data[0] <= data_in_recv_d1;   
 72              4'd2 : data[1] <= data_in_recv_d1;
 73              4'd3 : data[2] <= data_in_recv_d1;
 74              4'd4 : data[3] <= data_in_recv_d1;
 75              4'd5 : data[4] <= data_in_recv_d1;
 76              4'd6 : data[5] <= data_in_recv_d1;
 77              4'd7 : data[6] <= data_in_recv_d1;
 78              4'd8 : data[7] <= data_in_recv_d1;   
 79              default:;                                    
 80             endcase
 81         end
 82         else 
 83             data <= data;
 84     else
 85         data <= 8'd0;
 86 end
 87 
 88 always @(posedge sys_clk or negedge sys_rst_n)begin
 89 if(!sys_rst_n)begin
 90 data_out_recv<=8'd0;
 91 data_en<=1'b0;
 92 end
 93 else if(bit_cnt==9)begin
 94 data_out_recv<=data;
 95 data_en<=1'b1;
 96 end
 97 else begin
 98 data_en<=1'b0;
 99 end
100 end
101 endmodule 

发送模块:

 1 module uart_send
 2 (
 3 input sys_clk,
 4 input sys_rst_n,
 5 input data_en,
 6 input [7:0]data_in_send,
 7 output reg data_out_send
 8 );
 9 parameter CLK=50_000_000;
10 parameter UART_BPS=9600;
11 parameter BPS_CNT=CLK/UART_BPS;
12 reg [7:0]tem_data;
13 reg [15:0]bps_cnt;
14 reg [3:0]bit_cnt;
15 reg send_flag;
16 
17 always @(posedge sys_clk or negedge sys_rst_n)begin
18 if(!sys_rst_n)
19 send_flag<=1'b0;
20 else if(data_en)begin
21 tem_data<=data_in_send;
22 send_flag<=1'b1;
23 end
24 else if((bit_cnt==4'd9)&&(bps_cnt==BPS_CNT/2))begin
25 tem_data<=8'd0;
26 send_flag<=1'b0;
27 end
28 else begin
29 tem_data<=tem_data;
30 send_flag<=send_flag;
31 end
32 end
33 
34 always @(posedge sys_clk or negedge sys_rst_n)begin
35 if(!sys_rst_n)begin
36 bps_cnt<=16'd0;
37 bit_cnt<=4'd0;
38 end
39 else if(send_flag)begin
40 if(bps_cnt<BPS_CNT-1)begin
41 bps_cnt<=bps_cnt+1'b1;
42 bit_cnt<=bit_cnt;
43 end
44 else begin
45 bps_cnt<=16'd0;
46 bit_cnt<=bit_cnt+1'b1;
47 end
48 end
49 else begin
50 bps_cnt<=16'd0;
51 bit_cnt<=4'd0;
52 end
53 end
54 
55 always @(posedge sys_clk or negedge sys_rst_n) begin        
56     if (!sys_rst_n)  
57         data_out_send <= 1'b1;        
58     else if (send_flag)
59         case(bit_cnt)
60             4'd0: data_out_send <= 1'b0;         
61             4'd1: data_out_send <= tem_data[0];  
62             4'd2: data_out_send <= tem_data[1];
63             4'd3: data_out_send <= tem_data[2];
64             4'd4: data_out_send <= tem_data[3];
65             4'd5: data_out_send <= tem_data[4];
66             4'd6: data_out_send <= tem_data[5];
67             4'd7: data_out_send <= tem_data[6];
68             4'd8: data_out_send <= tem_data[7];   
69             4'd9: data_out_send <= 1'b1;       
70             default: ;
71         endcase
72     else 
73         data_out_send <= 1'b1;                  
74 end
75 endmodule 

仿真:

 1 `timescale 1ns/1ns 
 2 module uart_top_tb;
 3 reg                         sys_clk                 ; 
 4 reg                         sys_rst_n              ; 
 5 reg                         uart_recv             ;
 6 wire                        uart_send           ;
 7 
 8 uart_top u_uart_top
 9 (
10     .sys_clk                    (sys_clk                ),
11     .sys_rst_n                  (sys_rst_n              ),
12     .uart_recv                (uart_recv            ),
13     .uart_send                (uart_send            )
14 );
15 
16 initial begin
17     sys_clk = 1;
18 end
19 always #10 sys_clk = ~sys_clk;
20 
21 initial begin
22     sys_rst_n = 0; 
23    #30 sys_rst_n = 1;
24 end
25 
26 reg  [7:0]              mem[15:0]           ; 
27 integer                 i                         ;
28 integer                 j                         ;
29 
30 
31 initial $readmemh("./data.txt",mem);
32 
33 task bit_cnt
34 (
35     input [7:0]         data
36 );
37     begin
38         for(i=0;i<=9;i=i+1) begin   //10?bit?
39             case(i)
40                  0: uart_recv = 1'b0;
41                  1: uart_recv = data[i-1];
42                  2: uart_recv = data[i-1];
43                  3: uart_recv = data[i-1];
44                  4: uart_recv = data[i-1];
45                  5: uart_recv = data[i-1];
46                  6: uart_recv = data[i-1];
47                  7: uart_recv = data[i-1];
48                  8: uart_recv = data[i-1];
49                  9: uart_recv = 1'b1;
50             endcase
51             #1040; 
52         end        
53     end
54 endtask
55 
56 task rx_byte;
57     begin
58         for(j=0;j<=15;j=j+1) 
59             bit_cnt(mem[j]);
60     end
61 endtask
62 
63 initial begin
64     # 401 rx_byte();
65 end
66 
67 initial begin
68     #180000;
69     $stop;
70 end
71 endmodule

 仿真的时候,在sim目录下建立一个data.txt存放宽度8位,深度16位的文件,然后仿真接收这个文件,然后发出这个文件。

这里的数字是十六进制,比如8'h00  8'h01...

仿真结果以及串口调试结果:

暂时还没有解惑的地方:

①不明白作者为什么在仿真的时候,顶层模块这里需要有一个52?

#(
.BPS_CNT (52) //仿真用的,串口调试不用直接删掉!
)

②根据接收模块最后的代码可以知道,当接收成功每一个字(8'h00  8'h01...)的所有二进制位,data_en就会被拉高,前面的8'h00  8'h01...到8'h0e,一直都是接收完之后就拉高data_en,然而为什么到了最后一个字8'h0f就没有出现了?

参考文章:https://www.cnblogs.com/xianyufpga/p/11086676.html

原文地址:https://www.cnblogs.com/FPGAer/p/13788424.html