Do you know how a UART works? If not, first brush up on the basics of UARTs before continuing on. Have you considered how you might sample data with an FPGA? Think about data coming into your FPGA. Data can arrive by itself or it can arrive with a clock. When it arrives with a clock, it is call synchronous. When it arrives without a clock, it is called asynchronous. A UART is an asynchronous interface.

In any asynchronous interface, the first thing you need to know is when in time you should sample (look at) the data. If you do not sample the data at the right time, you might see the wrong data. In order to receive your data correctly, the transmitter and receiver must agree on the baud rate. The baud rate is the rate at which the data is transmitted. For example, 9600 baud means 9600 bits per second. The code below uses a generic in VHDL or a parameter in Verilog to determine how many clock cycles there are in each bit. This is how the baud rate gets determined.

The FPGA is continuously sampling the line. Once it sees the line transition from high to low, it knows that a UART data word is coming. This first transition indicates the start bit. Once the beginning of the start bit is found, the FPGA waits for one half of a bit period. This ensures that the middle of the data bit gets sampled. From then on, the FPGA just needs to wait one bit period (as specified by the baud rate) and sample the rest of the data. The figure below shows how the UART receiver works inside of the FPGA. First a falling edge is detected on the serial data line. This represents the start bit. The FPGA then waits until the middle of the first data bit and samples the data. It does this for all eight data bits. UART: 8 Data Bits, 1 Start Bit, 1 Stop Bit, No Parity Bit

UART Serial Data Stream The above data stream shows how the code below is structured. The code below uses one Start Bit, one Stop Bit, eight Data Bits, and no parity. Note that the transmitter modules below both have a signal otxactive. This is used to infer a tri-state buffer for half-duplex communication. It is up your specific project requirements if you want to create a half-duplex UART or a full-duplex UART. The code below will work for both!

Verilog Receiver (uartrx.v): <code verilog> File Downloaded from http://www.nandland.com This file contains the UART Receiver. This receiver is able to receive 8 bits of serial data, one start bit, one stop bit, and no parity bit. When receive is complete orxdv will be driven high for one clock cycle. Set Parameter CLKSPERBIT as follows: CLKSPERBIT = (Frequency of iClock)/(Frequency of UART) Example: 10 MHz Clock, 115200 baud UART (10000000)/(115200) = 87

module uartrx #(parameter CLKSPERBIT) ( input iClock,

 input        i_Rx_Serial,
 output       o_Rx_DV,
 output [7:0] o_Rx_Byte
 );
  
parameter s_IDLE         = 3'b000;
parameter s_RX_START_BIT = 3'b001;
parameter s_RX_DATA_BITS = 3'b010;
parameter s_RX_STOP_BIT  = 3'b011;
parameter s_CLEANUP      = 3'b100;
 
reg           r_Rx_Data_R = 1'b1;
reg           r_Rx_Data   = 1'b1;
 
reg [7:0]     r_Clock_Count = 0;
reg [2:0]     r_Bit_Index   = 0; //8 bits total
reg [7:0]     r_Rx_Byte     = 0;
reg           r_Rx_DV       = 0;
reg [2:0]     r_SM_Main     = 0;
 
// Purpose: Double-register the incoming data.
// This allows it to be used in the UART RX Clock Domain.
// (It removes problems caused by metastability)
always @(posedge i_Clock)
  begin
    r_Rx_Data_R <= i_Rx_Serial;
    r_Rx_Data   <= r_Rx_Data_R;
  end
 
 
// Purpose: Control RX state machine
always @(posedge i_Clock)
  begin
     
    case (r_SM_Main)
      s_IDLE :
        begin
          r_Rx_DV       <= 1'b0;
          r_Clock_Count <= 0;
          r_Bit_Index   <= 0;
           
          if (r_Rx_Data == 1'b0)          // Start bit detected
            r_SM_Main <= s_RX_START_BIT;
          else
            r_SM_Main <= s_IDLE;
        end
       
      // Check middle of start bit to make sure it's still low
      s_RX_START_BIT :
        begin
          if (r_Clock_Count == (CLKS_PER_BIT-1)/2)
            begin
              if (r_Rx_Data == 1'b0)
                begin
                  r_Clock_Count <= 0;  // reset counter, found the middle
                  r_SM_Main     <= s_RX_DATA_BITS;
                end
              else
                r_SM_Main <= s_IDLE;
            end
          else
            begin
              r_Clock_Count <= r_Clock_Count + 1;
              r_SM_Main     <= s_RX_START_BIT;
            end
        end // case: s_RX_START_BIT
       
       
      // Wait CLKS_PER_BIT-1 clock cycles to sample serial data
      s_RX_DATA_BITS :
        begin
          if (r_Clock_Count < CLKS_PER_BIT-1)
            begin
              r_Clock_Count <= r_Clock_Count + 1;
              r_SM_Main     <= s_RX_DATA_BITS;
            end
          else
            begin
              r_Clock_Count          <= 0;
              r_Rx_Byte[r_Bit_Index] <= r_Rx_Data;
               
              // Check if we have received all bits
              if (r_Bit_Index < 7)
                begin
                  r_Bit_Index <= r_Bit_Index + 1;
                  r_SM_Main   <= s_RX_DATA_BITS;
                end
              else
                begin
                  r_Bit_Index <= 0;
                  r_SM_Main   <= s_RX_STOP_BIT;
                end
            end
        end // case: s_RX_DATA_BITS
   
   
      // Receive Stop bit.  Stop bit = 1
      s_RX_STOP_BIT :
        begin
          // Wait CLKS_PER_BIT-1 clock cycles for Stop bit to finish
          if (r_Clock_Count < CLKS_PER_BIT-1)
            begin
              r_Clock_Count <= r_Clock_Count + 1;
              r_SM_Main     <= s_RX_STOP_BIT;
            end
          else
            begin
              r_Rx_DV       <= 1'b1;
              r_Clock_Count <= 0;
              r_SM_Main     <= s_CLEANUP;
            end
        end // case: s_RX_STOP_BIT
   
       
      // Stay here 1 clock
      s_CLEANUP :
        begin
          r_SM_Main <= s_IDLE;
          r_Rx_DV   <= 1'b0;
        end
       
       
      default :
        r_SM_Main <= s_IDLE;
       
    endcase
  end   
 
assign o_Rx_DV   = r_Rx_DV;
assign o_Rx_Byte = r_Rx_Byte;
 

endmodule uart_rx </code> Uart发送模块(uarttx.v): <code verilog> File Downloaded from http://www.nandland.com This file contains the UART Transmitter. This transmitter is able to transmit 8 bits of serial data, one start bit, one stop bit, and no parity bit. When transmit is complete oTxdone will be driven high for one clock cycle. Set Parameter CLKSPERBIT as follows: CLKSPERBIT = (Frequency of iClock)/(Frequency of UART) Example: 10 MHz Clock, 115200 baud UART (10000000)/(115200) = 87
module uarttx #(parameter CLKSPERBIT) ( input iClock, input iTxDV, input [7:0] iTxByte, output oTxActive, output reg oTxSerial, output oTxDone );
parameter sIDLE = 3'b000; parameter sTXSTARTBIT = 3'b001; parameter sTXDATABITS = 3'b010; parameter sTXSTOPBIT = 3'b011; parameter sCLEANUP = 3'b100;
reg [2:0] r
SMMain = 0; reg [7:0] rClockCount = 0; reg [2:0] rBitIndex = 0; reg [7:0] rTxData = 0; reg rTxDone = 0; reg rTxActive = 0;
always @(posedge i
Clock) begin
case (rSMMain) sIDLE : begin oTxSerial ⇐ 1'b1; Drive Line High for Idle rTxDone ⇐ 1'b0; rClockCount ⇐ 0; rBitIndex ⇐ 0;
if (iTxDV == 1'b1) begin rTxActive ⇐ 1'b1; rTxData ⇐ iTxByte; rSMMain ⇐ sTXSTARTBIT; end else rSMMain ⇐ sIDLE; end
case: s
IDLE

Send out Start Bit. Start bit = 0

      s_TX_START_BIT :
        begin
          o_Tx_Serial <= 1'b0;
           
          // Wait CLKS_PER_BIT-1 clock cycles for start bit to finish
          if (r_Clock_Count < CLKS_PER_BIT-1)
            begin
              r_Clock_Count <= r_Clock_Count + 1;
              r_SM_Main     <= s_TX_START_BIT;
            end
          else
            begin
              r_Clock_Count <= 0;
              r_SM_Main     <= s_TX_DATA_BITS;
            end
        end // case: s_TX_START_BIT
       
       
      // Wait CLKS_PER_BIT-1 clock cycles for data bits to finish         
      s_TX_DATA_BITS :
        begin
          o_Tx_Serial <= r_Tx_Data[r_Bit_Index];
           
          if (r_Clock_Count < CLKS_PER_BIT-1)
            begin
              r_Clock_Count <= r_Clock_Count + 1;
              r_SM_Main     <= s_TX_DATA_BITS;
            end
          else
            begin
              r_Clock_Count <= 0;
               
              // Check if we have sent out all bits
              if (r_Bit_Index < 7)
                begin
                  r_Bit_Index <= r_Bit_Index + 1;
                  r_SM_Main   <= s_TX_DATA_BITS;
                end
              else
                begin
                  r_Bit_Index <= 0;
                  r_SM_Main   <= s_TX_STOP_BIT;
                end
            end
        end // case: s_TX_DATA_BITS
       
       
      // Send out Stop bit.  Stop bit = 1
      s_TX_STOP_BIT :
        begin
          o_Tx_Serial <= 1'b1;
           
          // Wait CLKS_PER_BIT-1 clock cycles for Stop bit to finish
          if (r_Clock_Count < CLKS_PER_BIT-1)
            begin
              r_Clock_Count <= r_Clock_Count + 1;
              r_SM_Main     <= s_TX_STOP_BIT;
            end
          else
            begin
              r_Tx_Done     <= 1'b1;
              r_Clock_Count <= 0;
              r_SM_Main     <= s_CLEANUP;
              r_Tx_Active   <= 1'b0;
            end
        end // case: s_Tx_STOP_BIT
       
       
      // Stay here 1 clock
      s_CLEANUP :
        begin
          r_Tx_Done <= 1'b1;
          r_SM_Main <= s_IDLE;
        end
       
       
      default :
        r_SM_Main <= s_IDLE;
       
    endcase
  end

assign oTxActive = rTxActive;

assign o_Tx_Done   = r_Tx_Done;
 

endmodule </code>

测试平台:

//////////////////////////////////////////////////////////////////////
// File Downloaded from http://www.nandland.com
//////////////////////////////////////////////////////////////////////
 
// This testbench will exercise both the UART Tx and Rx.
// It sends out byte 0xAB over the transmitter
// It then exercises the receive by receiving byte 0x3F
`timescale 1ns/10ps
 
`include "uart_tx.v"
`include "uart_rx.v"
 
module uart_tb ();
 
  // Testbench uses a 10 MHz clock
  // Want to interface to 115200 baud UART
  // 10000000 / 115200 = 87 Clocks Per Bit.
  parameter c_CLOCK_PERIOD_NS = 100;
  parameter c_CLKS_PER_BIT    = 87;
  parameter c_BIT_PERIOD      = 8600;
 
  reg r_Clock = 0;
  reg r_Tx_DV = 0;
  wire w_Tx_Done;
  reg [7:0] r_Tx_Byte = 0;
  reg r_Rx_Serial = 1;
  wire [7:0] w_Rx_Byte;
 
 
  // Takes in input byte and serializes it 
  task UART_WRITE_BYTE;
    input [7:0] i_Data;
    integer     ii;
    begin
 
      // Send Start Bit
      r_Rx_Serial <= 1'b0;
      #(c_BIT_PERIOD);
      #1000;
 
 
      // Send Data Byte
      for (ii=0; ii<8; ii=ii+1)
        begin
          r_Rx_Serial <= i_Data[ii];
          #(c_BIT_PERIOD);
        end
 
      // Send Stop Bit
      r_Rx_Serial <= 1'b1;
      #(c_BIT_PERIOD);
     end
  endtask // UART_WRITE_BYTE
 
 
  uart_rx #(.CLKS_PER_BIT(c_CLKS_PER_BIT)) UART_RX_INST
    (.i_Clock(r_Clock),
     .i_Rx_Serial(r_Rx_Serial),
     .o_Rx_DV(),
     .o_Rx_Byte(w_Rx_Byte)
     );
 
  uart_tx #(.CLKS_PER_BIT(c_CLKS_PER_BIT)) UART_TX_INST
    (.i_Clock(r_Clock),
     .i_Tx_DV(r_Tx_DV),
     .i_Tx_Byte(r_Tx_Byte),
     .o_Tx_Active(),
     .o_Tx_Serial(),
     .o_Tx_Done(w_Tx_Done)
     );
 
 
  always
    #(c_CLOCK_PERIOD_NS/2) r_Clock <= !r_Clock;
 
 
  // Main Testing:
  initial
    begin
 
      // Tell UART to send a command (exercise Tx)
      @(posedge r_Clock);
      @(posedge r_Clock);
      r_Tx_DV <= 1'b1;
      r_Tx_Byte <= 8'hAB;
      @(posedge r_Clock);
      r_Tx_DV <= 1'b0;
      @(posedge w_Tx_Done);
 
      // Send a command to the UART (exercise Rx)
      @(posedge r_Clock);
      UART_WRITE_BYTE(8'h3F);
      @(posedge r_Clock);
 
      // Check that the correct command was received
      if (w_Rx_Byte == 8'h3F)
        $display("Test Passed - Correct Byte Received");
      else
        $display("Test Failed - Incorrect Byte Received");
 
    end
 
endmodule