OpenCores
URL https://opencores.org/ocsvn/uart_fifo_cpu_if_sv_testbench/uart_fifo_cpu_if_sv_testbench/trunk

Subversion Repositories uart_fifo_cpu_if_sv_testbench

[/] [uart_fifo_cpu_if_sv_testbench/] [trunk/] [rtl/] [uart.vhd] - Rev 4

Compare with Previous | Blame | View Log

----------------------------------------------------------------------
----                                                              ----
----  UART and FIFO cpu interface                                 ----
----                                                              ----
----  This file is part of the xxx project                        ----
----  http://www.opencores.org/cores/xxx/                         ----
----                                                              ----
----  Description                                                 ----
--             Serial UART with byte wide register interface for control/status, data, and baud rate.
--             Transmit(Tx) and Receive(Rx) data is FIFO buffered. Tx and Rx FIFO size configurable independently.
--             Currently only supports no parity, 8 data bits, 1 stop bit (N81).
--             Data is sent least significant bit first.
--             Baud rate divisor set via 16 bit register, allowing a wide range of baud rates and system clocks.
--
--             Future:
--              - insertion and checking of parity bit
--              - data bit order configurable
--              - number of data bits configurable
--              - cts/rts
--              - interrupt on rx data ready and/or tx fifo empty
--
--Registers: 0x00: Data:
--                    Write this register to push data into the transmit FIFO. The UART
--                    empties the FIFO and transmits data at the specified baud rate.
--                    If the FIFO is full, writes are ignored.
--                    Read this register to pull data from the receive FIFO. If the FIFO
--                    is empty, reading returns the previously read value.
--           0x01: Control/Status:
--                    bit0: Rx FIFO data ready:     High when data waiting in the rx FIFO.
--                    bit1: Rx FIFO overflow flag:  High if overflow occurs. Write 0 to clear.
--                    bit2: Rx stop bit error flag: High if invalid stop bit. Write 0 to clear.
--                    bit3: Tx FIFO full:           High when the tx FIFO is full.        
--                    bit4: Tx FIFO overflow flag:  High if overflow occurs. Write 0 to clear.
--                    bit5: unused
--                    bit6: Cpu interface facing loopback enable: loopback is at serial txd/rxd pins
--                    bit7: Txd/rxd pin facing loopback enable: loopback is at txd/rxd pins
--                          (both loopbacks can be enabled at the same time)
--
--           0x02: Baud Rate Divisor LSB: baud rate = System clock / (baud rate divisor+1)
--           0x03: Baud Rate Divisor MSB: e.g. set to 433 to get 115200 with a 50MHz
--                                        system clock. Error is < 0.01%. 
--
-- Fmax and resource use data:
--
----  To Do:                                                      ----
----   -                                                          ----
----                                                              ----
----  Author(s):                                                  ----
----      - Andrew Bridger, andrew.bridger@gmail.org              ----
----                                                              ----
----------------------------------------------------------------------
----                                                              ----
---- Copyright (C) 2001 Authors and OPENCORES.ORG                 ----
----                                                              ----
---- This source file may be used and distributed without         ----
---- restriction provided that this copyright statement is not    ----
---- removed from the file and that any derivative work contains  ----
---- the original copyright notice and the associated disclaimer. ----
----                                                              ----
---- This source file is free software; you can redistribute it   ----
---- and/or modify it under the terms of the GNU Lesser General   ----
---- Public License as published by the Free Software Foundation; ----
---- either version 2.1 of the License, or (at your option) any   ----
---- later version.                                               ----
----                                                              ----
---- This source is distributed in the hope that it will be       ----
---- useful, but WITHOUT ANY WARRANTY; without even the implied   ----
---- warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      ----
---- PURPOSE.  See the GNU Lesser General Public License for more ----
---- details.                                                     ----
----                                                              ----
---- You should have received a copy of the GNU Lesser General    ----
---- Public License along with this source; if not, download it   ----
---- from http://www.opencores.org/lgpl.shtml                     ----
----                                                              ----
----------------------------------------------------------------------
--
-- CVS Revision History
--
-- $Log$
--
 
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
 
entity uart is
  generic(
    BASE_ADDR           : natural := 0;                --Uart registers are offset from
                                                       -- this base address.
    TX_FIFO_ADDR_LENGTH : natural := 5;                --5 length addr => 32 byte deep FIFO
    RX_FIFO_ADDR_LENGTH : natural := 5
    );
  port(
    clk            : in  std_logic;                    --all inputs(except rxd) MUST be synchronous to clk.
    reset          : in  std_logic;                    --synchronous reset
    --Serial UART
    i_rxd          : in  std_logic;                    --receive serial data (asynchronous)
    o_txd          : out std_logic;                    --transmit serial data
    --Cpu register interface
    i_addr         : in  std_logic_vector;             --highest index is msb of address.
    i_write_enable : in  std_logic;                    --high for 1 clk period for a write
    i_read_enable  : in  std_logic;                    --high for 1 clk period for a read
    i_data         : in  std_logic_vector(7 downto 0);
    o_data         : out std_logic_vector(7 downto 0)  --data returned up to 2 clock cycles after read_enable
    );
end uart;
 
architecture rtl of uart is
 
  signal rxd_d1, rxd_clean : std_logic;
 
  --FIFO from/to main process communication.
  signal tx_fifo_write_data, tx_fifo_read_data       : std_logic_vector(7 downto 0);
  signal tx_fifo_write_request, tx_fifo_read_request : std_logic;
  signal rx_fifo_write_data, rx_fifo_read_data       : std_logic_vector(7 downto 0);
  signal rx_fifo_write_request, rx_fifo_read_request : std_logic;
  signal rx_fifo_read_request_d1                     : std_logic;
  signal tx_fifo_full_flag                           : std_logic;
  signal tx_fifo_overflow                            : std_logic;
  signal tx_fifo_empty, tx_fifo_data_waiting         : std_logic;
  signal rx_fifo_overflow                            : std_logic;
  signal rx_fifo_empty                               : std_logic;
  signal rx_fifo_data_ready_flag                     : std_logic;
 
  --Cpu regs/bits
  signal rx_fifo_overflow_flag    : std_logic;
  signal tx_fifo_overflow_flag    : std_logic;
  signal rx_stop_bit_invalid_flag : std_logic;
 
  signal cpu_facing_loopback_enable     : std_logic;
  signal serial_facing_loopback_enable  : std_logic;
  --Baud rate divisor for 50MHz system clock and 115200 baud rate.
  constant BAUD_RATE_DIVISOR_50M_115200 : std_logic_vector(15 downto 0) := std_logic_vector(to_unsigned(433, 16));
  signal baud_rate_divisor_slv          : std_logic_vector(15 downto 0);
  --cpu register addresses and bit indexes
  constant DATA_REG                     : std_logic_vector(1 downto 0)  := "00";
  constant CONTROL_STATUS_REG           : std_logic_vector(1 downto 0)  := "01";
  constant BAUD_RATE_DIVISOR_LSB_REG    : std_logic_vector(1 downto 0)  := "10";
  constant BAUD_RATE_DIVISOR_MSB_REG    : std_logic_vector(1 downto 0)  := "11";
  constant BASE_ADDR_SLV                : std_logic_vector(i_addr'length-1 downto 0)
    := std_logic_vector(to_unsigned(BASE_ADDR, i_addr'length));
 
begin
 
  main : process(clk)
    type uart_state_t is (IDLE, START, DATA, STOP);
    variable tx_state, rx_state : uart_state_t;
    subtype baud_rate_t is natural range 0 to (2**16)-1;
    variable baud_rate_divisor  : baud_rate_t;
    variable tx_baud_rate_count : baud_rate_t;
    variable rx_baud_rate_count : baud_rate_t;
    variable tx_data_count      : natural range 0 to 7;
    variable rx_data_count      : natural range 0 to 7;
    variable rx_bit_enable      : boolean;
    variable tx_bit_enable      : boolean;
    variable chip_select        : boolean;
    variable rxd, txd           : std_logic;
    variable addr               : std_logic_vector(i_addr'length-1 downto 0);
  begin
    if rising_edge(clk) then
      if reset = '1' then
        --Keep uart serial lines high during reset. Don't want any glitches at startup.
        rxd_d1                        <= '1';
        rxd_clean                     <= '1';
        rxd                           := '1';
        txd                           := '1';
        o_txd                         <= '1';
        --Put UART rx/tx FSMs and counters into a known state at reset.
        tx_state                      := IDLE;
        rx_state                      := IDLE;
        tx_baud_rate_count            := 0;
        rx_baud_rate_count            := 0;
        tx_fifo_write_request         <= '0';
        tx_fifo_read_request          <= '0';
        rx_fifo_write_request         <= '0';
        rx_fifo_read_request          <= '0';
        --Power up state for registers
        baud_rate_divisor_slv         <= BAUD_RATE_DIVISOR_50M_115200;  --set default for 115200
        rx_fifo_overflow_flag         <= '0';
        rx_stop_bit_invalid_flag      <= '0';
        tx_fifo_overflow_flag         <= '0';
        cpu_facing_loopback_enable    <= '0';
        serial_facing_loopback_enable <= '0';
        --Check base addr on x4 byte boundary.
        --Only check during reset so no burden on sim speed. (Synplicity doesn't honour this assert unfortunately.)
        assert BASE_ADDR_SLV(1 downto 0) = "00" report "UART Base address must be 32 bit aligned. I.e. 2 LSBs must be 00"
          severity failure;
      else
        ------[CPU Interface]------
        --CPU interface side of rx and tx fifos. Including the registers directly within this module means
        --this is a self-contained module.
        addr        := i_addr;                                          --ensure addr is in "downto" form.
        chip_select := (addr(addr'high downto 2) = BASE_ADDR_SLV(BASE_ADDR_SLV'high downto 2));
        if chip_select then
          --Cpu register write.
          if i_write_enable = '1' then
            case i_addr(1 downto 0) is
              when DATA_REG =>
                tx_fifo_write_data    <= i_data;
                tx_fifo_write_request <= '1';
              when CONTROL_STATUS_REG =>
                --Some bits are only write zero to clear.
                if i_data(1) = '0' then
                  rx_fifo_overflow_flag <= '0';
                end if;
                if i_data(2) = '0' then
                  rx_stop_bit_invalid_flag <= '0';
                end if;
                if i_data(4) = '0' then
                  tx_fifo_overflow_flag <= '0';
                end if;
                --Standard read/write control bits
                cpu_facing_loopback_enable    <= i_data(6);
                serial_facing_loopback_enable <= i_data(7);
 
              when BAUD_RATE_DIVISOR_LSB_REG => baud_rate_divisor_slv(7 downto 0)  <= i_data;
              when BAUD_RATE_DIVISOR_MSB_REG => baud_rate_divisor_slv(15 downto 8) <= i_data;
              when others                    => null;
            end case;
          end if;
          --Cpu register read.
          if i_read_enable = '1' then
            case i_addr(1 downto 0) is
              when DATA_REG           => rx_fifo_read_request <= '1';
              when CONTROL_STATUS_REG => o_data               <= serial_facing_loopback_enable &
                                                                 cpu_facing_loopback_enable &
                                                                 '0' &  --unused
                                                                 tx_fifo_overflow_flag &
                                                                 tx_fifo_full_flag &
                                                                 rx_stop_bit_invalid_flag &
                                                                 rx_fifo_overflow_flag &
                                                                 rx_fifo_data_ready_flag;
              when BAUD_RATE_DIVISOR_LSB_REG => o_data <= baud_rate_divisor_slv(7 downto 0);
              when BAUD_RATE_DIVISOR_MSB_REG => o_data <= baud_rate_divisor_slv(15 downto 8);
              when others                    => null;
            end case;
          end if;
        end if;
        --Takes 1 clock to read data out of rx fifo.
        rx_fifo_read_request_d1 <= rx_fifo_read_request;
        if rx_fifo_read_request_d1 = '1' then
          o_data <= rx_fifo_read_data;
        end if;
        --type conversion for baud rate divisor.
        baud_rate_divisor := to_integer( unsigned( baud_rate_divisor_slv ));
 
        ------[UART Transmit (tx) FSM]------
        tx_fifo_read_request <= '0';    --default
        case tx_state is
          when IDLE =>
            txd := '1';
            if tx_fifo_data_waiting = '1' then
              tx_state             := START;
              tx_fifo_read_request <= '1';
            end if;
          --output 1 start bit
          when START =>
            if tx_bit_enable then
              txd           := '0';
              tx_state      := DATA;
              tx_data_count := 0;
            end if;
          --output 8 data bits, least significant bit first.
          when DATA =>
            if tx_bit_enable then
              txd := tx_fifo_read_data(tx_data_count);
              if tx_data_count = 7 then
                tx_state := STOP;
              else
                tx_data_count := tx_data_count + 1;
              end if;
            end if;
          --output 1 stop bit
          when STOP =>
            if tx_bit_enable then
              txd      := '1';
              tx_state := IDLE;
            end if;
        end case;
 
        --transmit baud rate "clk" (enable)
        if tx_baud_rate_count = 0 then
          tx_baud_rate_count := baud_rate_divisor;
          tx_bit_enable      := true;
        else
          tx_baud_rate_count := tx_baud_rate_count - 1;
          tx_bit_enable      := false;
        end if;
 
        ------[UART Receive (rx) FSM]------
        rx_fifo_write_request <= '0';
        case rx_state is
          when IDLE =>
            if rxd = '0' then
              rx_state           := START;
              rx_baud_rate_count := baud_rate_divisor/2;  --setup baud rate counter so we sample
                                                          --at middle of bit period
            end if;
          --look for a start bit that is continuously low for at least 1/2 the nominal bit
          --period. This helps to filter short duration glitches. And gets us sampling
          --rxd at the center of a bit period.
          when START =>
            if rxd /= '0' then
              --start bit has not stayed low for longer than 1/2 a bit period.
              rx_state := IDLE;
            elsif rx_bit_enable then
              rx_state      := DATA;
              rx_data_count := 0;
            end if;
          --read in 8 data bits.
          when DATA =>
            if rx_bit_enable then
              rx_fifo_write_data(rx_data_count) <= rxd;
              if rx_data_count = 7 then
                rx_state := STOP;
              else
                rx_data_count := rx_data_count + 1;
              end if;
            end if;
          --check stop bit is '1'. If not, set the rx error flag.
          when STOP =>
            if rx_bit_enable then
              if rxd = '1' then
                --Valid stop bit, so attempt to write the received byte to the rx fifo.
                rx_fifo_write_request <= '1';
              else
                --Invalid stop bit.
                rx_stop_bit_invalid_flag <= '1';
              end if;
              rx_state := IDLE;
            end if;
        end case;
 
        --receive baud rate "clk" (enable)
        if rx_baud_rate_count = 0 then
          rx_baud_rate_count := baud_rate_divisor;
          rx_bit_enable      := true;
        else
          rx_baud_rate_count := rx_baud_rate_count - 1;
          rx_bit_enable      := false;
        end if;
 
        ------[Latch FIFO overflow bits]------
        if tx_fifo_overflow = '1' then
          tx_fifo_overflow_flag <= '1';
        end if;
        if rx_fifo_overflow = '1' then
          rx_fifo_overflow_flag <= '1';
        end if;
 
        ------[Loopbacks and Rxd Retime]------
        --rxd is an asynchronous input so retime it onto the system clock domain.
        rxd_d1    <= i_rxd;
        rxd_clean <= rxd_d1;
        if cpu_facing_loopback_enable = '0' then
          --normal operation.
          rxd := rxd_clean;
        else
          --loopback enabled.
          rxd := txd;
        end if;
        if serial_facing_loopback_enable = '0' then
          --normal operation.
          o_txd <= txd;
        else
          --loopback enabled.
          o_txd <= rxd_clean;
        end if;
      end if;
    end if;
  end process;
 
  tx_fifo : entity work.synchronous_FIFO(rtl)
    generic map (
      ADDR_LENGTH => TX_FIFO_ADDR_LENGTH,
      DATA_WIDTH  => 8)
    port map (
      clk           => clk,
      reset         => reset,
      write_data    => tx_fifo_write_data,
      write_request => tx_fifo_write_request,
      full          => tx_fifo_full_flag,
      overflow      => tx_fifo_overflow,
      read_data     => tx_fifo_read_data,
      read_request  => tx_fifo_read_request,
      empty         => tx_fifo_empty,
      underflow     => open,
      half_full     => open);
 
  tx_fifo_data_waiting <= not tx_fifo_empty;
 
  rx_fifo : entity work.synchronous_FIFO(rtl)
    generic map (
      ADDR_LENGTH => RX_FIFO_ADDR_LENGTH,
      DATA_WIDTH  => 8)
    port map (
      clk           => clk,
      reset         => reset,
      write_data    => rx_fifo_write_data,
      write_request => rx_fifo_write_request,
      full          => open,
      overflow      => rx_fifo_overflow,
      read_data     => rx_fifo_read_data,
      read_request  => rx_fifo_read_request,
      empty         => rx_fifo_empty,
      underflow     => open,
      half_full     => open);
 
  rx_fifo_data_ready_flag <= not rx_fifo_empty;
end rtl;
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.