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

Subversion Repositories sd_mmc_emulator

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

Compare with Previous | Blame | View Log

--------------------------------------------------------------------------
-- Package of auto_baud components
--
-- Contains the following components:
--
--   auto_baud_with_tracking
--
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
 
package auto_baud_pack is
 
  component auto_baud_with_tracking
    generic (
      CLOCK_FACTOR    : natural; -- Output is this factor times the baud rate
      FPGA_CLKRATE    : real;    -- FPGA system clock rate
      MIN_BAUDRATE    : real;    -- Minimum expected incoming Baud rate
      DELTA_THRESHOLD : integer  -- Max. number of sys_clks difference allowed between
                                 -- "half_measurement" and "measurement"
    );
    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=odd
 
      -- serial input
      rx_stream_i  : in  std_logic;
 
      -- Output
      baud_lock_o  : out std_logic;
      baud_clk_o   : out std_logic
    );
  end component;
 
end auto_baud_pack;
 
package body auto_baud_pack is
end auto_baud_pack;
 
-------------------------------------------------------------------------------
-- Auto Baud with tracking core
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Date  : Aug. 20, 2002 Began project
-- Update: Sep.  5, 2002 copied this file from "autobaud.v"
--                       Added tracking functions, and debugged them.
-- Update: Sep. 13, 2002 Added test data.  Module complete, it works well.
-- Update: Nov. 20, 2009 Began translation into VHDL
-- Update: Mar. 22, 2012 After much successful use of this core in VHDL,
--                       decided to add sys_clk_en input.
-- Update: July  2, 2012 I was astounded to find a "false positive" reading
--                       from this module during a simulation, and added the
--                       "edge count" counter to help eliminate such cases,
--                       in which two zero bits can get interpreted as a
--                       single start bit, and somehow, amazingly, the rest
--                       of the bits following this also match the expected
--                       values.  In these cases, the edge count will not
--                       match, and the measurement will be discarded.
-- Update: Dec. 13, 2012 Changed SYS_CLK_RATE and MIN_BAUD_RATE from integer
--                       to real values.
-- Update: Jul. 29, 2015 Added second synchronization flip flop to mitigate
--                       metastability issues with rx_stream_i.
--
-- Description
-------------------------------------------------------------------------------
-- This is a state-machine driven core that measures transition intervals
-- in a particular character arriving via rs232 transmission (i.e. PC serial 
-- port.)  Measurements of time intervals between transitions in the received
-- character are then used to generate a baud rate clock for use in serial
-- communications back and forth with the device that originally transmitted
-- the measured character.  The clock which is generated is in reality a
-- clock enable pulse, one single clock wide, occurring at a rate suitable
-- for use in serial communications.  (This means that it will be generated
-- at 4x or 8x or 16x the actual measured baud rate of the received character.
-- The multiplication factor is called "CLOCK_FACTOR" and is a settable
-- parameter within this module.  The parameter "CLOCK_FACTOR" need not
-- be a power of two, but it should be a number between 2 and 16 inclusive.)
--
-- The particular character which is targeted for measurement and verification
-- in this module is: carriage return (CR) = 0x0d = 13.
-- This particular character was chosen because it is frequently used at the
-- end of a command line, as entered at a keyboard by a human user interacting
-- with a command interpreter.  It is anticipated that the user would press
-- the "enter" key once upon initializing communications with the electronic
-- device, and the resulting carriage return character would be used for 
-- determining BAUD rate, thus allowing the device to respond at the correct
-- rate, and to carry on further communications.  The electronic device using
-- this "auto_baud" module adjusts its baud rate to match the baud rate of
-- the received data.  This works for all baud rates, within certain limits,
-- and for all system clock rates, within certain limits.
--
-- Received serially, and with no parity bit, the carriage return appears as
-- the following waveform:
-- ________    __    ____          _______________
--         |__|d0|__|d2d3|________|stop
--        start   d1      d4d5d6d7
--
-- The waveform is shown with an identical "high" time and "low" time for
-- each bit.  However, actual measurements taken using a logic analyzer
-- on characters received from a PC show that the times are not equal.
-- The "high" times turned out shorter, and the "low" times longer...
-- Therefore, this module attempts to average out this discrepancy by
-- measuring one low time and one high time.
--
-- Since the transition measurements must unavoidably contain small amounts
-- of error, the measurements are made during the beginning 2 bits of
-- the received character, (that is, start bit and data bit zero).
-- Then the measurement is immediately transformed into a baud rate clock,
-- used to verify correct reception of the remaining 8 bits of the character.
-- If the entire character is not received correctly using the generated
-- baud rate, then the measurement is scrapped, and the unit goes into an
-- idle scanning mode waiting for another character to test.
--
-- This effectively filters out characters that the unit is not interested in
-- receiving (anything that is not a carriage return.)  There is a slight
-- possibility that a group of other characters could appear by random
-- chance in a configuration that resembles a carriage return closely enough
-- that the unit might accept the measurement and produce a baud clock too
-- low.  But the probability of this happening is remote enough that the
-- unit is considered highly "robust" in normal use, especially when used
-- for command entry by humans.  It would take a very clever user indeed, to
-- enter the correct series of characters with the correct intercharacter
-- timing needed to possibly "fool" the unit!
--
-- (Also, the baud rate produced falls within certain limits imposed by
--  the hardware of the unit, which prevents the auto_baud unit from mistaking
--  a series of short glitches on the serial data line for a really
--  fast CR character.)
--
-- This method of operation implies that each carriage return character
-- received will produce a correctly generated baud rate clock.  Therefore,
-- each and every carriage return actually causes a new baud rate clock to
-- be produced.  However, updates occur smoothly, and there should be no
-- apparent effect as an old BAUD clock is stopped and a new one started.
-- The transition is done smoothly.
--
-- For users who desire a single measurement at the beginning of a session
-- to produce a steady baud clock during the entire session, there is a
-- slightly smaller module called "auto_baud.v" which performs a single
-- measurement, but which requires a reset pulse in order to begin measuring
-- for a new BAUD rate.
--
-- NOTES:
-- - This module uses a counter to divide down the sys_clk signal to produce the
--   baud_clk_o signal.  Since the frequency of baud_clk_o is nominally
--   CLOCK_FACTOR * rx_baud_rate, where "rx_baud_rate" is the baud rate
--   of the received character, then the higher you make CLOCK_FACTOR, the
--   higher the generated baud_clk_o signal frequency, and hence the lower the
--   resolution of the divider.  Therefore, using a lower value for the
--   CLOCK_FACTOR will allow you to use a lower sys_clk with this module.
-- - The lower the minimum baud rate setting, the larger the counters will be.
--   This is so that the counters can accomodate the large count values needed
--   to divide the system clock into the low Baud rate output.
--
-- - If the percentage error for your highest desired baud rate is greater
--   than a few percent, you might want to use a higher Fsys_clk or else a 
--   lower CLOCK_FACTOR.
--
-- Note: Simply changing the template bits does not reconfigure the
--       module to look for a different character (because a new measurement
--       window might have to be defined for a different character...)
--       The template bits are the exact bits used during verify, against
--       which the incoming character is checked.
--       The LSB of the character is not used for verification, since it is
--       actually used in the measurement.
--
 
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
library work;
use work.convert_pack.all;
 
entity auto_baud_with_tracking is
    generic (
      CLOCK_FACTOR    : natural := 16;        -- Output is this factor times the baud rate
      FPGA_CLKRATE    : real := 25000000.0;   -- FPGA system clock rate
      MIN_BAUDRATE    : real := 57600.0;      -- Minimum expected incoming Baud rate
      DELTA_THRESHOLD : integer := 6          -- Max. number of sys_clks difference allowed between
                                              -- "half_measurement" and "measurement"
    );
    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=odd
 
      -- serial input
      rx_stream_i  : in  std_logic;
 
      -- Output
      baud_lock_o  : out std_logic;
      baud_clk_o   : out std_logic
    );
end auto_baud_with_tracking;
 
architecture beh of auto_baud_with_tracking is
 
-- Constants
constant TEMPLATE_BITS    : unsigned(7 downto 0) := "00001101";  -- Carriage return
constant MAIN_COUNT_WIDTH : integer := timer_width(FPGA_CLKRATE/MIN_BAUDRATE); -- Bit Width of timer.
 
-- Internal signal declarations
  -- State Machine
type FSM_STATE_TYPE is (IDLE, MEASURE_START_BIT, MEASURE_DATA_BIT, VERIFY);
signal fsm_state        : FSM_STATE_TYPE;
 
signal baud_lock        : std_logic; -- When high, indicates output can operate.
signal char_mismatch    : std_logic; -- Indicates character did not verify
signal baud_count       : unsigned(MAIN_COUNT_WIDTH-1 downto 0); -- BAUD counter register
signal baud_div         : unsigned(MAIN_COUNT_WIDTH-1 downto 0); -- measurement for running
signal measurement      : unsigned(MAIN_COUNT_WIDTH-1 downto 0); -- measurement for verify
signal half_check       : unsigned(MAIN_COUNT_WIDTH-1 downto 0); -- Half of measurement
signal half_measurement : unsigned(MAIN_COUNT_WIDTH-1 downto 0); -- measurement at end of start bit
signal delta            : unsigned(MAIN_COUNT_WIDTH-1 downto 0); -- Difference value
signal target_bits      : unsigned(8 downto 0); -- Character bits to compare
signal target_bit_count : unsigned(3 downto 0); -- Contains count of bits remaining to check
signal parity           : std_logic; -- The "template bit" for checking the received parity bit.
signal edge_count       : unsigned(3 downto 0);
signal edge             : std_logic;
signal rx_stream_r1     : std_logic;
signal rx_stream_r2     : std_logic;
 
----------------------------------------------------------------------------
-- Functions
----------------------------------------------------------------------------
  function gen_even_parity(in_a : unsigned) return std_logic is
  variable i : natural;
  variable o : std_logic;
 
  begin
 
  o := '0'; -- Default value
  for i in 0 to in_a'length-1 loop
    o := o xor in_a(i);
  end loop;
 
  return(o);
 
  end;
 
-----------------------------------------------------------------------------
begin
 
parity <= '1' when (rx_parity_i="00") else
          gen_even_parity(TEMPLATE_BITS) when (rx_parity_i="01") else
          not gen_even_parity(TEMPLATE_BITS) when (rx_parity_i="10") else
          '1';
-- Form a difference between the measurement and twice the "half-measurement"
-- Take the absolute value.
half_check <= '0' & measurement(MAIN_COUNT_WIDTH-1 downto 1);
delta <= (half_check-half_measurement) when (half_check>half_measurement) else
         (half_measurement-half_check);
 
-- This is state machine.  It checks the status of the rx_stream_i line
-- and coordinates the measurement of the time interval of the first two
-- bits of the received character, which is the "measurement interval."
-- Following the measurement interval, the state machine enters a new
-- phase of bit verification.  If the measured time interval is accurate
-- enough to measure the remaining 8 or 9 bits of the character correctly, then
-- the measurement is accepted, and the baud rate clock is driven onto
-- the baud_clk_o output pin.  Incidentally, the process of verification
-- effectively filters out characters which are not the desired target
-- character for measurement.  In this case, the target character is the
-- carriage return.
 
fsm_proc: process(sys_clk, sys_rst_n, parity)
begin
  if (sys_rst_n='0') then
    fsm_state        <= IDLE;   -- asynchronous reset
    baud_clk_o       <= '0';
    baud_lock        <= '0';
    char_mismatch    <= '0';
    baud_count       <= (others=>'0');
    half_measurement <= (others=>'0'); -- The measurement at the end of the start bit
    measurement      <= (others=>'0'); -- The candidate divider value.
    baud_div         <= (others=>'0'); -- The "running" copy of measurement
    target_bits      <= '1' & parity & TEMPLATE_BITS(7 downto 1);
    target_bit_count <= (others=>'0');
    edge_count       <= (others=>'0');
    rx_stream_r1     <= '1';
    rx_stream_r2     <= '1';
  elsif (sys_clk'event and sys_clk='1') then
    if (sys_clk_en='1') then
 
      -- Store previous stream sample, for edge detection
      rx_stream_r1 <= rx_stream_i;
      rx_stream_r2 <= rx_stream_r1;
 
      -- Handle the baud clock output generation, if we have achieved "baud_lock"
      if (baud_lock='1') then
        if (baud_count + 2*CLOCK_FACTOR > baud_div) then
          baud_count <= to_unsigned(CLOCK_FACTOR,baud_count'length); 
            -- baud_count <= baud_count + to_unsigned(CLOCK_FACTOR,baud_count'length) - baud_div; 
            -- (Compromised above for simplicity)
            -- (It could have been baud_count+CLOCK_FACTOR-baud_div)
          baud_clk_o <= '1';
        else
          baud_count   <= baud_count + 2*CLOCK_FACTOR;
          baud_clk_o   <= '0';
        end if;
      end if;
 
      case (fsm_state) is
 
        when IDLE =>
          char_mismatch    <= '0';
          measurement      <= (others=>'0');
          half_measurement <= (others=>'0');
          target_bits      <= '1' & parity & TEMPLATE_BITS(7 downto 1);
          if (rx_parity_i=0) then
            target_bit_count <= to_unsigned(8,target_bit_count'length);
          else
            target_bit_count <= to_unsigned(9,target_bit_count'length); -- ODD/EVEN parity means extra bit.
          end if;
          if (rx_stream_r1 = '0') then
            fsm_state <= MEASURE_START_BIT;
          end if;
 
        when MEASURE_START_BIT =>
          if (rx_stream_r1 = '1') then
            half_measurement <= measurement;
            fsm_state        <= MEASURE_DATA_BIT;
          else
            measurement <= measurement+1;
          end if;
 
        when MEASURE_DATA_BIT =>
          -- The duration of the data bit must not be significantly different
          -- than the duration of the start bit...
          measurement <= measurement+1;
          edge_count  <= (others=>'0');
          if (rx_stream_r1='0') then  -- Look for the end of the least significant data bit.
            if (delta>DELTA_THRESHOLD) then
              fsm_state <= IDLE;
            else
              fsm_state <= VERIFY;            
            end if;
          end if;
 
        when VERIFY =>  -- Wait for verify operations to finish
          if (edge='1') then
            edge_count <= edge_count+1;
          end if;
          if (target_bit_count=0) then -- At the stop bit, check the status
            if (char_mismatch='0' and edge_count=3) then
              baud_div  <= measurement; -- Store final verified measurement for running
              baud_lock <= '1';
            end if;
            if (rx_stream_r1='1') then -- Only return when there is a chance of making a valid new measurement...
              fsm_state <= IDLE; -- Whether successful, or not, return to IDLE ("autotracking" feature.)
            end if;
          else
            if (half_measurement>=measurement) then -- Initial setting leads to near mid-bit sampling.
              half_measurement <= (others=>'0');
              target_bits <= '0' & target_bits(target_bits'length-1 downto 1);
              target_bit_count <= target_bit_count-1;
              if (target_bits(0)/=rx_stream_r1) then
                char_mismatch <= '1';
              end if;
            else
              half_measurement <= half_measurement+2;
            end if;
          end if;
 
        --when others => 
        --  fsm_state <= IDLE;
      end case;
 
    end if; -- sys_clk_en
  end if; -- sys_clk
end process;
 
edge <= rx_stream_r2 xor rx_stream_r1;
 
baud_lock_o <= baud_lock;
 
 
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.