aboutsummaryrefslogblamecommitdiff
path: root/src/rtl/uart_core.v
blob: 14a7ef74f072e383064ca04073cd7511e7037563 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

















                                                                        
                            

































                                                                        
                                               




















                                                                    








































































                                                                    
                              








                                                                    

                                                  

















































                                                                    
                                         








                                           
                                 



                          
                                           













                                                         
                                         


























































































































































                                                                    
                                                         












                                                                 
                                               







                                        
                                                     










                                            
                                                            


















































































                                                                    
                                                













                                               
                                               






                                        
                                                 


















                                                                    
                                                            


















                                                                        
//======================================================================
//
// uart_core.v
// -----------
// A simple universal asynchronous receiver/transmitter (UART)
// interface. The interface contains 16 byte wide transmit and 
// receivea buffers and can handle start and stop bits. But in 
// general is rather simple. The primary purpose is as host 
// interface for the coretest design. The core also has a
// loopback mode to allow testing of a serial link.
//
// Note that the UART has a separate API interface to allow
// a control core to change settings such as speed. But the core
// has default values to allow it to start operating directly
// after reset. No config should be needed.
//
//
// Author: Joachim Strombergson
// Copyright (c) 2014, SUNET
// 
// Redistribution and use in source and binary forms, with or 
// without modification, are permitted provided that the following 
// conditions are met: 
// 
// 1. Redistributions of source code must retain the above copyright 
//    notice, this list of conditions and the following disclaimer. 
// 
// 2. Redistributions in binary form must reproduce the above copyright 
//    notice, this list of conditions and the following disclaimer in 
//    the documentation and/or other materials provided with the 
//    distribution. 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//======================================================================

module uart_core(
                 input wire          clk,
                 input wire          reset_n,

                 // Configuration parameters
                 input wire [15 : 0] bit_rate,
                 input wire [3 : 0]  data_bits,
                 input wire [1 : 0]  stop_bits,
                 
                 // External data interface
                 input wire          rxd,
                 output wire         txd,

                 // Internal receive interface.
                 output wire         rxd_syn,
                 output [7 : 0]      rxd_data,
                 input wire          rxd_ack,
                 
                 // Internal transmit interface.
                 input wire          txd_syn,
                 input wire [7 : 0]  txd_data,
                 output wire         txd_ack
                );

  
  //----------------------------------------------------------------
  // Internal constant and parameter definitions.
  //----------------------------------------------------------------
  parameter ERX_IDLE  = 0; 
  parameter ERX_START = 1;
  parameter ERX_BITS  = 2;
  parameter ERX_STOP  = 3;
  parameter ERX_SYN   = 4;

  parameter ETX_IDLE  = 0; 
  parameter ETX_ACK   = 1;
  parameter ETX_START = 2;
  parameter ETX_BITS  = 3;
  parameter ETX_STOP  = 4;
 
  
  //----------------------------------------------------------------
  // Registers including update variables and write enable.
  //----------------------------------------------------------------
  reg          rxd_reg;

  reg [7 : 0]  rxd_byte_reg;
  reg          rxd_byte_we;

  reg [4 : 0]  rxd_bit_ctr_reg;
  reg [4 : 0]  rxd_bit_ctr_new;
  reg          rxd_bit_ctr_we;
  reg          rxd_bit_ctr_rst;
  reg          rxd_bit_ctr_inc;

  reg [15 : 0] rxd_bitrate_ctr_reg;
  reg [15 : 0] rxd_bitrate_ctr_new;
  reg          rxd_bitrate_ctr_we;
  reg          rxd_bitrate_ctr_rst;
  reg          rxd_bitrate_ctr_inc;

  reg          rxd_syn_reg;
  reg          rxd_syn_new;
  reg          rxd_syn_we;
  
  reg [2 : 0]  erx_ctrl_reg;
  reg [2 : 0]  erx_ctrl_new;
  reg          erx_ctrl_we;
  
  reg          txd_reg;
  reg          txd_new;
  reg          txd_we;
  
  reg [7 : 0]  txd_byte_reg;
  reg [7 : 0]  txd_byte_new;
  reg          txd_byte_we;

  reg [4 : 0]  txd_bit_ctr_reg;
  reg [4 : 0]  txd_bit_ctr_new;
  reg          txd_bit_ctr_we;
  reg          txd_bit_ctr_rst;
  reg          txd_bit_ctr_inc;

  reg [15 : 0] txd_bitrate_ctr_reg;
  reg [15 : 0] txd_bitrate_ctr_new;
  reg          txd_bitrate_ctr_we;
  reg          txd_bitrate_ctr_rst;
  reg          txd_bitrate_ctr_inc;

  reg          txd_ack_reg;
  reg          txd_ack_new;
  reg          txd_ack_we;
  
  reg [2 : 0]  etx_ctrl_reg;
  reg [2 : 0]  etx_ctrl_new;
  reg          etx_ctrl_we;

  
  //----------------------------------------------------------------
  // Wires.
  //----------------------------------------------------------------
  wire [15 : 0] half_bit_rate;
  
  
  //----------------------------------------------------------------
  // Concurrent connectivity for ports etc.
  //----------------------------------------------------------------
  assign txd      = txd_reg;
  assign rxd_syn  = rxd_syn_reg;
  assign rxd_data = rxd_byte_reg;
  assign txd_ack  = txd_ack_reg;

  assign half_bit_rate = {1'b0, bit_rate[15 : 1]};
  
  
  //----------------------------------------------------------------
  // reg_update
  //
  // Update functionality for all registers in the core.
  // All registers are positive edge triggered with synchronous
  // active low reset. All registers have write enable.
  //----------------------------------------------------------------
  always @ (posedge clk)
    begin: reg_update
      if (!reset_n)
        begin
          rxd_reg             <= 0;
          rxd_byte_reg        <= 8'h00;
          rxd_bit_ctr_reg     <= 4'h0;
          rxd_bitrate_ctr_reg <= 16'h0000;
          rxd_syn_reg         <= 0;
          erx_ctrl_reg        <= ERX_IDLE;
          
          txd_reg             <= 1;
          txd_byte_reg        <= 8'h00;
          txd_bit_ctr_reg     <= 4'h0;
          txd_bitrate_ctr_reg <= 16'h0000;
          txd_ack_reg         <= 0;
          etx_ctrl_reg        <= ETX_IDLE;
        end
      else
        begin
          // We sample the rx input port every cycle.
          rxd_reg <= rxd;

          // We shift the rxd bit into msb.
          if (rxd_byte_we)
            begin
              rxd_byte_reg <= {rxd_reg, rxd_byte_reg[7 : 1]};
            end

          if (rxd_bit_ctr_we)
            begin
              rxd_bit_ctr_reg <= rxd_bit_ctr_new;
            end

          if (rxd_bitrate_ctr_we)
            begin
              rxd_bitrate_ctr_reg <= rxd_bitrate_ctr_new;
            end
          
          if (rxd_syn_we)
            begin
              rxd_syn_reg <= rxd_syn_new;
            end
          
          if (erx_ctrl_we)
            begin
              erx_ctrl_reg <= erx_ctrl_new;
            end
          
          if (txd_we)
            begin
              txd_reg <= txd_new;
            end
          
          if (txd_byte_we)
            begin
              txd_byte_reg <= txd_byte_new;
            end

          if (txd_bit_ctr_we)
            begin
              txd_bit_ctr_reg <= txd_bit_ctr_new;
            end

          if (txd_bitrate_ctr_we)
            begin
              txd_bitrate_ctr_reg <= txd_bitrate_ctr_new;
            end
          
          if (txd_ack_we)
            begin
              txd_ack_reg <= txd_ack_new;
            end
          
          if (etx_ctrl_we)
            begin
              etx_ctrl_reg <= etx_ctrl_new;
            end
        end
    end // reg_update


  //----------------------------------------------------------------
  // rxd_bit_ctr
  //
  // Bit counter for receiving data on the external 
  // serial interface.
  //----------------------------------------------------------------
  always @*
    begin: rxd_bit_ctr
      rxd_bit_ctr_new = 4'h0;
      rxd_bit_ctr_we  = 0;

      if (rxd_bit_ctr_rst)
        begin
          rxd_bit_ctr_new = 4'h0;
          rxd_bit_ctr_we  = 1;
        end

      else if (rxd_bit_ctr_inc)
        begin
          rxd_bit_ctr_new = rxd_bit_ctr_reg + 4'b0001;
          rxd_bit_ctr_we  = 1;
        end
    end // rxd_bit_ctr


  //----------------------------------------------------------------
  // rxd_bitrate_ctr
  //
  // Bitrate counter for receiving data on the external 
  // serial interface.
  //----------------------------------------------------------------
  always @*
    begin: rxd_bitrate_ctr
      rxd_bitrate_ctr_new = 16'h0000;
      rxd_bitrate_ctr_we  = 0;

      if (rxd_bitrate_ctr_rst)
        begin
          rxd_bitrate_ctr_new = 16'h0000;
          rxd_bitrate_ctr_we  = 1;
        end

      else if (rxd_bitrate_ctr_inc)
        begin
          rxd_bitrate_ctr_new = rxd_bitrate_ctr_reg + 16'h0001;
          rxd_bitrate_ctr_we  = 1;
        end
    end // rxd_bitrate_ctr



  //----------------------------------------------------------------
  // txd_bit_ctr
  //
  // Bit counter for transmitting data on the external 
  // serial interface.
  //----------------------------------------------------------------
  always @*
    begin: txd_bit_ctr
      txd_bit_ctr_new = 4'h0;
      txd_bit_ctr_we  = 0;

      if (txd_bit_ctr_rst)
        begin
          txd_bit_ctr_new = 4'h0;
          txd_bit_ctr_we  = 1;
        end

      else if (txd_bit_ctr_inc)
        begin
          txd_bit_ctr_new = txd_bit_ctr_reg + 4'b0001;
          txd_bit_ctr_we  = 1;
        end
    end // txd_bit_ctr


  //----------------------------------------------------------------
  // txd_bitrate_ctr
  //
  // Bitrate counter for transmitting data on the external 
  // serial interface.
  //----------------------------------------------------------------
  always @*
    begin: txd_bitrate_ctr
      txd_bitrate_ctr_new = 16'h0000;
      txd_bitrate_ctr_we  = 0;

      if (txd_bitrate_ctr_rst)
        begin
          txd_bitrate_ctr_new = 16'h0000;
          txd_bitrate_ctr_we  = 1;
        end

      else if (txd_bitrate_ctr_inc)
        begin
          txd_bitrate_ctr_new = txd_bitrate_ctr_reg + 16'h0001;
          txd_bitrate_ctr_we  = 1;
        end
    end // txd_bitrate_ctr
  

  //----------------------------------------------------------------
  // external_rx_engine
  //
  // Logic that implements the receive engine towards 
  // the external interface. Detects incoming data, collects it, 
  // if required checks parity and store correct data into 
  // the rx buffer.
  //----------------------------------------------------------------
  always @*
    begin: external_rx_engine
      rxd_bit_ctr_rst     = 0;
      rxd_bit_ctr_inc     = 0;
      rxd_bitrate_ctr_rst = 0;
      rxd_bitrate_ctr_inc = 0;
      rxd_byte_we         = 0;
      rxd_syn_new         = 0;
      rxd_syn_we          = 0;
      erx_ctrl_new        = ERX_IDLE;
      erx_ctrl_we         = 0;
      
      case (erx_ctrl_reg)
        ERX_IDLE:
          begin
            if (!rxd_reg)
              begin
                // Possible start bit detected.
                rxd_bitrate_ctr_rst = 1;
                erx_ctrl_new        = ERX_START;
                erx_ctrl_we         = 1;
              end
          end


        ERX_START:
          begin
            rxd_bitrate_ctr_inc = 1;
            if (rxd_reg)
              begin
                // Just a glitch.
                erx_ctrl_new = ERX_IDLE;
                erx_ctrl_we  = 1;
              end
            else
              begin
                if (rxd_bitrate_ctr_reg == half_bit_rate)
                  begin
                    // start bit assumed. We start sampling data.
                    rxd_bit_ctr_rst     = 1;
                    rxd_bitrate_ctr_rst = 1;
                    erx_ctrl_new        = ERX_BITS;
                    erx_ctrl_we         = 1;
                  end
              end
          end

        
        ERX_BITS:
          begin
            if (rxd_bitrate_ctr_reg < bit_rate)
              begin
                rxd_bitrate_ctr_inc = 1;
              end
            else
              begin
                rxd_byte_we         = 1;
                rxd_bit_ctr_inc     = 1;
                rxd_bitrate_ctr_rst = 1;
                if (rxd_bit_ctr_reg == data_bits - 1)
                  begin
                    erx_ctrl_new = ERX_STOP;
                    erx_ctrl_we  = 1;
                  end
              end
          end

        
        ERX_STOP:
          begin
            rxd_bitrate_ctr_inc = 1;
            if (rxd_bitrate_ctr_reg == bit_rate * stop_bits)
              begin
                rxd_syn_new  = 1;
                rxd_syn_we   = 1;
                erx_ctrl_new = ERX_SYN;
                erx_ctrl_we  = 1;
              end
          end

        
        ERX_SYN:
          begin
            if (rxd_ack)
              begin
                rxd_syn_new  = 0;
                rxd_syn_we   = 1;
                erx_ctrl_new = ERX_IDLE;
                erx_ctrl_we  = 1;
              end
          end

        
        default:
          begin

          end
      endcase // case (erx_ctrl_reg)
    end // external_rx_engine


  //----------------------------------------------------------------
  // external_tx_engine
  //
  // Logic that implements the transmit engine towards 
  // the external interface.
  //----------------------------------------------------------------
  always @*
    begin: external_tx_engine
      txd_new             = 0;
      txd_we              = 0;
      txd_byte_new        = 0;
      txd_byte_we         = 0;
      txd_bit_ctr_rst     = 0;
      txd_bit_ctr_inc     = 0;
      txd_bitrate_ctr_rst = 0;
      txd_bitrate_ctr_inc = 0;
      txd_ack_new         = 0;
      txd_ack_we          = 0;
      etx_ctrl_new        = ETX_IDLE;
      etx_ctrl_we         = 0;

      case (etx_ctrl_reg)
        ETX_IDLE:
          begin
            txd_new = 1;
            txd_we  = 1;
            if (txd_syn)
              begin
                txd_byte_new        = txd_data;
                txd_byte_we         = 1;
                txd_ack_new         = 1;
                txd_ack_we          = 1;
                txd_bitrate_ctr_rst = 1;
                etx_ctrl_new        = ETX_ACK;
                etx_ctrl_we         = 1;
              end
          end

        
        ETX_ACK:
          begin
            if (!txd_syn)
              begin
                txd_new      = 0;
                txd_we       = 1;
                txd_ack_new  = 0;
                txd_ack_we   = 1;
                etx_ctrl_new = ETX_START;
                etx_ctrl_we  = 1;
              end
          end
        
        ETX_START:
          begin
            if (txd_bitrate_ctr_reg == bit_rate)
              begin
                txd_bit_ctr_rst     = 1;
                etx_ctrl_new        = ETX_BITS;
                etx_ctrl_we         = 1;
              end
            else
              begin
                txd_bitrate_ctr_inc = 1;
              end
          end

        
        ETX_BITS:
          begin
            if (txd_bitrate_ctr_reg < bit_rate)
              begin
                txd_bitrate_ctr_inc = 1;
              end
            else
              begin
                txd_bitrate_ctr_rst = 1;
                
                if (txd_bit_ctr_reg == data_bits)
                  begin
                    txd_new      = 1;
                    txd_we       = 1;
                    etx_ctrl_new = ETX_STOP;
                    etx_ctrl_we  = 1;
                  end
                else
                  begin
                    txd_new         = txd_byte_reg[txd_bit_ctr_reg];
                    txd_we          = 1;
                    txd_bit_ctr_inc = 1;
                  end
              end
          end
        
            
        ETX_STOP:
          begin
            txd_bitrate_ctr_inc = 1;
            if (txd_bitrate_ctr_reg == bit_rate * stop_bits)
              begin
                etx_ctrl_new = ETX_IDLE;
                etx_ctrl_we  = 1;
              end
          end

        
        default:
          begin

          end
      endcase // case (etx_ctrl_reg)
    end // external_tx_engine
  
endmodule // uart

//======================================================================
// EOF uart.v
//======================================================================