1. 程式人生 > >UART學習之路(四)VerilogHDL實現的簡單UART,VIVADO下完成模擬

UART學習之路(四)VerilogHDL實現的簡單UART,VIVADO下完成模擬

用VerilogHDL實現UART並完成模擬就算是對UART整個技術有了全面的理解,同時也算是Verilog入門了。整個UART分為3部分完成,傳送模組(Transmitter),接收模組(Receiver)和波特率發生模組(BuadRateGenerator)。傳送模組相比於接收模組要簡單一些,主要功能就是每1/9600s傳送1bit的資料,接收模組就在取樣時鐘下完成資料的取樣,波特率傳送模組就是產生對應的波特率。UART的基本電路模型可以看UART學習之路(二) 基本時序介紹,當中對UART進行了完整的電路建模。

1.傳送模組

模組程式碼:

`timescale 1ns / 1ps
module
UART_TRANSMITTER( input [7:0] DataIn,//並行資料輸入 input baud16x,TxEn,rstn,//baud16x=波特率×16,TxEn是並行資料裝入使能訊號,rstn復位訊號 output reg DataOut,//序列資料輸出 output reg TxBusy// 說明串列埠正忙的訊號,檢測其下降沿就可以判斷是否可以裝入新的資料 ); reg [7:0] DataInReg;//輸入資料暫存器 reg [7:0] cnt;//計數器 reg cnt_start;//計數開始標誌位 reg TransEnIn0;//當前狀態取樣 reg
TransEnIn1;//上一個狀態取樣 wire pos_en; //採TxEn的上升沿 always@(posedge baud16x or negedge rstn) if(!rstn)begin TransEnIn0<=1'b0; TransEnIn1<=1'b0; end else begin TransEnIn0 <= TxEn;//now TransEnIn1 <= TransEnIn0;// delay end // Description // I0 I1
// 0 0 : 1&0=0 // 1 0 : 1&1=1 // 1 1 : 1&0=0 // 0 1 :0&0=0 assign pos_en = TransEnIn0 & !TransEnIn1; //資料裝入 always@(negedge rstn or posedge baud16x) if(!rstn)begin DataInReg <= 8'd0; TxBusy <= 1'b0; end else if(pos_en == 1'b1)begin DataInReg <= DataIn; cnt_start <= 1'b1; TxBusy <= 1'b1; end else if(cnt >= 8'd160) begin cnt_start <= 1'b0; TxBusy <= 1'b0; end //計數 always@(posedge baud16x or negedge rstn) if(!rstn) cnt <= 8'd0; else if(cnt_start == 1'b1) cnt <= cnt + 1'b1; else cnt <= 8'd0; //UART 傳送 always@(posedge baud16x or negedge rstn) if(!rstn) DataOut <=1'b1; else if(cnt_start == 1'b1) case(cnt) 8'd0:DataOut <= 1'b0; 8'd16:DataOut <= DataInReg[0]; 8'd32:DataOut <= DataInReg[1]; 8'd48:DataOut <= DataInReg[2]; 8'd64:DataOut <= DataInReg[3]; 8'd80:DataOut <= DataInReg[4]; 8'd96:DataOut <= DataInReg[5]; 8'd112:DataOut <= DataInReg[6]; 8'd128:DataOut <= DataInReg[7]; 8'd144:DataOut <= 1'b1; endcase else DataOut <= 1'b1; endmodule

TestBench

`timescale 1ns / 1ps
module TESTBENCH_UART_TRANSMITTER(
    );
    parameter CLKPERIOD = 100;
    
    reg [7:0] TempData;
    reg clk,en,nrst;

    wire TxDataOut;
    wire TxOverFlag;
    
    initial begin
     clk = 0;
     en = 0;
     nrst = 0;
     TempData = 8'd0;
    end
    
    //clk generate 
    always #(CLKPERIOD/2) clk = ~clk;
    
    //復位
    initial begin
     #CLKPERIOD nrst = 1;
    end
    
    //資料使能
    initial begin
     #CLKPERIOD en = 1;
     TempData = 8'b1010_0101;
     #CLKPERIOD en = 0;
     
     #(CLKPERIOD*200) en = 1;
     TempData = 8'b0101_1010;
     #CLKPERIOD en = 0;
    end
    //version 1.0 2018-12-4
    //module initial
    UART_TRANSMITTER U1(//input
                        .baud16x(clk),
                        .TxEn(en),
                        .rstn(nrst),
                        .DataIn(TempData),
                        //output
                        .DataOut(TxDataOut),
                        .TxBusy(TxOverFlag));
endmodule

模擬結果

分析:

需要傳送的並行資料儲存在TempData裡面,第一次傳送10100101傳送起始位bit0=0,之後是資料位bit1,bit2,bit3,bit4,bit5,bit6,bit7,bit8,最後是停止位bit9=1。第二次傳送01011010,同第一次傳送。

 

 

 

2.接收模組

模組程式碼

`timescale 1ns / 1ps
/*2018-9-21 verson 1.0 
baud rate = 9600 bit/s
1 bit takes 1/9600s = 104.167 us ~= 104167ns
input frequency of clk is 100Mhz*/

// baud rate       T_bdr       N                          System clock = 50M
// 9600           104167ns    104167/System_clk_priod          5208-1
// 19200          52083ns     52083/System_clk_priod           2604-1
// 38400          26041ns     26041/System_clk_priod           1302-1
// 57600          17361ns     17361/System_clk_priod           868-1
// 115200         8680ns      8680/System_clk_priod            434-1   
//
//Input clock is 16x bound rate, the first sample data is at 24, and the next sample data is at 40
// 24,40,56,72,88,104,120,136,152
module UART_RECEIVE(
input baud16x,rstn,
input recv,
output reg [7:0] rdata,
output reg recv_ready
    );
    reg recvIn0;//下降沿捕捉,當前時刻值
    reg recvIn1;//下降沿捕捉,上一個時刻值
    
    reg[7:0] cnt_bit;
    reg RecvNeFlag;
    //起始位獲取
    always@(posedge baud16x or negedge rstn)
    if(!rstn) begin
    recvIn0 <= 1'b1;
    recvIn1 <= 1'b1;
    end
    else begin
    /*下降沿取樣*/
    recvIn0 <= recv;//當前時刻的recv給recvIn0
    recvIn1 <= recvIn0;//前一個時刻的recv給recvIn1
    end
    
    wire neg_rec;
    assign neg_rec = !recvIn0 && recvIn1;
    /*下降沿判斷,開啟接收功能標誌*/
    always@(negedge rstn or posedge baud16x)
    if(!rstn) begin
     RecvNeFlag <= 1'b0;
     recv_ready <= 1'b1;
    end
    else if(neg_rec == 1'b1)begin
     RecvNeFlag <= 1'b1;
     recv_ready <= 1'b0;
    end
    else if(cnt_bit == 8'd152)begin
     RecvNeFlag <= 1'b0;
     recv_ready <= 1'b1;
    end
    
    
    //bit計數
    always@(posedge baud16x or negedge rstn)
    if(!rstn)
    cnt_bit <= 1'b0;
    else if(RecvNeFlag == 1'b1)
    cnt_bit <= cnt_bit + 1'b1;
    else cnt_bit <= 8'd0;
    
    //取樣接收
    always@(posedge baud16x or negedge rstn)
    if(!rstn)
    rdata <= 8'b0000_0000;
    else case(cnt_bit)
    8'd24:rdata[0]  <= recv;//資料位第1位
    8'd40:rdata[1]  <= recv;
    8'd56:rdata[2]  <= recv;
    8'd72:rdata[3]  <= recv;
    8'd88:rdata[4]  <= recv;
    8'd104:rdata[5] <= recv;
    8'd120:rdata[6] <= recv;
    8'd136:rdata[7] <= recv;//資料位第8位
    endcase
endmodule

2.TestBench

`timescale 1ns / 1ps
module TESTBENCH_UART_RECEIVE(
    );
    parameter CLKPERIOD=100;
    
    reg GCLK;
    reg nrst;
    reg DataIn;
    wire [7:0] recv_data;
    wire RecvDoneFlag;
    //初始化
    initial begin
     nrst = 0;
     GCLK = 0;
     DataIn = 1;
    end
    //生成時鐘激勵
    always #(CLKPERIOD/2) GCLK = ~GCLK;
    
    //復位使能
    initial #(CLKPERIOD*10) nrst = 1;
    
    //模擬傳送資料
    initial begin
      #(CLKPERIOD*49) DataIn = 0;
      #(CLKPERIOD*16) DataIn = 1;
      #(CLKPERIOD*16) DataIn = 0;
      #(CLKPERIOD*16) DataIn = 1;
      #(CLKPERIOD*16) DataIn = 0;
      #(CLKPERIOD*16) DataIn = 1;
      #(CLKPERIOD*16) DataIn = 0;
      #(CLKPERIOD*16) DataIn = 0;
      #(CLKPERIOD*16) DataIn = 1;
      #(CLKPERIOD*16) DataIn = 1;
    end
    
    
    UART_RECEIVE U1(//input
                     .baud16x(GCLK),
                     .rstn(nrst),
                     .recv(DataIn),
                    //output
                     .rdata(recv_data),
                     .recv_ready(RecvDoneFlag));
endmodule

3.模擬結果

分析:

模組復位輸出8'b0000_0000,接收取樣在輸入的中點處。序列輸入資料,轉換成並行的資料。

3.波特率發生模組實際上就是對100Mhz的始終進行分頻,分成BaudRate*16的時鐘提供給傳送和接收模組。

4.參考程式碼:https://github.com/jamieiles/uart ,GitHub上別人的另外一種實現方式。