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

Subversion Repositories smbus_controller

[/] [smbus_controller/] [trunk/] [hw/] [sources/] [SMBusAnalyzer.vhd] - Rev 2

Compare with Previous | Blame | View Log

------------------------------------------------------------------------
-- Engineer:    Dalmasso Loic
-- Create Date: 30/10/2024
-- Module Name: SMBusAnalyzer
-- Description:
--      SMBus Analyzer in charge to detect:
--          - Bus Busy Detection
--          - Bus Inactivity
--          - Bus Timeout
--          - Bus Arbitration
--          - Clock Stretching
--
-- WARNING: /!\ Require Pull-Up on SMBCLK and SMBDAT pins /!\
--
-- Generics
--		INPUT_CLOCK_FREQ: Module Input Clock Frequency
--		SMBUS_CLASS: SMBus Class (100kHz, 400kHz, 1MHz)
--
-- Ports
--		Input 	-	i_clock: Module Input Clock
--		Input 	-	i_reset: Module Reset ('0': No Reset, '1': Reset)
--		Input 	-	i_smbclk_controller: SMBus Serial Clock from Controller
--		Input 	-	i_smbclk_line: SMBus Serial Clock bus line
--		Input 	-	i_smbdat_controller: SMBus Serial Data from Controller
--		Input 	-	i_smbdat_line: SMBus Serial Data bus line
--		Output 	-	o_smbus_busy: SMBus Busy detection ('0': Not Busy, '1': Busy)
--		Output 	-	o_smbus_timeout: SMBus Timeout detection ('0': No Timeout, '1': Timeout)
--		Output 	-	o_smbus_arbitration: SMBus Arbitration detection ('0': Lost Arbitration, '1': Win Arbitration)
--		Output 	-	o_smbclk_stretching: SMBus Clock Stretching detection ('0': Not Stretching, '1': Stretching)
------------------------------------------------------------------------
 
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;
USE IEEE.MATH_REAL."ceil";
 
ENTITY SMBusAnalyzer is
 
GENERIC(
    INPUT_CLOCK_FREQ: INTEGER := 12_000_000;
	SMBUS_CLASS: INTEGER := 100_000
);
 
PORT(
	i_clock: IN STD_LOGIC;
	i_reset: IN STD_LOGIC;
    i_smbclk_controller: IN STD_LOGIC;
	i_smbclk_line: IN STD_LOGIC;
	i_smbdat_controller: IN STD_LOGIC;
    i_smbdat_line: IN STD_LOGIC;
    o_smbus_busy: OUT STD_LOGIC;
    o_smbus_timeout: OUT STD_LOGIC;
    o_smbus_arbitration: OUT STD_LOGIC;
	o_smbclk_stretching: OUT STD_LOGIC
);
 
END SMBusAnalyzer;
 
ARCHITECTURE Behavioral of SMBusAnalyzer is
 
------------------------------------------------------------------------
-- Constant Declarations
------------------------------------------------------------------------
-- SMBus Classes (100kHz, 400kHz, 1MHz)
constant SMBUS_100K_CLASS: INTEGER := 100_000;
constant SMBUS_400K_CLASS: INTEGER := 400_000;
constant SMBUS_1M_CLASS: INTEGER := 1_000_000;
 
-- SMBus Minimum Free Time (only after Stop condition)
-- SMBus 100kHz Class: 4.7 µs
-- SMBus 400kHz Class: 1.3 µs
-- SMBus 1MHz Class: 0.5 µs
constant SMBUS_100K_MIN_FREE_TIME_NS: INTEGER := 4700;
constant SMBUS_400K_MIN_FREE_TIME_NS: INTEGER := 1300;
constant SMBUS_1M_MIN_FREE_TIME_NS: INTEGER := 500;
 
-- SMBus Minimum Free Time Cycles ('/1_000_000_000' -> SMBus Minimum Free Time in ns)
constant SMBUS_100K_MIN_FREE_TIME_CYCLES: INTEGER := INTEGER(CEIL(REAL(SMBUS_100K_MIN_FREE_TIME_NS * INPUT_CLOCK_FREQ) / REAL(1_000_000_000)));
constant SMBUS_400K_MIN_FREE_TIME_CYCLES: INTEGER := INTEGER(CEIL(REAL(SMBUS_400K_MIN_FREE_TIME_NS * INPUT_CLOCK_FREQ) / REAL(1_000_000_000)));
constant SMBUS_1M_MIN_FREE_TIME_CYCLES: INTEGER := INTEGER(CEIL(REAL(SMBUS_1M_MIN_FREE_TIME_NS * INPUT_CLOCK_FREQ) / REAL(1_000_000_000)));
 
-- SMBus Inactivity (50 µs)
constant SMBUS_INACTIVITY_US: INTEGER := 50;
 
-- SMBus Inactivity Cycles ('/1_000_000' -> SMBus Inactivity in µs)
constant SMBUS_INACTIVITY_CYCLES: INTEGER := INTEGER(CEIL(REAL(SMBUS_INACTIVITY_US * INPUT_CLOCK_FREQ) / REAL(1_000_000)));
 
-- SMBus Timeout (35 ms)
constant SMBUS_TIMEOUT_MS: INTEGER := 35;
 
-- SMBus Timeout Cycles ('/1_000' -> SMBus Timeout in ms)
constant SMBUS_TIMEOUT_CYCLES: INTEGER := INTEGER(CEIL(REAL(SMBUS_TIMEOUT_MS * INPUT_CLOCK_FREQ) / REAL(1_000)));
 
------------------------------------------------------------------------
-- Signal Declarations
------------------------------------------------------------------------
-- SMBus SMBCLK / SMBDAT Line Levels
signal smbclk_line_level: STD_LOGIC := '0';
signal smbdat_line_level: STD_LOGIC := '0';
signal smbdat_line_level_reg: STD_LOGIC := '0';
 
-- SMBus Start & Stop Conditions
signal smbus_start_cond: STD_LOGIC := '0';
signal smbus_stop_cond: STD_LOGIC := '0';
 
-- SMBus Busy & Timing Counter
signal smbus_busy: STD_LOGIC := '0';
signal busy_timing_counter: INTEGER range 0 to SMBUS_INACTIVITY_CYCLES := 0;
signal busy_timing_counter_end: STD_LOGIC := '0';
 
-- SMBus Timeout
signal timeout_counter: INTEGER range 0 to SMBUS_TIMEOUT_CYCLES := 0;
 
-- SMBus Arbitration
signal smbus_arbitration: STD_LOGIC := '0';
 
-- SMBus Clock Stretching
signal smbclk_stretching: STD_LOGIC := '0';
 
------------------------------------------------------------------------
-- Module Implementation
------------------------------------------------------------------------
begin
	---------------------------------------
	-- SMBus SMBCLK Line Level Converter --
	---------------------------------------
	-- Convert 'Z' into '1' level
	smbclk_line_level <= '0' when i_smbclk_line = '0' else '1';
 
	---------------------------------------
	-- SMBus SMBDAT Line Level Converter --
	---------------------------------------
	-- Convert 'Z' into '1' level
	smbdat_line_level <= '0' when i_smbdat_line = '0' else '1';
 
	-- SMBDAT Line Level Register
	process(i_clock)
	begin
		if rising_edge(i_clock) then
			smbdat_line_level_reg <= smbdat_line_level;
		end if;
	end process;
 
	-------------------------------------
	-- SMBus Start Condition Detection --
	-------------------------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Start Condition (SMBCLK High while SDA High to Low)
			if (smbclk_line_level = '1') and (smbdat_line_level_reg = '1') and (smbdat_line_level = '0') then
				smbus_start_cond <= '1';
			else
				smbus_start_cond <= '0';
			end if;
		end if;
	end process;
 
	------------------------------------
	-- SMBus Stop Condition Detection --
	------------------------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Stop Condition Detection while SMBus Busy
			if (smbus_busy = '1') then
 
				-- Stop Condition (SMBCLK High while SDA Low to High)
				if (smbclk_line_level = '1') and (smbdat_line_level_reg = '0') and (smbdat_line_level = '1') then
					smbus_stop_cond <= '1';
				end if;
 
			-- Disable Stop Condition (SMBus NOT Busy)
			else
				smbus_stop_cond <= '0';
 
			end if;
		end if;
	end process;
 
	-------------------------------
	-- SMBus Busy Timing Counter --
	-------------------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Reset Counter when Reset or SMBCLK / SMBDAT toggle
			if (i_reset = '1') or (smbclk_line_level = '0') or (smbdat_line_level = '0') then
				busy_timing_counter <= 0;
 
			-- Increment Counter (SMBCLK & SMBDAT High)
			else
				busy_timing_counter <= busy_timing_counter +1;
			end if;
		end if;
	end process;
 
	-- Busy Timing Counter End
	busy_timing_counter_end <= 	'1' when smbus_stop_cond = '1' and busy_timing_counter = SMBUS_100K_MIN_FREE_TIME_CYCLES and SMBUS_CLASS = SMBUS_100K_CLASS else
								'1' when smbus_stop_cond = '1' and busy_timing_counter = SMBUS_400K_MIN_FREE_TIME_CYCLES and SMBUS_CLASS = SMBUS_400K_CLASS else
								'1' when smbus_stop_cond = '1' and busy_timing_counter = SMBUS_1M_MIN_FREE_TIME_CYCLES and SMBUS_CLASS = SMBUS_1M_CLASS else
								'1' when busy_timing_counter = SMBUS_INACTIVITY_CYCLES else
								'0';
 
	--------------------------
	-- SMBus Busy Detection --
	--------------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Bus IDLE when Reset / Stop Condition then Minimum Free Time or after Inactivity Period
			if (i_reset = '1') or (busy_timing_counter_end = '1') then
				smbus_busy <= '0';
 
			-- Bus BUSY when Start Condition (SMBCLK High while SDA High to Low)
			elsif (smbus_start_cond = '1') then
				smbus_busy <= '1';
 
			end if;
		end if;
	end process;
	o_smbus_busy <= smbus_busy;
 
	---------------------------
	-- SMBus Timeout Counter --
	---------------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Reset Counter when Reset or SMBCLK is High
			if (i_reset = '1') or (smbclk_line_level = '1') then
				timeout_counter <= 0;
 
			-- Increment Counter
			else
				timeout_counter <= timeout_counter +1;
			end if;
		end if;
	end process;
 
	-- SMBus Timeout
	o_smbus_timeout <= 	'1' when timeout_counter = SMBUS_TIMEOUT_CYCLES else '0';
 
	---------------------------
	-- SMBus Bus Arbitration --
	---------------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- SMBDAT from Controller = SMBDAT Line (when SMBCLK is High)
			if (smbclk_line_level = '1') then
				smbus_arbitration <= i_smbdat_controller xnor smbdat_line_level;
			end if;
 
		end if;
	end process;
    o_smbus_arbitration <= smbus_arbitration;
 
    -----------------------------
	-- SMBus SMBCLK Stretching --
	-----------------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- SMBus Controller release SMBCLK ('Z') while SMBus Target pull-down SMBCLK
			smbclk_stretching <= i_smbclk_controller and not(smbclk_line_level);
 
		end if;
	end process;
	o_smbclk_stretching <= smbclk_stretching;
 
end Behavioral;

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

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