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就沒有出現了?