URL
https://opencores.org/ocsvn/auto_baud/auto_baud/trunk
Subversion Repositories auto_baud
[/] [auto_baud/] [trunk/] [auto_baud_pack.vhd] - Rev 7
Compare with Previous | Blame | View Log
-------------------------------------------------------------------------- -- Package of auto_baud components -- -- Contains the following components: -- -- auto_baud_with_tracking -- auto_baud_with_tracking_slv -- -- the version with the suffix _slv attached to the name provides -- IO busses of the "std_logic_vector" type instead of "unsigned." -- In all other ways, these components are identical. 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 : integer; -- FPGA system clock rate MIN_BAUDRATE : integer; -- 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; -- 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; component auto_baud_with_tracking_slv generic ( CLOCK_FACTOR : natural; -- Output is this factor times the baud rate FPGA_CLKRATE : integer; -- FPGA system clock rate MIN_BAUDRATE : integer; -- 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; -- rate and parity rx_parity_i : in std_logic_vector(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 -- -- 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; entity auto_baud_with_tracking is generic ( CLOCK_FACTOR : natural := 16; -- Output is this factor times the baud rate FPGA_CLKRATE : integer := 25000000; -- FPGA system clock rate MIN_BAUDRATE : integer := 57600; -- 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; -- 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 := integer(ceil(log2(real(FPGA_CLKRATE)/real(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. ---------------------------------------------------------------------------- -- 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'); elsif (sys_clk'event and sys_clk='1') then -- 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_i = '0') then fsm_state <= MEASURE_START_BIT; end if; when MEASURE_START_BIT => if (rx_stream_i = '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; if (rx_stream_i='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 (target_bit_count=0) then -- At the stop bit, check the status if (char_mismatch='0') then baud_div <= measurement; -- Store final verified measurement for running baud_lock <= '1'; end if; if (rx_stream_i='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_i) 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; end process; baud_lock_o <= baud_lock; end beh; -------------------------------------------------------------------- -- ...A wrapper to allow instantiating this module with -- busses of type "std_logic_vector" for those who need that. -- -------------------------------------------------------------------- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; library work; use work.auto_baud_pack.all; entity auto_baud_with_tracking_slv is generic ( CLOCK_FACTOR : natural := 16; -- Output is this factor times the baud rate FPGA_CLKRATE : integer := 25000000; -- FPGA system clock rate MIN_BAUDRATE : integer := 57600; -- 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; -- rate and parity rx_parity_i : in std_logic_vector(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_slv; architecture beh of auto_baud_with_tracking_slv is -- Internal signal declarations signal rx_parity_u : unsigned(1 downto 0); -- The "template bit" for checking the received parity bit. ----------------------------------------------------------------------------- begin -- This module generates a serial BAUD clock automatically. -- The unit synchronizes on the carriage return character, so the user -- only needs to press the "enter" key for serial communications to start -- working, no matter what BAUD rate and clk_i frequency are used! auto_baud1 : auto_baud_with_tracking generic map( CLOCK_FACTOR => CLOCK_FACTOR, -- Output is this factor times the baud rate FPGA_CLKRATE => FPGA_CLKRATE, -- FPGA system clock rate MIN_BAUDRATE => MIN_BAUDRATE, -- Minimum expected incoming Baud rate DELTA_THRESHOLD => DELTA_THRESHOLD -- Max. number of sys_clks difference allowed between -- "half_measurement" and "measurement" ) port map( sys_rst_n => sys_rst_n, sys_clk => sys_clk, -- rate and parity rx_parity_i => rx_parity_u, -- 0=none, 1=even, 2=odd -- serial input rx_stream_i => rx_stream_i, -- Output baud_lock_o => baud_lock_o, baud_clk_o => baud_clk_o ); rx_parity_u <= unsigned(rx_parity_i); end beh;