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

Subversion Repositories System09

[/] [System09/] [trunk/] [rtl/] [VHDL/] [acia6850.vhd] - Rev 209

Go to most recent revision | Compare with Previous | Blame | View Log

--===========================================================================--
--                                                                           --
--                  Synthesizable 6850 compatible ACIA                       --
--                                                                           --
--===========================================================================--
--
--  File name      : acia6850.vhd
--
--  Entity name    : acia6850
--
--  Purpose        : Implements a RS232 6850 compatible 
--                   Asynchronous Communications Interface Adapter (ACIA)
--                  
--  Dependencies   : ieee.std_logic_1164
--                   ieee.numeric_std
--                   ieee.std_logic_unsigned
--
--  Author         : John E. Kent
--
--  Email          : dilbert57@opencores.org      
--
--  Web            : http://opencores.org/project,system09
--
--  Origins        : miniUART written by Ovidiu Lupas olupas@opencores.org
--
--  Registers      :
--
--  IO address + 0 Read - Status Register
--
--     Bit[7] - Interrupt Request Flag
--     Bit[6] - Receive Parity Error (parity bit does not match)
--     Bit[5] - Receive Overrun Error (new character received before last read)
--     Bit[4] - Receive Framing Error (bad stop bit)
--     Bit[3] - Clear To Send level
--     Bit[2] - Data Carrier Detect (lost modem carrier)
--     Bit[1] - Transmit Buffer Empty (ready to accept next transmit character)
--     Bit[0] - Receive Data Ready (character received)
-- 
--  IO address + 0 Write - Control Register
--
--     Bit[7]     - Rx Interupt Enable
--          0     - disabled
--          1     - enabled
--     Bits[6..5] - Transmit Control
--        0 0     - TX interrupt disabled, RTS asserted
--        0 1     - TX interrupt enabled,  RTS asserted
--        1 0     - TX interrupt disabled, RTS cleared
--        1 1     - TX interrupt disabled, RTS asserted, Send Break
--     Bits[4..2] - Word Control
--      0 0 0     - 7 data, 2 stop, even parity 
--      0 0 1     - 7 data, 2 stop, odd  parity
--      0 1 0     - 7 data, 1 stop, even parity
--      0 1 1     - 7 data, 1 stop, odd  parity
--      1 0 0     - 8 data, 2 stop, no   parity
--      1 0 1     - 8 data, 1 stop, no   parity
--      1 1 0     - 8 data, 1 stop, even parity
--      1 1 1     - 8 data, 1 stop, odd  parity
--     Bits[1..0] - Baud Control
--        0 0     - Baud Clk divide by 1
--        0 1     - Baud Clk divide by 16
--        1 0     - Baud Clk divide by 64
--        1 1     - Reset
--
--  IO address + 1 Read - Receive Data Register
--
--     Read when Receive Data Ready bit set
--     Read resets Receive Data Ready bit 
--
--  IO address + 1 Write - Transmit Data Register
--
--     Write when Transmit Buffer Empty bit set
--     Write resets Transmit Buffer Empty Bit
--
--
--  Copyright (C) 2002 - 2012 John Kent
--
--  This program is free software: you can redistribute it and/or modify
--  it under the terms of the GNU General Public License as published by
--  the Free Software Foundation, either version 3 of the License, or
--  (at your option) any later version.
--
--  This program 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 General Public License for more details.
--
--  You should have received a copy of the GNU General Public License
--  along with this program.  If not, see <http://www.gnu.org/licenses/>.
--
--===========================================================================--
--                                                                           --
--                              Revision  History                            --
--                                                                           --
--===========================================================================--
--
-- Version Author        Date         Changes
--
-- 0.1     Ovidiu Lupas  2000-01-15   New model
-- 1.0     Ovidiu Lupas  2000-01      Synthesis optimizations
-- 2.0     Ovidiu Lupas  2000-04      Bugs removed - the RSBusCtrl did not
--                                    process all possible situations
--
-- 3.0     John Kent     2002-10      Changed Status bits to match MC6805
--                                    Added CTS, RTS, Baud rate control & Software Reset
-- 3.1     John Kent     2003-01-05   Added Word Format control a'la mc6850
-- 3.2     John Kent     2003-07-19   Latched Data input to UART
-- 3.3     John Kent     2004-01-16   Integrated clkunit in rxunit & txunit
--                                    TX / RX Baud Clock now external
--                                    also supports x1 clock and DCD. 
-- 3.4     John Kent     2005-09-13   Removed LoadCS signal. 
--                                    Fixed ReadCS and Read 
--                                    in miniuart_DCD_Init process
-- 3.5     John Kent     2006-11-28   Cleaned up code.
--
-- 4.0     John Kent     2007-02-03   Renamed ACIA6850
-- 4.1     John Kent     2007-02-06   Made software reset synchronous
-- 4.2     John Kent     2007-02-25   Changed sensitivity lists
--                                    Rearranged Reset process.
-- 4.3     John Kent     2010-06-17   Updated header
-- 4.4     John Kent     2010-08-27   Combined with ACIA_RX & ACIA_TX
--                                    Renamed to acia6850
-- 4.5     John Kent     2012-02-04   Re-arranged Rx & Tx Baud clock edge detect.
-- 4.6     John Kent     3021-01-30   Double sample RxC, TxC, and RxD with cpu_clk
--                                    for 125MHz Clock on Zybo Z7 board.
--
 
library ieee;
  use ieee.std_logic_1164.all;
  use ieee.numeric_std.all;
  use ieee.std_logic_unsigned.all;
--library unisim;
--  use unisim.vcomponents.all;
 
-----------------------------------------------------------------------
-- Entity for ACIA_6850                                              --
-----------------------------------------------------------------------
 
entity acia6850 is
  port (
    --
    -- CPU Interface signals
    --
    clk      : in  std_logic;                     -- CPU Clock
    rst      : in  std_logic;                     -- Reset input (active high)
    cs       : in  std_logic;                     -- miniUART Chip Select
    addr     : in  std_logic;                     -- Register Select
    rw       : in  std_logic;                     -- Read / Not Write
    data_in  : in  std_logic_vector(7 downto 0);  -- Data Bus In 
    data_out : out std_logic_vector(7 downto 0);  -- Data Bus Out
    irq      : out std_logic;                     -- Interrupt Request out
    --
    -- RS232 Interface Signals
    --
    RxC   : in  std_logic;              -- Receive Baud Clock
    TxC   : in  std_logic;              -- Transmit Baud Clock
    RxD   : in  std_logic;              -- Receive Data
    TxD   : out std_logic;              -- Transmit Data
    DCD_n : in  std_logic;              -- Data Carrier Detect
    CTS_n : in  std_logic;              -- Clear To Send
    RTS_n : out std_logic               -- Request To send
    );
end acia6850;  --================== End of entity ==============================--
 
-------------------------------------------------------------------------------
-- Architecture for ACIA_6850 Interface registees
-------------------------------------------------------------------------------
 
architecture rtl of acia6850 is
 
  -----------------------------------------------------------------------------
  -- Reset Signals
  -----------------------------------------------------------------------------
  signal ac_rst        : std_logic;          -- Reset (Software & Hardware)
  signal rx_rst        : std_logic;          -- Receive Reset (Software & Hardware)
  signal tx_rst        : std_logic;          -- Transmit Reset (Software & Hardware)
 
  --------------------------------------------------------------------
  --  Status Register: status_reg 
  ----------------------------------------------------------------------
  --
  -----------+--------+-------+--------+--------+--------+--------+--------+
  --  Irq    | PErr   | OErr  | FErr   |  CTS   |  DCD   | TxRdy  | RxRdy  |
  -----------+--------+-------+--------+--------+--------+--------+--------+
  --
  -- Irq   - Bit[7] - Interrupt request
  -- PErr  - Bit[6] - Receive Parity error (parity bit does not match)
  -- OErr  - Bit[5] - Receive Overrun error (new character received before last read)
  -- FErr  - Bit[4] - Receive Framing Error (bad stop bit)
  -- CTS   - Bit[3] - Clear To Send level
  -- DCD   - Bit[2] - Data Carrier Detect (lost modem carrier)
  -- TxRdy - Bit[1] - Transmit Buffer Empty (ready to accept next transmit character)
  -- RxRdy - Bit[0] - Receive Data Ready (character received)
  -- 
  constant RXRBIT       : integer := 0; -- Receive Data Ready
  constant TXRBIT       : integer := 1; -- Transmir Data Ready
  constant DCDBIT       : integer := 2; -- Data Carrier Detect
  constant CTSBIT       : integer := 3; -- Clear To Send (inverted)
  constant FERBIT       : integer := 4; -- Framing Error
  constant OERBIT       : integer := 5; -- Over Run Error
  constant PERBIT       : integer := 6; -- Parity error
  constant IRQBIT       : integer := 7; -- Interrupt Request Flag
 
 
  ----------------------------------------------------------------------
  --  Control Register: control_reg
  ----------------------------------------------------------------------
  --
  -----------+--------+--------+--------+--------+--------+--------+--------+
  --  RIEBIT | TX1BIT | TX0BIT | DATBIT | STPBIT | POEBIT | BD1BIT | BD0BIT |
  -----------+--------+--------+--------+--------+--------+--------+--------+
  -- RIEBIT - Bit[7]
  -- 0       - Rx Interrupt disabled
  -- 1       - Rx Interrupt enabled
  --
  -- TXnBIT - Bits[6..5]
  -- 0 0     - RTS low,  Tx Interrupt Disabled
  -- 0 1     - RTS low,  Tx Interrupt Enable
  -- 1 0     - RTS high, Tx interrupt Disabled
  -- 1 1     - RTS low,  Tx interrupt Disabled, send break
  --
  -- DATBIT, STPBIT, POEBIT - Bits[4..2]
  -- 0 0 0   - 7 data, 2 stop, even parity
  -- 0 0 1   - 7 data, 2 stop, odd  parity
  -- 0 1 0   - 7 data, 1 stop, even parity
  -- 0 1 1   - 7 data, 1 stop, odd  parity
  -- 1 0 0   - 8 data, 2 stop, no   parity
  -- 1 0 1   - 8 data, 1 stop, no   parity
  -- 1 1 0   - 8 data, 1 stop, even parity
  -- 1 1 1   - 8 data, 1 stop, odd  parity
  -- BDnBIT - Bits[1..0]
  -- 0 0     - Baud Clk divide by 1
  -- 0 1     - Baud Clk divide by 16
  -- 1 0     - Baud Clk divide by 64
  -- 1 1     - reset
 
  constant BD0BIT       : integer := 0; -- Baud clock divider 0=>1/64 1=>16/RESET
  constant BD1BIT       : integer := 1; -- Baud clock divider 0=>1/16 1=>64/RESET
  constant POEBIT       : integer := 2; -- parity 0=>even 1=>odd            (ctrl(DATBIT)=1 & ctrl(STPBIT)=0 => no parity)
  constant STPBIT       : integer := 3; -- stop bits 0=>2 stop 1=>1 stop    (ctrl(DATBIT)=1 & ctrl(STPBIT)=0 & ctrl(POEBIT)=1 => 1 stop bit)
  constant DATBIT       : integer := 4; -- data bits 0=>7 data bits 1=>8 data bits
  constant TX0BIT       : integer := 5; -- Transmit control 0=>TX IRQ disabled 1=>RTS_N low
  constant TX1BIT       : integer := 6; -- Transmit control 1=>TX IRQ disabled 0=>RTS_N low (ctrl(TX0BIT)=1 & ctrl(TX1BIT)=1 => break)
  constant RIEBIT       : integer := 7; -- receive interrupt enable
 
  signal status_reg     : std_logic_vector(7 downto 0) := (others => '0'); -- status register        IO+0 RW=1
  signal control_reg    : std_logic_vector(7 downto 0) := (others => '0'); -- control register       IO+0 RW=0
  signal rx_data_reg    : std_logic_vector(7 downto 0) := (others => '0'); -- receive data register  IO+1 RW=1
  signal tx_data_reg    : std_logic_vector(7 downto 0) := (others => '0'); -- transmit data registet IO+1 RW=0
 
  signal status_read    : std_logic := '0';   -- Read status register
  signal rx_data_read   : std_logic := '0';   -- Read receive buffer
  signal tx_data_write  : std_logic := '0';   -- Write Transmit buffer
 
  signal RxRdy          : std_logic := '0';   -- Receive Data ready
  signal TxRdy          : std_logic := '1';   -- Transmit buffer empty
  signal DCDInt         : std_logic := '0';   -- DCD Interrupt
  signal FErr           : std_logic := '0';   -- Receive Data ready
  signal OErr           : std_logic := '0';   -- Receive Data ready
  signal PErr           : std_logic := '0';   -- Receive Data ready
  signal TxIE           : std_logic := '0';   -- Transmit interrupt enable
  signal RxIE           : std_logic := '0';   -- Receive interrupt enable
 
  --
  -- status register error bit controls
  --
  signal status_rxr_set : std_logic := '0';
  signal status_txr_set : std_logic := '0';
  signal status_fer_set : std_logic := '0';
  signal status_fer_clr : std_logic := '0';
  signal status_oer_set : std_logic := '0';
  signal status_oer_clr : std_logic := '0';
  signal status_per_set : std_logic := '0';
  signal status_per_clr : std_logic := '0';
 
 
  type dcd_state_type is (DCD_State_Idle, DCD_State_Int, DCD_State_Reset);
 
  signal DCDState       : DCD_State_Type;     -- DCD Reset state sequencer
  signal DCDDel         : std_logic := '0';   -- Delayed DCD_n
  signal DCDEdge        : std_logic := '0';   -- Rising DCD_N Edge Pulse
 
  -----------------------------------------------------------------------------
  -- RX & TX state machine types
  -----------------------------------------------------------------------------
 
  type state_type is ( start_state, data_state, parity_state, stop_state,  idle_state );
 
  -----------------------------------------------------------------------------
  -- RX Signals
  -----------------------------------------------------------------------------
 
  signal rx_current_state : state_type;                 -- receive bit current state
  signal rx_next_state    : state_type;                 -- receive bit next state
 
  signal RxDat          : std_logic := '1';                     -- Resampled RxD bit
  signal RxDDel         : Std_Logic_Vector(1 downto 0) := "11"; -- Delayed RxD Input
  signal RxDEdge        : Std_Logic := '0';                     -- RxD Edge pulse
  signal RxCDel         : Std_Logic_Vector(1 downto 0) := "00"; -- Delayed RxC Input
  signal RxCEdge        : Std_Logic := '0';                     -- RxC Edge pulse
  signal RxClkCnt       : Std_Logic_Vector(5 downto 0) := (others => '0'); -- Rx Baud Clock Counter
  signal RxBdClk        : Std_Logic := '0';             -- Rx Baud Clock
  signal RxBdDel        : Std_Logic := '0';             -- Delayed Rx Baud Clock
  signal RxBdEdgeRise   : Std_Logic := '0';             -- Rx Baud Clock rising edge
  signal RxBdEdgeFall   : Std_Logic := '0';             -- Rx Baud Clock falling edge
 
  signal rx_parity      : Std_Logic := '0';             -- Calculated RX parity bit
  signal rx_bit_count   : Std_Logic_Vector(3 downto 0) := (others => '0');  -- Rx Bit counter
  signal rx_shift_reg   : Std_Logic_Vector(7 downto 0) := (others => '0');  -- Shift Register
 
  -----------------------------------------------------------------------------
  -- TX Signals
  -----------------------------------------------------------------------------
 
  signal tx_current_state : state_type;                 -- Transmitter current state
  signal tx_next_state    : state_type;                 -- Transmitter next state
 
  signal TxOut          : std_logic := '1';             -- Transmit data bit
  signal TxDat          : std_logic := '1';             -- Transmit data bit
  signal TxCDel         : Std_Logic_Vector(1 downto 0) := "00";             -- Delayed TxC Input
  signal TxCEdge        : Std_Logic := '0';             -- TxC Edge pulse
  signal TxClkCnt       : Std_Logic_Vector(5 downto 0) := (others => '0');  -- Tx Baud Clock Counter
  signal TxBdClk        : Std_Logic := '0';             -- Tx Baud Clock
  signal TxBdDel        : Std_Logic := '0';             -- Delayed Tx Baud Clock
  signal TxBdEdgeRise   : Std_Logic := '0';             -- Tx Baud Clock rising edge
  signal TxBdEdgeFall   : Std_Logic := '0';             -- Tx Baud Clock falling edge
 
  signal tx_parity      : Std_logic := '0';              -- Parity Bit
  signal tx_bit_count   : Std_Logic_Vector(3 downto 0) := (others => '0');  -- Data Bit Counter
  signal tx_shift_reg   : Std_Logic_Vector(7 downto 0) := (others => '0');  -- Transmit shift register
  --
  -- Data register controls
  --
  type data_reg_type   is (data_reg_rst,  data_reg_load,   data_reg_idle);
  signal rx_data_ctrl   : data_reg_type;
 
  --
  -- Shift register controls
  --
  type shift_reg_type  is (shift_reg_rst, shift_reg_load, shift_reg_shift, shift_reg_idle);
  signal rx_shift_ctrl  : shift_reg_type;
  signal tx_shift_ctrl  : shift_reg_type;
 
  --
  -- Count register controls
  --  
  type count_reg_type  is (count_reg_rst, count_reg_decr,  count_reg_idle);
  signal rx_count_ctrl  : count_reg_type;
  signal tx_count_ctrl  : count_reg_type;
 
begin
 
---------------------------------------------------------------
-- ACIA Reset may be hardware or software
---------------------------------------------------------------
  acia_reset : process( clk, rst, ac_rst, dcd_n )
  begin
    --
    -- ACIA reset Synchronous 
    -- Includes software reset
    --
    if falling_edge(clk) then
      ac_rst <= (control_reg(1) and control_reg(0)) or rst;
    end if;              
    -- Receiver reset
    rx_rst <= ac_rst or DCD_n;
    -- Transmitter reset
    tx_rst <= ac_rst;
 
  end process;
 
 
-----------------------------------------------------------------------------
-- Generate Read / Write strobes.
-----------------------------------------------------------------------------
 
  acia_read_write : process(clk, rst)
  begin
    if falling_edge(clk) then
      status_read   <= '0';
      tx_data_write <= '0';
      rx_data_read  <= '0';
      if rst = '1' then
        control_reg(1 downto 0) <= "11";
        control_reg(7 downto 2) <= (others => '0');
        tx_data_reg   <= (others => '0');
      else
        if cs = '1' then
          if addr = '0' then              -- Control / Status register
            if rw = '0' then              -- write control register
              control_reg   <= data_in;
            else                          -- read status register
              status_read   <= '1';
            end if;
          else                            -- Data Register
            if rw = '0' then              -- write transmiter register
              tx_data_reg <= data_in;
              tx_data_write <= '1';
            else                          -- read receiver register
              rx_data_read  <= '1';
            end if;
          end if;
        end if;
      end if;
    end if;
  end process;
 
  -----------------------------------------------------------------------------
  -- ACIA Status Register
  -----------------------------------------------------------------------------
 
  acia_status : process( RxRdy, TxRdy, DCDInt, CTS_n, FErr, OErr, PErr, RxIE, TxIE,  status_reg, control_reg )
  begin
    TxIE <= (not control_reg(TX1BIT)) and control_reg(TX0BIT);
    RxIE <= control_reg(RIEBIT);
    status_reg(RXRBIT) <= RxRdy;
    status_reg(TXRBIT) <= TxRdy and (not CTS_n);
    status_reg(DCDBIT) <= DCDInt;                -- Data Carrier Detect
    status_reg(CTSBIT) <= CTS_n;                 -- Clear To Send
    status_reg(FERBIT) <= FErr;
    status_reg(OERBIT) <= OErr;
    status_reg(PERBIT) <= PErr;
    status_reg(IRQBIT) <= (RxIE and RxRdy) or
                          (RxIE and DCDInt) or
                          (TxIE and TxRdy);
    irq <= status_reg(IRQBIT);
  end process;
 
  -----------------------------------------------------------------------------
  -- ACIA Rx data ready status
  -----------------------------------------------------------------------------
 
  acia_rx_data_ready : process( clk, rx_rst, RxBdEdgeFall, status_rxr_set )
  begin
    if falling_edge( clk ) then
      if rx_rst = '1' then
        RxRdy <= '0';
      else
        if RxBdEdgeFall = '1' and status_rxr_set = '1' then
          RxRdy <= '1';
        elsif rx_data_read  = '1' then
          RxRdy <= '0';
        end if;
      end if;
    end if;
  end process;
 
 
  -----------------------------------------------------------------------------
  -- ACIA Tx data ready status
  -----------------------------------------------------------------------------
 
  acia_tx_data_ready : process( clk, tx_rst, TxBdEdgeRise, status_txr_set )
  begin
    if falling_edge( clk ) then
      if tx_rst = '1' then
         TxRdy <= '1';
      else
        if TxBdEdgeRise = '1' and status_txr_set = '1' then
          TxRdy <= '1';
        elsif  tx_data_write  = '1' then
          TxRdy <= '0';
        end if;
      end if;
    end if;
  end process;
 
 
  -----------------------------------------------------------------------------
  -- ACIA Framing Error
  -----------------------------------------------------------------------------
 
  acia_fer_status : process( clk )
  begin
    if falling_edge( clk ) then
      if rx_rst = '1' then
        FErr <= '0';
      elsif RxBdEdgeFall = '1' then
        if status_fer_clr = '1' then
          FErr <= '0';
        end if;
        if status_fer_set = '1' then
          FErr <= '1';
        end if;
      end if;
    end if;
  end process;
 
  -----------------------------------------------------------------------------
  -- ACIA Over-run Error
  -----------------------------------------------------------------------------
 
  acia_oer_status : process( clk )
  begin
    if falling_edge( clk ) then
      if rx_rst = '1' then
        OErr <= '0';
      elsif RxBdEdgeFall = '1' then
        if status_oer_set = '1' then
          OErr <= '1';
        end if;
        if status_oer_clr = '1' or rx_data_read = '1' then
          OErr <= '0';
        end if;
      end if;
    end if;
  end process;
 
 
  -----------------------------------------------------------------------------
  -- ACIA Parity Error
  -----------------------------------------------------------------------------
 
  acia_per_status : process( clk )
  begin
    if falling_edge( clk ) then
      if rx_rst = '1' then
        PErr <= '0';
      elsif RxBdEdgeFall = '1' and status_per_set = '1' then
        PErr <= '1';
      elsif RxBdEdgeFall = '1' and status_per_clr = '1' then
        PErr <= '0';
      elsif rx_data_read = '1' then
        PErr <= '0';
      end if;
    end if;
  end process;
 
---------------------------------------------------------------
-- Set Data Output Multiplexer
--------------------------------------------------------------
 
  acia_data_mux : process(addr, rx_data_reg, status_reg)
  begin
    if addr = '1' then
      data_out <= rx_data_reg;               -- read receiver register
    else
      data_out <= status_reg;               -- read status register
    end if;
  end process;
 
---------------------------------------------------------------
-- Data Carrier Detect Edge rising edge detect
---------------------------------------------------------------
  acia_dcd_edge : process( clk, ac_rst )
  begin
    if falling_edge(clk) then
      if ac_rst = '1' then
        DCDDel  <= '0';
        DCDEdge <= '0';
      else
        DCDDel  <= DCD_n;
        DCDEdge <= DCD_n and (not DCDDel);
      end if;
    end if;
  end process;
 
 
---------------------------------------------------------------
-- Data Carrier Detect Interrupt
---------------------------------------------------------------
-- If Data Carrier is lost, an interrupt is generated
-- To clear the interrupt, first read the status register
--      then read the data receive register
 
  acia_dcd_int : process( clk, ac_rst )
  begin
    if falling_edge(clk) then
      if ac_rst = '1' then
        DCDInt   <= '0';
        DCDState <= DCD_State_Idle;
      else
        case DCDState is
        when DCD_State_Idle =>
          -- DCD Edge activates interrupt
          if DCDEdge = '1' then
            DCDInt   <= '1';
            DCDState <= DCD_State_Int;
          end if;
        when DCD_State_Int =>
          -- To reset DCD interrupt, 
          -- First read status
          if status_read = '1' then
            DCDState <= DCD_State_Reset;
          end if;
        when DCD_State_Reset =>
          -- Then read receive register
          if rx_data_read = '1' then
            DCDInt   <= '0';
            DCDState <= DCD_State_Idle;
          end if;
        when others =>
          null;
        end case;
      end if;
    end if;
  end process;
 
 
  ---------------------------------------------------------------------
  -- Receiver Clock Edge Detection
  ---------------------------------------------------------------------
  -- A rising edge will produce a one CPU clock cycle pulse
  --
  acia_rx_clock_edge : process( clk, rx_rst )
  begin
    if falling_edge(clk) then
      if rx_rst = '1' then
        RxCDel  <= "00";
        RxCEdge <= '0';
      else
        --
        -- RxClkEdge is one CPU clock cycle wide
        --
        RxCDel(0) <= RxC;
        RxCDel(1) <= RxCDel(0);
        RxCEdge <= (not RxCDel(1)) and RxCDel(0);
      end if;
    end if;
  end process;
 
  ---------------------------------------------------------------------
  -- Receiver Data Edge Detection
  ---------------------------------------------------------------------
  --
  -- If there is a falling edge on the RxD line
  -- and the ACIA receiver is in the start, stop or idle state 
  -- RxDatEdge is generated to reset the Baud Clock divide
  -- so that it is synchronized to the edge of the data
  -- 2021-01-30 JEK Double clock RxD
  acia_rx_data_edge : process( clk, rx_rst, RxD, RxDDel )
  begin
    if falling_edge(clk) then
      if (rx_rst = '1') then
        RxDDel(0) <= RxD;
		  RxDDel(1) <= RxDDel(0);
        RxDEdge <= '0';
      else
        RxDDel(0) <= RxD;
		  RxDDel(1) <= RxDDel(0);
        RxDEdge <= not(RxDDel(0)) and RxDDel(1);
      end if;
    end if;
  end process;
 
  ---------------------------------------------------------------------
  -- Receiver Clock Divider
  ---------------------------------------------------------------------
  -- Hold the Rx Clock divider in reset when the receiver is disabled
  -- Advance the count only on a rising Rx clock edge
  --
  acia_rx_clock_divide : process( clk, rx_rst, RxDEdge, RxCEdge )
  begin
    if falling_edge(clk) then
      if (rx_rst = '1') or (RxDEdge = '1') then
        --
        -- Reset counter on rx_rst or falling data edge
        --
        RxClkCnt  <= (others => '0');
      elsif RxCEdge = '1' then 
        --         
        -- increment count on Rx Clock edge
        --
        RxClkCnt <= RxClkCnt + "000001";
      end if;
    end if;
  end process;
 
 
-----------------------------------------------------------------------------
-- ACIA RX Baud select
-----------------------------------------------------------------------------
-- 2021-01-30 JEK change RxC to RxCDel(0)
  acia_rx_baud_control : process(clk, control_reg, RxC, RxClkCnt )
  begin
    ---------------------------------------------------------------------
    -- Receive Baud Clock Selector
    ---------------------------------------------------------------------
    -- control_reg(BD1BIT downto BD0BIT)
    -- 0 0     - Baud Clk divide by 1
    -- 0 1     - Baud Clk divide by 16
    -- 1 0     - Baud Clk divide by 64
    -- 1 1     - reset
    if falling_edge(clk) then
      case control_reg(BD1BIT downto BD0BIT) is
      when "00" =>	                         -- Div by 1
        RxBdClk <= RxCDel(0);
      when "01" =>	                         -- Div by 16
        RxBdClk <= RxClkCnt(3);
      when "10" =>	                         -- Div by 64
        RxBdClk <= RxClkCnt(5);
      when others =>                          -- Software reset
        RxBdClk <= '0';
      end case;
    end if;
 
  end process;
 
 
  ---------------------------------------------------------------------
  -- Receiver Baud Clock Edge Detection
  ---------------------------------------------------------------------
  -- A Rising Baud Clock edge will produce a single CPU clock pulse
  --
  acia_rx_baud_edge : process( clk, rx_rst, RxBdClk, RxBdDel )
  begin
    if falling_edge(clk) then
      if rx_rst = '1' then
        RxBdDel      <= '0';
        RxBdEdgeRise <= '0';
        RxBdEdgeFall <= '0';
      else
        RxBdDel      <= RxBdClk;
        RxBdEdgeRise <= not(RxBdDel) and     RxBdClk;
        RxBdEdgeFall <=     RxBdDel  and not(RxBdClk);
      end if;
    end if;
  end process;
 
  ---------------------------------------------------------------------
  -- Receiver process
  ---------------------------------------------------------------------
  -- WdFmt - Bits[4..2]
  -- 0 0 0   - 7 data, even parity, 2 stop
  -- 0 0 1   - 7 data, odd  parity, 2 stop
  -- 0 1 0   - 7 data, even parity, 1 stop
  -- 0 1 1   - 7 data, odd  parity, 1 stop
  -- 1 0 0   - 8 data, no   parity, 2 stop
  -- 1 0 1   - 8 data, no   parity, 1 stop
  -- 1 1 0   - 8 data, even parity, 1 stop
  -- 1 1 1   - 8 data, odd  parity, 1 stop
  --
  -- Registers activated on rising bit clock edge
  -- State transitions on falling bit clock edge
  --
  acia_rx_receive : process( rx_current_state, control_reg, rx_bit_count, RxDDel, RxDat, rx_parity, RxRdy )
  begin
          rx_data_ctrl   <= data_reg_idle;
          rx_shift_ctrl  <= shift_reg_idle;
          rx_next_state  <= start_state;
 
          status_rxr_set <= '0';                       -- receive data ready
          status_fer_clr <= '0';                       -- framing error status
          status_fer_set <= '0';                       -- framing error status
          status_oer_clr <= '0';                       -- over-run error status
          status_oer_set <= '0';                       -- over-run error status
          status_per_clr <= '0';                       -- parity  error status
          status_per_set <= '0';                       -- parity  error status
 
          case rx_current_state is
          when start_state =>
            rx_shift_ctrl <= shift_reg_rst;            -- Reset Shift register on rising baud clock
            if RxDat = '0' then                        -- RxDat = '0' => start bit
              rx_next_state <= data_state;             -- if low, start shifting in data
            end if;
 
          when data_state =>                           -- Receiving data bits
            --  on rising baud clock edge 
            rx_shift_ctrl <= shift_reg_shift;          -- shift in data bit
 
            -- on falling baud clock edge transition state
            if rx_bit_count = "0000" then               -- All bits shifted in ?
              --                                       -- yes, transition to parity or stop state
              -- if control_reg(DATBIT) = '1' and control_reg(STPBIT) = '0'
              -- then 8 data bit, no parity
              --
              if control_reg(DATBIT)='1' and control_reg(STPBIT)='0' then                                                   
                rx_next_state  <= stop_state;          -- 8 data, 1 or 2 stops, no parity => stop_state
              else
                rx_next_state <= parity_state;         -- control_reg(DATBIT) = '0' => 7 data + parity or
              end if;                                  -- control_reg(STPBIT) = '1' => 8 data + parity => parity_state
            else
              rx_next_state <= data_state;             -- bit count none zero => remain in data state
            end if;
 
          when parity_state =>                         -- Receive Parity bit
            -- on rising baud clock edge:
            if control_reg(DATBIT) = '0' then          -- if 7 data bits, shift parity into MSB
              rx_shift_ctrl <= shift_reg_shift;        -- 7 data + parity
            end if;
 
            -- on falling baud clock edge, set parity
            if rx_parity = (RxDat xor control_reg(POEBIT)) then
              status_per_set <= '1';                   -- set parity  error status
            else
              status_per_clr <= '1';                   -- resetset parity  error status
            end if;
            rx_next_state <= stop_state;
 
          when stop_state =>                           -- stop bit (Only one required for RX)
            -- on falling baud clock edge
            rx_data_ctrl   <= data_reg_load;             -- load receive data reg with shift register
            status_rxr_set <= '1';                       -- flag receive data ready
 
            -- on falling baud clock edge
            if control_reg(DATBIT)='1' and control_reg(STPBIT)='0' then                                                   
                status_per_clr <= '1';                 -- reset parity  error status if no parity
            end if;
 
            -- on falling baud clock edge
            if RxRdy = '1' then                        -- Has previous data been read ? 
              status_oer_set <= '1';                   -- no, set over-run  error status
            else
              status_oer_clr <= '1';                   -- yes, reset over-run  error status
            end if;
 
            -- on falling baud clock edge
            if RxDat = '1' then                          -- stop bit received ?
              status_fer_clr <= '1';                   -- yes, reset framing error status
            else
              status_fer_set <= '1';                   -- no, set framing error status
            end if;
 
            -- on falling baud clock edge
            if RxDat = '0' then                        -- wait until RxDat returns high
              rx_next_state  <= idle_state;
            end if;
 
          when idle_state =>
            if RxDat = '0' then                        -- wait until RxD returns high
              rx_next_state <= idle_state;
            end if;
 
          when others =>
            null;
          end case;
  end process;
 
  ---------------------------------------------------------------------
  -- Rx State machine
  ---------------------------------------------------------------------
  --
  -- State machine transitions on the falling edge of the Rx Baud clock
  --
  acia_rx_state : process(clk, rst )
  begin
    if falling_edge( clk ) then
      if rx_rst = '1' then
        rx_current_state <= start_state;
      else
        if RxBdEdgeFall = '1' then
          rx_current_state <= rx_next_state;
        end if;
      end if;
    end if;
 
  end process;
 
 
  -----------------------------------------------------------------------------
  -- ACIA Rx Shift Register
  -----------------------------------------------------------------------------
  -- 2021-01-30 JEK change RxD to RxDDel(0)
  --
  acia_rx_shift_reg : process( clk, rx_rst, RxBdEdgeRise, rx_shift_reg, RxDDel, rx_parity )
  begin
    if falling_edge( clk ) then
      if rx_rst = '1' then
        RxDat <= '1';
        rx_bit_count <= (others=>'0');
        rx_shift_reg <= (others=>'0');
        rx_parity    <= '0';        
      elsif RxBdEdgeRise = '1' then
        RxDat <= RxDDel(0);
        case rx_shift_ctrl is
        when shift_reg_rst =>
          if control_reg(DATBIT) = '0' then  -- control_reg(DATBIT) = '0' => 7 data bits
            rx_bit_count <= "0111";
          else                               -- control_reg(DATBIT) = '1' => 8 data bits
            rx_bit_count <= "1000";
          end if;
          rx_shift_reg <= (others=>'0');
          rx_parity    <= '0';
        when shift_reg_shift =>
          rx_bit_count <= rx_bit_count - "0001";
          rx_shift_reg <= RxDDel(0) & rx_shift_reg(7 downto 1);
          rx_parity    <= rx_parity xor RxDDel(0);
        when others =>
          null;
        end case;
      end if;
    end if;
  end process;
 
  -----------------------------------------------------------------------------
  -- ACIA Rx Data Register
  -----------------------------------------------------------------------------
 
  acia_rx_data_reg : process( clk, rx_rst, rx_shift_reg )
  begin
    if falling_edge( clk ) then
      if rx_rst = '1' then
        rx_data_reg <= (others=>'0');
      elsif RxBdEdgeFall = '1' then
        case rx_data_ctrl is
        when data_reg_rst =>
          rx_data_reg <= (others=>'0');
        when data_reg_load =>
          rx_data_reg  <= rx_shift_reg;
        when others =>
          null;
        end case;
      end if;
    end if;
  end process;
 
  -----------------------------------------------------------------------------
  -- ACIA TX control
  -----------------------------------------------------------------------------
 
  acia_tx_control : process(control_reg, TxDat )
  begin
    case control_reg(TX1BIT downto TX0BIT) is
      when "00" =>                      -- Disable TX Interrupts, Assert RTS
        TxD   <= TxDat;
        RTS_n <= '0';
      when "01" =>                      -- Enable TX interrupts, Assert RTS
        TxD   <= TxDat;
        RTS_n <= '0';
      when "10" =>                      -- Disable Tx Interrupts, Clear RTS
        TxD   <= TxDat;
        RTS_n <= '1';
      when "11" =>                      -- Disable Tx interrupts, Assert RTS, send break
        TxD   <= '0';
        RTS_n <= '0';
      when others =>
        null;
    end case;
  end process;
 
  ---------------------------------------------------------------------
  -- Transmit Clock Edge Detection
  -- A rising edge will produce a one clock cycle pulse
  ---------------------------------------------------------------------
  -- 2021-01-30 JEK add one more bit to TxCDel. Double sample TxC
  acia_tx_clock_edge : process( Clk, tx_rst )
  begin
    if falling_edge(clk) then
      if tx_rst = '1' then
        TxCDel(1 downto 0) <= "00";
        TxCEdge <= '0';
      else
        TxCDel(0) <= TxC;
		  TxCDel(1) <= RxCDel(0);
        TxCEdge <= (not TxCDel(1)) and TxCDel(0);
      end if;
    end if;
  end process;
 
  ---------------------------------------------------------------------
  -- Transmit Clock Divider
  -- Advance the count only on an input clock pulse
  ---------------------------------------------------------------------
 
  acia_tx_clock_divide : process( clk, tx_rst )
  begin
    if falling_edge(clk) then
      if tx_rst = '1' then
        TxClkCnt <= (others=>'0');
      elsif TxCEdge = '1' then 
        TxClkCnt <= TxClkCnt + "000001";
      end if;
    end if;
  end process;
 
 
-----------------------------------------------------------------------------
-- ACIA TX Baud select
-----------------------------------------------------------------------------
-- 2021-01-30 JEK change TxC to TxCDel(0)
  acia_tx_baud_select : process(clk, control_reg, TxDat, TxCDel, TxClkCnt )
  begin
 
    ---------------------------------------------------------------------
    -- Transmit Baud Clock Selector
    ---------------------------------------------------------------------
    -- control_reg(BD1BIT downto BD0BIT)
    -- 0 0     - Baud Clk divide by 1
    -- 0 1     - Baud Clk divide by 16
    -- 1 0     - Baud Clk divide by 64
    -- 1 1     - reset
    if falling_edge(clk) then
      case control_reg(BD1BIT downto BD0BIT) is
      when "00" =>	                         -- Div by 1
        TxBdClk <= TxCDel(0);
      when "01" =>	                         -- Div by 16
        TxBdClk <= TxClkCnt(3);
      when "10" =>	                         -- Div by 64
        TxBdClk <= TxClkCnt(5);
      when others =>                          -- Software reset
        TxBdClk <= '0';
      end case;
    end if;
 
  end process;
 
 
  ---------------------------------------------------------------------
  -- Transmit Baud Clock Edge Detection
  ---------------------------------------------------------------------
  -- A Falling edge will produce a single pulse on TxBdEdgeFall
  --
  acia_tx_baud_edge : process( clk, tx_rst )
  begin
    if falling_edge(clk) then
      if tx_rst = '1' then
        TxBdDel      <= '0';
        TxBdEdgeRise <= '0';
        TxBdEdgeFall <= '0';
      else
        TxBdDel  <= TxBdClk;
        TxBdEdgeRise <= (not TxBdDel) and TxBdClk;
        TxBdEdgeFall <= TxBdDel and (not TxBdClk);
      end if;
    end if;
  end process;
 
  -----------------------------------------------------------------------------
  -- Implements the Tx unit
  -----------------------------------------------------------------------------
  -- WdFmt - Bits[4..2]
  -- 0 0 0   - 7 data, even parity, 2 stop
  -- 0 0 1   - 7 data, odd  parity, 2 stop
  -- 0 1 0   - 7 data, even parity, 1 stop
  -- 0 1 1   - 7 data, odd  parity, 1 stop
  -- 1 0 0   - 8 data, no   parity, 2 stop
  -- 1 0 1   - 8 data, no   parity, 1 stop
  -- 1 1 0   - 8 data, even parity, 1 stop
  -- 1 1 1   - 8 data, odd  parity, 1 stop
  acia_tx_transmit : process( tx_current_state, TxRdy, tx_bit_count, tx_shift_reg, control_reg, tx_parity )
  begin
          status_txr_set <= '0';
          case tx_current_state is
          when idle_state =>
            TxOut <= '1';
            tx_shift_ctrl <= shift_reg_idle;
            if TxRdy = '0' then
              tx_next_state <= start_state;
            else
              tx_next_state <= idle_state;
            end if;
 
          when start_state =>
            TxOut         <= '0';                    -- Start bit
            tx_shift_ctrl <= shift_reg_load;         -- Load Shift reg with Tx Data
            tx_next_state <= data_state;
 
          when data_state =>
            TxOut <= tx_shift_reg(0);
            tx_shift_ctrl <= shift_reg_shift;        -- shift tx shift reg
            if tx_bit_count = "000" then
              if (control_reg(DATBIT) = '1') and (control_reg(STPBIT) = '0') then
                if control_reg(POEBIT) = '0' then    -- 8 data bits
                  tx_next_state <= stop_state;       -- 2 stops
                else
                  tx_next_state <= idle_state;       -- 1 stop
                end if;
              else
                tx_next_state <= parity_state;        -- parity
              end if;
            else
              tx_next_state <= data_state;
            end if;
 
          when parity_state =>                        -- 7/8 data + parity bit
            if control_reg(POEBIT) = '0' then
              TxOut <= not(tx_parity);                -- even parity
            else
              TxOut <= tx_parity;                     -- odd parity
            end if;
            tx_shift_ctrl <= shift_reg_idle;
            if control_reg(STPBIT) = '0' then
              tx_next_state <= stop_state;            -- 2 stops
            else
              tx_next_state <= idle_state;            -- 1 stop
            end if;
 
          when stop_state =>                          -- first of two stop bits
            TxOut          <= '1';
            tx_shift_ctrl  <= shift_reg_idle;
            status_txr_set <= '1';
            tx_next_state  <= idle_state;
 
 --         when others =>
 --           null;
 
          end case;
  end process;
 
  ---------------------------------------------------------------------
  -- Tx State machine
  ---------------------------------------------------------------------
  --
  -- State machine transitions on the rising edge of the Tx Baud clock
  --
  acia_tx_state : process(clk, tx_rst )
  begin
     if falling_edge( clk ) then
      if tx_rst = '1' then
        tx_current_state <= idle_state;
      else
        if TxBdEdgeRise = '1' then
          tx_current_state <= tx_next_state;
        end if;
      end if;
    end if;
  end process;
 
  -----------------------------------------------------------------------------
  -- ACIA tx Shift Register
  -----------------------------------------------------------------------------
 
  acia_tx_shift_reg : process( clk, TxBdEdgeFall, tx_shift_ctrl, tx_data_reg, tx_shift_reg, tx_parity )
  begin
    if falling_edge( clk ) then
      if tx_rst = '1' then
        tx_bit_count <= (others=>'0');
        tx_shift_reg <= (others=>'1');
        tx_parity    <= '0';
        TxDat        <= '1';        
      elsif TxBdEdgeFall = '1' then
        TxDat <= TxOut;
        case tx_shift_ctrl is
        when shift_reg_load =>
          if control_reg(DATBIT) = '0' then  -- control_reg(DATBIT) = '0' => 7 data bits
            tx_bit_count <= "0111";
          else                               -- control_reg(DATBIT) = '1' => 8 data bits
            tx_bit_count <= "1000";
          end if;
          tx_shift_reg  <= tx_data_reg;
          tx_parity     <= '0';
        when shift_reg_shift =>
          tx_bit_count <= tx_bit_count - "0001";
          tx_shift_reg <= '1' & tx_shift_reg(7 downto 1);
          tx_parity    <= tx_parity xor tx_shift_reg(0);
        when others =>
          null;
        end case;
      end if;
    end if;
  end process;
 
 
end rtl;
 
 

Go to most recent revision | 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.