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

Subversion Repositories sd_mmc_emulator

[/] [sd_mmc_emulator/] [trunk/] [rtl/] [uart_sqclk_pack.vhd] - Rev 2

Compare with Previous | Blame | View Log

--------------------------------------------------------------------------
-- Package of UART components
--
-- This UART uses a squarewave input for the BAUDRATE clock.  In other
-- words, the BAUD rate is exactly the same as the frequency of the
-- incoming clock.  This is in contrast to other UARTs which need a
-- Baud rate clock which is some multiple of the actual Baud rate
-- desired.  Because of the 1x nature of the Baud clock, the receiver
-- needs at least one Baud Clock interval in which to measure the
-- Baud clock versus the system clock, before it can start working.
-- Also, the system clock must be somewhat higher than the Baud clock
-- in order for the receiver to work.
--
-- This package contains the UART, plus individual async_tx and async_rx
-- modules, which are the transmit and receive sections of the UART.
--
--
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
 
package uart_sqclk_pack is
 
  component uart_sqclk
    port (
 
      sys_rst_n     : in std_logic;
      sys_clk       : in std_logic;
      sys_clk_en    : in std_logic;
 
      -- rate and parity
      parity_i      : in unsigned(1 downto 0); -- 0=none, 1=even, 2 or 3=odd
      rate_clk_i    : in std_logic;
 
      -- serial I/O
      tx_stream     : out std_logic;
      rx_stream     : in std_logic;
 
      --control and status
      tx_wr_i       : in  std_logic;        -- Starts Transmit
      tx_dat_i      : in  unsigned(7 downto 0);
      tx_done_o     : out std_logic;
      rx_restart_i  : in  std_logic;        -- High clears error flags, clears rx_done_o
      rx_dat_o      : out unsigned(7 downto 0);
      rx_wr_o       : out std_logic;        -- High pulse means store rx_dat_o.
      rx_done_o     : out std_logic;        -- Remains high after receive, until clk edge with rx_restart_i=1
      frame_err_o   : out std_logic;        -- High = error.  Reset when rx_restart_i asserted.
      parity_err_o  : out std_logic         -- High = error.  Reset when rx_restart_i asserted.
    );
  end component;
 
  component async_tx_sqclk
    port (
 
      sys_rst_n    : in std_logic;
      sys_clk      : in std_logic;
      sys_clk_en   : in std_logic;
 
      -- rate and parity
      tx_parity_i  : in unsigned(1 downto 0); -- 0=none, 1=even, 2 or 3=odd
      tx_clk_i     : in std_logic;
 
      -- serial output
      tx_stream    : out std_logic;
 
      -- control and status
      tx_wr_i      : in  std_logic;        -- Starts Transmit
      tx_dat_i     : in  unsigned(7 downto 0);
      tx_done_o    : out std_logic
    );
  end component;
 
 
  component async_rx_sqclk
    port (
 
      sys_rst_n    : in std_logic;
      sys_clk      : in std_logic;
      sys_clk_en   : in std_logic;
 
      -- rate and parity
      rx_parity_i  : in unsigned(1 downto 0); -- 0=none, 1=even, 2 or 3=odd
      rx_clk_i     : in std_logic;
 
      -- serial input
      rx_stream    : in std_logic;
 
      -- control and status
      rx_restart_i : in  std_logic;        -- High clears error flags, clears rx_done_o
      rx_dat_o     : out unsigned(7 downto 0);
      rx_wr_o      : out std_logic;        -- High pulse means store rx_dat_o.
      rx_done_o    : out std_logic;        -- Remains high after receive, until rx_restart_i
      frame_err_o  : out std_logic;        -- High = error.  Reset when rx_restart_i asserted.
      parity_err_o : out std_logic         -- High = error.  Reset when rx_restart_i asserted.
    );
  end component;
 
  component half_duplex_switch
    generic (
      HANG_TIME : integer;  -- Units of timer_i rising edges
      TX_DELAY  : integer   -- Units of timer_i rising edges
    );
    port (
 
      sys_rst_n  : in  std_logic;
      sys_clk    : in  std_logic;
      sys_clk_en : in  std_logic;
 
      -- Full Duplex side
      tx_i       : in  std_logic;
      rx_o       : out std_logic;
 
      -- Half Duplex side
      hd_o       : out std_logic;
      hd_i       : in  std_logic;
      hd_drive_o : out std_logic;
 
      -- Timer
      tick_i     : in  std_logic
    );
  end component;
 
end uart_sqclk_pack;
 
package body uart_sqclk_pack is
end uart_sqclk_pack;
 
 
--------------------------------------------------------------------
-- UART.  Variable Speed, RX Buffer, but no TX buffer
-- High Speed Asynchronous Receiver & Transmitter
-- 
-- Description:
--   This block receives and transmits asynchronous serial bytes.  The Baudrate
-- and parity are selectable through inputs, but the number of bits per character
-- is fixed at eight.
--
-- NOTES:
-- Transmit starts when tx_wr_i is detected high at a rising clock edge.
-- Once the transmit operation is completed, tx_done_o latches high.
--
-- The receive input is passed through two layers of synchronizing flip-flops
-- to help mitigate metastability issues, since this signal can come from
-- outside of the sys_clk clock domain.  All other logic connecting to inputs
-- of this function are assumed to be within the same clock domain.
--
-- The receiver looks for a new start bit immediately following the rx_wr_o
-- pulse, but rx_done_o is delayed until the expected end of the received
-- character.  If a new start bit is detected just prior to the expected end
-- of the received character, then rx_done_o is not asserted.
--
-- Receive begins when the falling edge of the start bit is detected.
-- Then after 10 or 11 bit times have passed (depending on the parity setting)
-- the rx_wr_o signal will pulse high for one clock period, indicating rx_dat_o
-- contains valid receive data.  The rx_wr_o pulse is only issued if there is
-- no parity error, and the stop bit is actually detected high at the sampling
-- time.
-- The rx_dat_o outputs will hold the received data until the next rx_wr_o pulse.
-- The rx_dat_o output provides the received data, even in the presence of a
-- parity or stop-bit error.
-- Error flags are valid during rx_wr_o, but they remain latched, until
-- rx_restart_i.
--
-- The Baud rate is equal to the frequency of the rate_clk_i input.
-- It should be, as nearly as possible, a square wave of the desired
-- communications rate.
--
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
 
entity uart_sqclk is
    port (
 
      sys_rst_n     : in std_logic;
      sys_clk       : in std_logic;
      sys_clk_en    : in std_logic;
 
      -- rate and parity
      parity_i      : in unsigned(1 downto 0); -- 0=none, 1=even, 2 or 3=odd
      rate_clk_i    : in std_logic;
 
      -- serial I/O
      tx_stream     : out std_logic;
      rx_stream     : in std_logic;
 
      --control and status
      tx_wr_i       : in  std_logic;        -- Starts Transmit
      tx_dat_i      : in  unsigned(7 downto 0);
      tx_done_o     : out std_logic;
      rx_restart_i  : in  std_logic;        -- High clears error flags, clears rx_done_o
      rx_dat_o      : out unsigned(7 downto 0);
      rx_wr_o       : out std_logic;        -- High pulse means store rx_dat_o.
      rx_done_o     : out std_logic;        -- Remains high after receive, until clk edge with rx_restart_i=1
      frame_err_o   : out std_logic;        -- High = error.  Reset when rx_restart_i asserted.
      parity_err_o  : out std_logic         -- High = error.  Reset when rx_restart_i asserted.
    );
end uart_sqclk;
 
library work;
use work.uart_sqclk_pack.all;
 
architecture beh of uart_sqclk is
 
-- Components
 
begin
 
  tx1: async_tx_sqclk
    port map (        
       sys_rst_n     => sys_rst_n,
       sys_clk       => sys_clk,
       sys_clk_en    => sys_clk_en,
 
       -- rate and parity
       tx_parity_i   => parity_i, -- 0=none, 1=even, 2 or 3=odd
       tx_clk_i      => rate_clk_i,
 
       -- serial output
       tx_stream     => tx_stream,
 
       -- control and status
       tx_wr_i       => tx_wr_i,       -- Starts Transmit
       tx_dat_i      => tx_dat_i,
       tx_done_o     => tx_done_o
    );
 
  rx1: async_rx_sqclk
    port map (        
       sys_rst_n     => sys_rst_n,
       sys_clk       => sys_clk,
       sys_clk_en    => sys_clk_en,
 
       -- rate and parity
       rx_parity_i   => parity_i, -- 0=none, 1=even, 2 or 3=odd
       rx_clk_i      => rate_clk_i,
 
       -- serial input
       rx_stream     => rx_stream,
 
       -- control and status
       rx_restart_i  => rx_restart_i,  -- High clears error flags, clears rx_done_o
       rx_dat_o      => rx_dat_o,
       rx_wr_o       => rx_wr_o,       -- High pulse means store rx_dat_o.
       rx_done_o     => rx_done_o,     -- Remains high after receive, until rx_restart_i
       frame_err_o   => frame_err_o,   -- High = error.  Reset when rx_restart_i asserted.
       parity_err_o  => parity_err_o   -- High = error.  Reset when rx_restart_i asserted.
    );
 
end beh;
 
 
-------------------------------------------------------------------------------
-- Asynchronous Transmitter With No Buffering
------------------------------------------------------------------------------- 
--
-- Author: John Clayton
-- Date  : Aug  08, 2013 Added this change log header, which was missing.
--                       Changed tx_done_o signal so that it pulses after
--                       the stop bit is finished.  How could this have
--                       remained so woefully incorrect for so long?!
--         Jan  02, 2014 Fixed a latent bug in the logic for asserting
--                       tx_done_o.  Prior to this fix, it was possible
--                       for a write that was coincident with do_txbit to
--                       be ignored!  Once again, how could this have
--                       remained so woefully incorrect all this time?!
--         Jan  07, 2014 Rewrote the startup logic to allow for cases
--                       when tx_wr_i='1' and tx_bcnt="0000" and do_txbit='1'
--                       Also rewrote the tx_done_o signal so that it is
--                       asserted earlier - when "tx_almost_done" is high.
--                       This is all calculated to allow the transmitter
--                       to send characters back-to-back using its own
--                       tx_done_o signal as a tx_wr_i signal.  This is
--                       actually getting pretty neat.  The unit sends out
--                       asynchronous characters, but insists on doing it
--                       in synchronism with the tx_clk_i input... so it
--                       isn't really very asynchronous in that sense!
--
-- Description
-------------------------------------------------------------------------------
-- Squarewave tx_clk_i input determines rate.
-- (tx_clk_i need not be a squarewave for this module, since only the rising
--  edge is used.  In the accompanying receiver, however, both edges are used.)
-- 
-- Description:
--   This block transmits asynchronous serial bytes.  The Baudrate and parity
-- are determined by inputs, but the number of bits per character is
-- fixed at eight.
--
-- NOTES:
-- Transmit starts when the transmitter is idle and tx_wr_i is detected high
-- at a rising sys_clk edge.
--
-- Once the transmit operation is completed, done_o latches high.
--
-- Since the baud clock might be asynchronous to the sys_clk, there are
-- syncronizing flip-flops on it inside this module.
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
 
entity async_tx_sqclk is
    port (
 
      sys_rst_n    : in std_logic;
      sys_clk      : in std_logic;
      sys_clk_en   : in std_logic;
 
      -- rate and parity
      tx_parity_i  : in unsigned(1 downto 0); -- 0=none, 1=even, 2 or 3=odd
      tx_clk_i     : in std_logic;
 
      -- serial output
      tx_stream    : out std_logic;
 
      -- control and status
      tx_wr_i      : in  std_logic;        -- Starts Transmit
      tx_dat_i     : in  unsigned(7 downto 0);
      tx_done_o    : out std_logic
    );
end async_tx_sqclk;
 
architecture beh of async_tx_sqclk is
 
-- TX signals
  -- TX clock synchronizing flip-flops and rising edge detection
signal tx_clk_r1 : std_logic;
signal tx_clk_r2 : std_logic;
  -- TX clock enable, shift register and bit count
signal do_txbit       : std_logic;
signal tx_sr          : unsigned(9 downto 0); 
signal tx_bcnt        : unsigned(3 downto 0); -- Number of bits
signal tx_almost_done : std_logic;
signal tx_done        : std_logic;
 
begin
 
  -- This process detects the rising edge of tx_clk_i
  tx_clk_edge_proc: Process(sys_rst_n,sys_clk)
  BEGIN
    if (sys_rst_n = '0') then
      tx_clk_r1 <= '0';
      tx_clk_r2 <= '0';
    elsif (sys_clk'event AND sys_clk='1') then
      if (sys_clk_en='1') then
        tx_clk_r1 <= tx_clk_i;
        tx_clk_r2 <= tx_clk_r1;
      end if;
    end if;
  END PROCESS tx_clk_edge_proc;
  do_txbit <= (tx_clk_r1 and not tx_clk_r2); -- rising edge detect
 
  -- This process loads the shift register, then counts as the bits transmit out.
  byte_tx: Process(sys_rst_n,sys_clk)  
  BEGIN
    if (sys_rst_n = '0') then
      tx_sr     <= (others=>'0');
      tx_bcnt   <= (others=>'0');
      tx_stream <= '1';
      tx_done   <= '1';
    elsif (sys_clk'event and sys_clk='1') then
      if (sys_clk_en='1') then
        -- Start a new transmission when ready
        -- Case 1 is starting while do_txbit is high
        if tx_bcnt="0000" and do_txbit='1' and tx_wr_i='1' then
          tx_stream <= '0';                             -- Provide start bit
          tx_sr(7 downto 0) <= tx_dat_i;                -- Load the TX data
          tx_sr(8) <= '1';                              -- Default the parity bit to one
          if(tx_parity_i = "00") then                   --If no parity...
            tx_bcnt  <= "1001";                         -- send start, 8 data bits, and stop
          elsif (tx_parity_i = "01") then               --If even parity...
            tx_bcnt  <= "1010";                         -- send start, 8 data bits, parity, and stop
            tx_sr(8) <= tx_dat_i(0) XOR tx_dat_i(1) XOR tx_dat_i(2) XOR tx_dat_i(3) XOR 
                        tx_dat_i(4) XOR tx_dat_i(5) XOR tx_dat_i(6) XOR tx_dat_i(7);
          else                                          --If odd parity...
            tx_bcnt  <= "1011";                         --send start, 8 data bits, parity, and stop
            tx_sr(8) <= NOT (tx_dat_i(0) XOR tx_dat_i(1) XOR tx_dat_i(2) XOR tx_dat_i(3) XOR 
                             tx_dat_i(4) XOR tx_dat_i(5) XOR tx_dat_i(6) XOR tx_dat_i(7));
          end if;
          tx_done <= '0';
        -- Case 2 is starting while do_txbit is low
        elsif tx_done='1' and tx_wr_i='1' then -- Only allow loads when transmitter is idle
          tx_sr(0) <= '0';                              -- Load start bit
          tx_sr(8 downto 1) <= tx_dat_i;                -- Load the TX data
          tx_sr(9) <= '1';                              -- Default the parity bit to one
          if(tx_parity_i = "00") then                   --If no parity...
            tx_bcnt  <= "1010";                         -- send start, 8 data bits, and stop
          elsif (tx_parity_i = "01") then               --If even parity...
            tx_bcnt  <= "1011";                         -- send start, 8 data bits, parity, and stop
            tx_sr(9) <= tx_dat_i(0) XOR tx_dat_i(1) XOR tx_dat_i(2) XOR tx_dat_i(3) XOR 
                        tx_dat_i(4) XOR tx_dat_i(5) XOR tx_dat_i(6) XOR tx_dat_i(7);
          else                                          --If odd parity...
            tx_bcnt  <= "1011";                         --send start, 8 data bits, parity, and stop
            tx_sr(9) <= NOT (tx_dat_i(0) XOR tx_dat_i(1) XOR tx_dat_i(2) XOR tx_dat_i(3) XOR 
                             tx_dat_i(4) XOR tx_dat_i(5) XOR tx_dat_i(6) XOR tx_dat_i(7));
          end if;
          tx_done <= '0';
        -- Process through the remaining data
        elsif(tx_bcnt>"0000" and do_txbit='1') then     -- Still have bits to send?
          tx_bcnt <= tx_bcnt-1;
          tx_sr(8 downto 0) <= tx_sr(9 downto 1);       -- Right shift the data (send LSB first)
          tx_sr(9) <= '1';
          tx_stream <= tx_sr(0);
        end if;
        -- Assert tx_done when truly finished.
        if tx_almost_done='1' and tx_wr_i='0' then
          tx_done <= '1';
        end if;
      end if; -- sys_clk_en
    end if; -- sys_clk'event...
  END PROCESS byte_tx;
 
  tx_almost_done <= '1' when (tx_done='0' and tx_bcnt="0000" and do_txbit='1') else '0';
  tx_done_o <= '1' when tx_done='1' or tx_almost_done='1' else '0';
 
end beh;
 
 
-------------------------------------------------------------------------------
-- Asynchronous Receiver With Output Buffer
------------------------------------------------------------------------------- 
--
-- Author: John Clayton
-- Date  : Aug  05, 2013 Added this change log header, which was missing.
--                       Added first_edge signal to avoid erroneous initial
--                       baud interval measurement (John Clayton & Philip 
--                       Kasavan)
--         Jan.  2, 2014 Added output buffer, changed idle_prep to include
--                       the actual transition to IDLE state.  Added
--                       POST_RECV state, so that the rx_done_o signal will
--                       reflect the true end of the received character.
--                       This helps in applications where a received
--                       asynchronous input is "echoed back" directly,
--                       as the rx_wr_o signal can be used to switch the
--                       signal at the correct time.
--         Feb.  6, 2014 Added requirement for half_baud to be non-zero
--                       before leaving IDLE state.  This prevents leaving
--                       IDLE due to falling edges prior to the first Baud
--                       interval measurement.
--
-- Description
-------------------------------------------------------------------------------
-- Squarewave tx_clk_i input determines rate.
-- (tx_clk_i does not really need to be a squarewave.  Only the rising edges
--  are measured and used.)
-- 
-- Description:
--   This block receives asynchronous serial bytes.  The Baudrate and parity
-- are determined by inputs, but the number of bits per character is
-- fixed at eight.
--
-- NOTES:
-- The receive input and baudrate clock are passed through two layers of
-- synchronizing flip-flops to mitigate metastability, since those signals can
-- originate outside of the sys_clk clock domain.  All other logic connecting to 
-- input of this function are assumed to be within the same clock domain.
--
-- The receiver looks for a new start bit immediately following the rx_wr_o
-- pulse, but rx_done_o is delayed until the expected end of the received
-- character.  If a new start bit is detected just prior to the expected end
-- of the received character, then rx_done_o is not asserted.
--
-- Receive begins when the falling edge of the start bit is detected.
-- Then after 10 or 11 bit times have passed (depending on the parity setting)
-- the rx_wr_o signal will pulse high for one clock period, indicating rx_dat_o
-- contains valid receive data.  The rx_wr_o pulse is only issued if there is
-- no parity error, and the stop bit is actually detected high at the sampling
-- time.
-- The rx_dat_o outputs will hold the received data until the next rx_wr_o pulse.
-- The rx_dat_o output provides the received data, even in the presence of a
-- parity or stop-bit error.
-- Error flags are valid during rx_wr_o, but they remain latched, until
-- rx_restart_i.
--
-- Although the receiver immediately restarts itself to receive the next
-- character, the rx_restart_i input can clear the error indicators.  The rx_restart_i
-- input is like a synchronous reset in this respect since it will cause a receive
-- operation to abort.
--
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
 
entity async_rx_sqclk is
    port (
 
      sys_rst_n    : in std_logic;
      sys_clk      : in std_logic;
      sys_clk_en   : in std_logic;
 
      -- rate and parity
      rx_parity_i  : in unsigned(1 downto 0); -- 0=none, 1=even, 2 or 3=odd
      rx_clk_i     : in std_logic;
 
      -- serial input
      rx_stream    : in std_logic;
 
      -- control and status
      rx_restart_i : in  std_logic;        -- High clears error flags, synchronously resets receiver
      rx_dat_o     : out unsigned(7 downto 0);
      rx_wr_o      : out std_logic;        -- High pulse means store rx_dat_o.
      rx_done_o    : out std_logic;        -- Indicates receiver is idle
      frame_err_o  : out std_logic;        -- High = error.  Reset when rx_restart_i asserted.
      parity_err_o : out std_logic         -- High = error.  Reset when rx_restart_i asserted.
    );
end async_rx_sqclk;
 
architecture beh of async_rx_sqclk is
 
 
-- RX signals
  -- rx_clk_i synchronizing flip-flops and rising edge detector
signal rx_clk_r1       : std_logic;
signal rx_clk_r2       : std_logic;
  -- RX input synchronizing flip flops
signal rx_stream_r1    : std_logic;
signal rx_stream_r2    : std_logic;
  -- RX signals
    -- RX State Machine
type RX_STATE_TYPE is (IDLE, CHECK_START_1, CHECK_START_2, RECV_DATA, POST_RECV);
signal rx_state        : RX_STATE_TYPE;
signal start_bit_start : std_logic;             -- Signals falling edge of rx_stream_i
signal rx_sr           : unsigned(8 downto 0);  -- Shift register
signal rx_bcnt         : unsigned(3 downto 0);  -- Number of bits left, counts down
signal rx_bcnt_start   : unsigned(3 downto 0);  -- Total number of bits
signal rx_parity_good  : std_logic;
  -- Timers have been sized to hold baud interval for speeds as slow as 9600 bps at 100MHz sys_clk
signal rx_timer        : unsigned(13 downto 0); -- Elapsed sys_clks from last bit time start
signal half_baud       : unsigned(13 downto 0); -- One half of full_baud
signal full_baud       : unsigned(13 downto 0); -- Baud interval, as measured from rx_clk_i
signal baud_timer      : unsigned(13 downto 0); -- Used to measure baud interval
signal bit_sampled     : std_logic;             -- High indicates bit is already sampled, don't allow resampling.
 
signal first_edge      : std_logic;
 
begin
 
  -- Synchronizing flip flops to avoid metastability issues...
  rx_stream_syncproc: Process(sys_rst_n,sys_clk)
  BEGIN
    if (sys_rst_n = '0') then
      rx_stream_r1 <= '1';
      rx_stream_r2 <= '1';
    elsif (sys_clk'event AND sys_clk='1') then
      if (sys_clk_en='1') then
        rx_stream_r2 <= rx_stream_r1;
        rx_stream_r1 <= rx_stream;
      end if;
    end if;
  END PROCESS rx_stream_syncproc;
  start_bit_start <= rx_stream_r2 and not rx_stream_r1;
 
  -- Synchronizing flip flops to avoid metastability issues...
  rx_clk_syncproc: Process(sys_rst_n,sys_clk)
  BEGIN
    if (sys_rst_n = '0') then
      rx_clk_r1 <= '0';
      rx_clk_r2 <= '0';
    elsif (sys_clk'event AND sys_clk='1') then
      if (sys_clk_en='1') then
        rx_clk_r2 <= rx_clk_r1;
        rx_clk_r1 <= rx_clk_i;
      end if;
    end if;
  END PROCESS rx_clk_syncproc;
 
  -- This is the baud interval measuring process.
  -- Measurements are only made between rising edges
  baud_measure_proc: Process(sys_rst_n,sys_clk)
  BEGIN
    if (sys_rst_n = '0') then
      full_baud  <= (others=>'0');
      baud_timer <= (others=>'0');
      first_edge <= '0';
    elsif (sys_clk'event AND sys_clk='1') then
      if (sys_clk_en='1') then
        if(first_edge = '1')then 
          if (rx_clk_r1='1' and rx_clk_r2='0') then
            full_baud  <= baud_timer;
            baud_timer <= (others=>'0');
          else
            baud_timer <= baud_timer+1;
          end if;
        elsif(rx_clk_r1='1' and rx_clk_r2='0') then
          first_edge <= '1';
        end if;
      end if;
    end if;
  END PROCESS baud_measure_proc;
 
 
  -- This process handles the incoming bits
  uart_rx_bits: Process(sys_rst_n,sys_clk)
 
  procedure idle_prep is
  begin
    rx_done_o   <= '1';
    bit_sampled <= '0';
    rx_bcnt     <= (others=>'0');
    rx_timer    <= (others=>'0');
    rx_state    <= IDLE;
  end idle_prep;
 
  begin
    if (sys_rst_n = '0') then
      idle_prep;
      rx_sr        <= (others=>'0');
      frame_err_o  <= '0';
      parity_err_o <= '0';
      rx_wr_o      <= '0';
      rx_dat_o     <= (others=>'0');
    elsif (sys_clk'event AND sys_clk='1') then
      if (sys_clk_en='1') then
        -- Default values
        rx_wr_o      <= '0';           -- Default to no data write
        -- Handle incrementing the sample timer
        rx_timer<=rx_timer+1;
        -- State transitions
        case rx_state is
 
          when IDLE         =>
            rx_done_o   <= '1';           -- Indicate receive is done.
            bit_sampled <= '0';           -- Indicate bit is not yet sampled.
            if (rx_restart_i='1') then
              idle_prep;
              frame_err_o  <= '0';        -- At rx_restart, also clear error flags
              parity_err_o <= '0';
            elsif (half_baud/=0 and start_bit_start='1') then
              rx_timer  <= (others=>'0'); -- Reset timer back to zero
              rx_bcnt   <= rx_bcnt_start; -- Initialize bit counter
              rx_done_o <= '0';
              rx_state  <= CHECK_START_1;
            end if;
 
          when CHECK_START_1 =>
            if (rx_restart_i='1') then    -- Restart has very high priority
              idle_prep;
            elsif (rx_stream_r2='1') then -- High during this time is an error
              frame_err_o <= '1';
              idle_prep;
            elsif (rx_timer>=half_baud) then -- Must use >= since threshold may change downward
              rx_state  <= CHECK_START_2;
            end if;
 
          when CHECK_START_2 =>            -- During second half of start bit, don't verify low level
            if (rx_restart_i='1') then     -- Restart has very high priority
              idle_prep;
            elsif (rx_timer>=full_baud or rx_stream_r2='1') then -- Wait for end of start bit
              rx_timer <= (others=>'0'); -- Reset timer back to zero
              rx_state <= RECV_DATA;
            end if;
 
          when RECV_DATA     =>
            if (rx_restart_i='1') then     -- Restart has very high priority
              idle_prep;
            elsif (rx_timer>=full_baud) then -- Must use >= since threshold may change downward
              rx_timer <= (others=>'0'); -- Reset timer back to zero
              bit_sampled <= '0';
            elsif (rx_timer>=half_baud and bit_sampled='0') then -- Must use >= since threshold may change downward
              bit_sampled <= '1';
              if (rx_bcnt="0000") then
                rx_state <= POST_RECV;
                rx_dat_o <= rx_sr(7 downto 0);
                if (rx_parity_good='1' and rx_stream_r2='1') then
                  rx_wr_o <= '1';            -- If all is correct, create a one clock long pulse to store rx_dat_o.
                else
                  if (rx_stream_r2='0') then
                    frame_err_o <= '1';        -- Record error if there is a bad stop bit
                  end if;
                  if (rx_parity_good='0') then
                    parity_err_o <= '1';       -- Record error if there is bad parity
                  end if;
                end if;
              else -- Process a new bit
                rx_sr(7 downto 0) <= rx_sr(8 downto 1);
                if (rx_parity_i = "00") then
                  rx_sr(7) <= rx_stream_r2;  -- Store the new incoming bit
                else
                  rx_sr(8) <= rx_stream_r2;
                end if;
                rx_bcnt <= rx_bcnt-1;
              end if;
            end if;
 
          when POST_RECV => -- Wait out latter half of stop bit, checking for start bits...
            if (rx_restart_i='1') then
              bit_sampled  <= '0';
              frame_err_o  <= '0'; -- At rx_restart, also clear error flags
              parity_err_o <= '0';
              idle_prep;
            elsif (start_bit_start='1') then
              bit_sampled <= '0';
              rx_timer  <= (others=>'0'); -- Reset timer back to zero
              rx_bcnt   <= rx_bcnt_start; -- Initialize bit counter
              rx_done_o <= '0';
              rx_state  <= CHECK_START_1;
            elsif (rx_timer>=full_baud) then -- Wait for end of start bit
              bit_sampled <= '0'; -- Indicate bit is not yet sampled.
              idle_prep; -- Asserts rx_done_o to indicate completion
            end if;
 
          when others => null;
 
        end case;
      end if;
    end if;
  end process uart_rx_bits;
 
  -------------------------
  -- Assign number of bits to shift in.
  rx_bcnt_start <= "1000" when (rx_parity_i="00") else "1001";
 
  -------------------------
  -- Assign half baud period
  half_baud <= ('0' & full_baud(13 downto 1));
 
  -------------------------
  -- Parity check process
  rx_parity_check: process(rx_sr, rx_parity_i)
  begin
    if (rx_parity_i="00") then       -- No parity...
      rx_parity_good <= '1'; -- (always good.)
    elsif (rx_parity_i="01") then    -- Even parity...
      rx_parity_good <= not (rx_sr(0) XOR rx_sr(1) XOR rx_sr(2) XOR rx_sr(3) XOR rx_sr(4) 
                                      XOR rx_sr(5) XOR rx_sr(6) XOR rx_sr(7) XOR rx_sr(8));
    else                     -- Odd parity...
      rx_parity_good <=     (rx_sr(0) XOR rx_sr(1) XOR rx_sr(2) XOR rx_sr(3) XOR rx_sr(4) 
                                      XOR rx_sr(5) XOR rx_sr(6) XOR rx_sr(7) XOR rx_sr(8));
    end if;
  end process;
 
end beh;
 
 
-------------------------------------------------------------------------------
-- Half Duplex Switch -- automatic priority to TX, with "hang timer" and delay
------------------------------------------------------------------------------- 
--
-- Author: John Clayton
-- Date  : Oct. 09, 2015 Initial creation.
--         Oct. 20, 2015 Updated to include delay, thereby allowing for
--                       switching from TX to RX at the very end of the
--                       TX data, at the expense of waiting additional
--                       time for the TX data to emerge.
--
-- Description
-------------------------------------------------------------------------------
--
-- This unit implements a simple switch for routing both TX and RX signals
-- over a single wire, called "half duplex" communication.  This is needed
-- for 2 wire RS-232 or 3 wire RS-485/RS-422 communications.
--
-- The switch immediately goes into transmit mode when the non-idle state
-- is detected on the tx_i input.  After HANG_TIME rising edges of the
-- tick_i input, with the idle state continuously present on the tx_i input,
-- the switch reverts to the receive mode.  The tick_i may be connected
-- to the Baudrate clock, or any other desired clock, but it must not
-- be tied to a constant value, since the edges are needed to advance
-- the timer.
--
-- It is recommended to set HANG_TIME to exceed the time needed for one
-- character to be transmitted, since that will prevent mid-character
-- timeouts.
--
-- An additional parameter, DELAY, allows for the transmit data to be
-- delayed by DELAY rising edges of the tick_i input.  Note that this
-- delays the TX data after the activity check, so that the moment when
-- the "hang" timer expires can effectively be lined up with the end of
-- the final TX data byte.  Take care!  If DELAY is set greater than
-- HANG_TIME, then some of the TX data can be effectively curtailed.
--
-- Note that the delay shift register effectively samples tx_i data
-- on the detected rising edges of tick_i.  Thus, tick_i rising edges
-- should be a synchronized 1x Baud rate clock, or else an unsynchronized
-- clock at more than 2x the Baud rate, to satisfy Nyquist criteria. By
-- setting DELAY=0, this constraint is removed, since the shift register
-- is not used for zero DELAY, and no re-sampling of tx_i actually occurs.
--
-- The hd_drive_o signal is provided so that tristates may be kept at the top
-- level of the system.
--
-- No synchronization flip flops are implemented within this switch
-- module.  If needed, they are assumed to be included elsewhere.
--
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
 
library work;
use work.convert_pack.all;
 
entity half_duplex_switch is
    generic (
      HANG_TIME : natural := 12; -- Units of timer_i rising edges
      TX_DELAY  : natural := 12  -- Units of timer_i rising edges
    );
    port (
 
      sys_rst_n  : in  std_logic;
      sys_clk    : in  std_logic;
      sys_clk_en : in  std_logic;
 
      -- Full Duplex side
      tx_i       : in  std_logic;
      rx_o       : out std_logic;
 
      -- Half Duplex side
      hd_o       : out std_logic;
      hd_i       : in  std_logic;
      hd_drive_o : out std_logic;
 
      -- Timer
      tick_i     : in  std_logic
    );
end half_duplex_switch;
 
architecture beh of half_duplex_switch is
 
-- TX signals
signal timer    : unsigned(timer_width(HANG_TIME)-1 downto 0);
signal delay_sr : unsigned(15 downto 0);
signal tick_r1  : std_logic;
signal tx_data  : std_logic;
 
begin
 
  main_proc: Process(sys_rst_n,sys_clk)
  begin
    if (sys_rst_n = '0') then
      tick_r1  <= '0';
      timer    <= (others=>'0');
      delay_sr <= (others=>'1');
    elsif (sys_clk'event AND sys_clk='1') then
      if (sys_clk_en='1') then
        tick_r1 <= tick_i;
        -- Handle the delay shift register
        if (tick_r1='0' and tick_i='1') then
          delay_sr <= delay_sr(14 downto 0) & tx_i;
        end if;
        -- Handle the hang timer
        if (tx_i='0') then
          timer <= to_unsigned(HANG_TIME,timer'length);
        elsif (tick_r1='0' and tick_i='1') then
          if (timer>0) then
            timer <= timer-1;
          end if;
        end if;
      end if;
    end if;
  end process main_proc;
 
  tx_data <= tx_i when (TX_DELAY=0) else delay_sr(TX_DELAY-1);
 
  rx_o <= '1' when (timer>0) else hd_i;
  hd_o <= tx_data when (timer>0) else '1';
  hd_drive_o <= '1' when (timer>0) else '0';
 
end beh;
 
 
 

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.