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

Subversion Repositories smbus_controller

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

Compare with Previous | Blame | View Log

------------------------------------------------------------------------
-- Engineer:    Dalmasso Loic
-- Create Date: 18/11/2024
-- Module Name: SMBusController
-- Description:
--      System Management Bus (SMBus) Controller, compatible with all 15 commands (from SMBus Speficiation 3.3.1)
--		Features:
--			- Controller-Transmitter with Read/Write Operations
--			- Command Byte activation/deactivation
--			- PEC (Packet Error Code) activation/deactivation
--			- Configurable Read/Write Data length
--			- Bus Busy Detection
--			- Bus Timeout Detection
--			- Clock Stretching Detection
--			- Multimaster (arbitration)
--			- SMBus Classes (100kHz, 400kHz, 1MHz)
--
-- WARNING: /!\ Require Pull-Up on SMBCLK and SMBDAT pins /!\
--
-- Usage:
--      The Ready signal indicates no operation is on going and the SMBus Controller is waiting operation.
--		The Busy signal indicates operation is on going.
--      Reset input can be trigger at any time to reset the SMBus Controller to the IDLE state.
--		1. Set all necessary inputs
--			* Mode (Write only, Read only or Write-then-Read)
--			* SMBus Slave Address Delay
--			* Enable/Disable SMBus Command to Write
--			* Enable/Disable SMBus PEC (Packet Error Code)
--			* Set the number of byte to Write (in Write-only and  Write-then-Read modes)
--			* Set the number of byte to Read (in Read-only and  Write-then-Read modes)
--			* Set SMBus Command Value (SMBus Commande enable)
--			* Set SMBus Data to Write
--      2. Asserts Start input. The Ready signal is de-asserted and the Busy signal is asserted.
--		3. SMBus Controller re-asserts the Ready signal at the end of transmission (Controller is ready for a new transmission)
--		4. The Data Read value is available when its validity signal is asserted
--		5. If an Error occur during transmission, the Error signal is asserterd
--
-- Generics
--		INPUT_CLOCK_FREQ: Module Input Clock Frequency
--		SMBUS_CLOCK_FREQ: SMBus Serial Clock Frequency
--		SMBUS_CLASS: SMBus Class (100kHz, 400kHz, 1MHz)
--		MAX_DATA_BIT_LENGTH: Maximum Length of the SMBus Data in bits
--
-- Ports
--		Input 	-	i_clock: Module Input Clock
--		Input 	-	i_reset: Reset ('0': No Reset, '1': Reset)
--		Input 	-	i_start: Start SMBus Transmission ('0': No Start, '1': Start)
--		Input 	-	i_mode: Operation Mode ("00": Write-Only, "11": Read-Only, "01": Write-then-Read)
--		Input 	-	i_slave_addr: SMBus Slave Address (7 bits)
--		Input 	-	i_cmd_enable: Command Byte Enable ('0': Disable, '1': Enable)
--		Input 	-	i_pec_enable: Packet Error Code Enable ('0': Disable, '1': Enable)
--		Input 	-	i_data_write_byte_number: Number of Data Bytes to Write
--		Input 	-	i_data_read_byte_number: Number of Data Bytes to Read
--		Input 	-	i_cmd: Command Value to Write
--		Input 	-	i_data_write: Data Value to Write
--		Output 	-	o_data_read: Read Data Value
--		Output 	-	o_data_read_valid: Validity of the Read Data Value ('0': Not Valid, '1': Valid)
--		Output 	-	o_ready: Ready State of SMBus Controller ('0': Not Ready, '1': Ready)
--		Output 	-	o_error: Error State of SMBus Controller ('0': No Error, '1': Error)
--		Output 	-	o_busy: Busy State of SMBus Controller ('0': Not Busy, '1': Busy)
--		In/Out 	-	io_smbclk: SMBus Serial Clock ('0'-'Z'(as '1') values, working with Pull-Up)
--		In/Out 	-	io_smbdat: SMBus Serial Data ('0'-'Z'(as '1') values, working with Pull-Up)
------------------------------------------------------------------------
 
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;
 
ENTITY SMBusController is
 
GENERIC(
	INPUT_CLOCK_FREQ: INTEGER := 12_000_000;
	SMBUS_CLOCK_FREQ: INTEGER := 100_000;
	SMBUS_CLASS: INTEGER := 100_000;
	MAX_DATA_BIT_LENGTH: INTEGER := 8
);
 
PORT(
	i_clock: IN STD_LOGIC;
    i_reset: IN STD_LOGIC;
	i_start: IN STD_LOGIC;
	i_mode: IN STD_LOGIC_VECTOR(1 downto 0);
	i_slave_addr: IN STD_LOGIC_VECTOR(6 downto 0);
	i_cmd_enable: IN STD_LOGIC;
	i_pec_enable: IN STD_LOGIC;
	i_data_write_byte_number: IN INTEGER range 0 to MAX_DATA_BIT_LENGTH/8;
	i_data_read_byte_number: IN INTEGER range 0 to MAX_DATA_BIT_LENGTH/8;
	i_cmd: IN STD_LOGIC_VECTOR(7 downto 0);
	i_data_write: IN STD_LOGIC_VECTOR(MAX_DATA_BIT_LENGTH-1 downto 0);
	o_data_read: OUT STD_LOGIC_VECTOR(MAX_DATA_BIT_LENGTH-1 downto 0);
	o_data_read_valid: OUT STD_LOGIC;
	o_ready: OUT STD_LOGIC;
	o_error: OUT STD_LOGIC;
	o_busy: OUT STD_LOGIC;
	io_smbclk: INOUT STD_LOGIC;
	io_smbdat: INOUT STD_LOGIC
);
 
END SMBusController;
 
ARCHITECTURE Behavioral of SMBusController is
 
------------------------------------------------------------------------
-- Component Declarations
------------------------------------------------------------------------
COMPONENT 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 COMPONENT;
 
COMPONENT SMBusPec is
 
	PORT(
		i_clock: IN STD_LOGIC;
		i_reset: IN STD_LOGIC;
		i_enable: IN STD_LOGIC;
		i_data: IN STD_LOGIC_VECTOR(7 downto 0);
		o_pec: OUT STD_LOGIC_VECTOR(7 downto 0)
	);
 
END COMPONENT;
 
------------------------------------------------------------------------
-- Constant Declarations
------------------------------------------------------------------------
-- SMBus Clock Dividers
constant CLOCK_DIV: INTEGER := INPUT_CLOCK_FREQ / SMBUS_CLOCK_FREQ;
constant CLOCK_DIV_1_4: INTEGER := CLOCK_DIV /4;
constant CLOCK_DIV_3_4: INTEGER := CLOCK_DIV - CLOCK_DIV_1_4;
 
-- SMBus Controller Modes ("00": Write-Only, "11": Read-Only, "01": Write-then-Read)
constant SMBUS_WRITE_ONLY_MODE: STD_LOGIC_VECTOR(1 downto 0) := "00";
constant SMBUS_READ_ONLY_MODE: STD_LOGIC_VECTOR(1 downto 0) := "11";
constant SMBUS_WRITE_THEN_READ_MODE: STD_LOGIC_VECTOR(1 downto 0) := "01";
 
-- SMBus Modes ('0': Write, '1': Read)
constant SMBUS_WRITE_MODE: STD_LOGIC := '0';
constant SMBUS_READ_MODE: STD_LOGIC := '1';
 
-- SMBus Bit Counter End (8-bit per cycle)
constant SMBUS_BIT_COUNTER_END: UNSIGNED(3 downto 0) := "0111";
 
-- SMBus SMBDAT Left Shift Value
constant LEFT_SHIFT_EMPTY_VALUE: STD_LOGIC := '0';
 
-- SMBus IDLE ('Z' with Pull-Up)
constant TRANSMISSION_IDLE: STD_LOGIC := 'Z';
 
-- SMBus Transmission Don't Care Bit
constant TRANSMISSION_DONT_CARE_BIT: STD_LOGIC := '1';
 
-- SMBus Transmission Start Bit
constant TRANSMISSION_START_BIT: STD_LOGIC := '0';
 
-- SMBus Transmission ACK Bit
constant TRANSMISSION_ACK_BIT: STD_LOGIC := '0';
 
-- SMBus Transmission NACK Bit
constant TRANSMISSION_NACK_BIT: STD_LOGIC := '1';
 
------------------------------------------------------------------------
-- Signal Declarations
------------------------------------------------------------------------
-- SMBus Controller States
TYPE smbusState is (IDLE, START_TX,
					WRITE_SLAVE_ADDR_W, WRITE_CMD_VALUE, WRITE_REG_VALUE, WRITE_PEC_VALUE,
					RE_START_TX,
					WRITE_SLAVE_ADDR_R, READ_REG_VALUE, READ_PEC_VALUE,
					STOP_TX);
signal state: smbusState := IDLE;
signal next_state: smbusState;
 
-- SMBus Clock Management
signal clock_divider: INTEGER range 0 to CLOCK_DIV-1 := 0;
signal clock_enable: STD_LOGIC := '0';
signal clock_enable_1_4: STD_LOGIC := '0';
signal clock_enable_3_4: STD_LOGIC := '0';
 
-- SMBus Bit Counter (8 bits per phass + 1 bit ACK/ACK)
signal bit_counter: UNSIGNED(3 downto 0) := (others => '0');
signal bit_counter_end: STD_LOGIC := '0';
 
-- SMBus Bit/Byte Counter
signal byte_counter: INTEGER range 0 to MAX_DATA_BIT_LENGTH/8 := 0;
signal byte_counter_end: STD_LOGIC := '0';
 
-- SMBus SMBCLK & SMBDAT IOs
signal smbclk_in: STD_LOGIC := '0';
signal smbclk_out: STD_LOGIC := '0';
signal smbclk_out_reg: STD_LOGIC := '0';
signal smbdat_in: STD_LOGIC := '0';
signal smbdat_in_reg: STD_LOGIC_VECTOR(MAX_DATA_BIT_LENGTH-1 downto 0) := (others => '0');
signal smbdat_in_valid: STD_LOGIC := '0';
signal smbdat_out: STD_LOGIC := '0';
signal smbdat_out_reg: STD_LOGIC_VECTOR(MAX_DATA_BIT_LENGTH-1 downto 0) := (others => '0');
 
-- SMBus PEC Signals
signal smbus_pec_reset: STD_LOGIC := '0';
signal smbus_pec_enable: STD_LOGIC := '0';
signal smbus_pec_input: STD_LOGIC_VECTOR(7 downto 0) := (others => '0');
signal smbus_pec_value: STD_LOGIC_VECTOR(7 downto 0) := (others => '0');
signal smbus_pec_received: STD_LOGIC_VECTOR(7 downto 0) := (others => '0');
 
-- SMBus Analyzer Signals
signal smbus_busy: STD_LOGIC := '0';
signal smbus_timeout: STD_LOGIC := '0';
signal smbus_arbitration: STD_LOGIC := '0';
signal smbclk_stretching: STD_LOGIC := '0';
 
-- SMBus Error
signal smbus_error: STD_LOGIC := '0';
 
------------------------------------------------------------------------
-- Module Implementation
------------------------------------------------------------------------
begin
 
	-------------------------
	-- SMBus Clock Divider --
	-------------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Reset Clock Divider
			if (i_reset = '1') or (clock_divider = CLOCK_DIV-1) then
				clock_divider <= 0;
 
			-- Increment Clock Divider (Waiting no SMBCLK Stretching)
			elsif (smbclk_stretching = '0') then
				clock_divider <= clock_divider +1;
			end if;
		end if;
	end process;
 
	-------------------------
	-- SMBus Clock Enables --
	-------------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- SMBCLK Stretching (Waiting no SMBCLK Stretching)
			if (smbclk_stretching = '0') then
 
				-- Clock Enable
				if (clock_divider = CLOCK_DIV-1) then
					clock_enable <= '1';
				else
					clock_enable <= '0';
				end if;
 
				-- Clock Enable (1/4)
				if (clock_divider = CLOCK_DIV_1_4-1) then
					clock_enable_1_4 <= '1';
				else
					clock_enable_1_4 <= '0';
				end if;
 
				-- Clock Enable (3/4)
				if (clock_divider = CLOCK_DIV_3_4-1) then
					clock_enable_3_4 <= '1';
				else
					clock_enable_3_4 <= '0';
				end if;
 
			-- SMBCLK Stretching (Disable all Clock Enables)
			else
				clock_enable <= '0';
				clock_enable_1_4 <= '0';
				clock_enable_3_4 <= '0';
 
			end if;
		end if;
	end process;
 
	-------------------------
	-- SMBus State Machine --
	-------------------------
    -- SMBus State
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Reset State
			if (i_reset = '1') then
				state <= IDLE;
 
			-- Next State (when Clock Enable)
			elsif (clock_enable = '1') then
				state <= next_state;
			end if;
		end if;
	end process;
 
    -- SMBus Next State
	process(state, i_start, i_mode, i_cmd_enable, i_pec_enable, i_data_write_byte_number, i_data_read_byte_number, smbus_busy, smbus_error, smbus_arbitration, bit_counter_end, byte_counter_end)
	begin
 
		case state is
			when IDLE => 	if (i_start = '1') and (smbus_busy = '0') then
								next_state <= START_TX;
							else
								next_state <= IDLE;
							end if;
 
			-- Start Transmission
			when START_TX =>
							-- Read-Only Mode
							if (i_mode = SMBUS_READ_ONLY_MODE) then
								next_state <= WRITE_SLAVE_ADDR_R;
 
							-- Write-Only or Write-then-Read Modes
							else
								next_state <= WRITE_SLAVE_ADDR_W;
							end if;
 
			-- Write Slave Address (Write Mode)
			when WRITE_SLAVE_ADDR_W =>
							-- Error
							if (smbus_error = '1') then
								next_state <= STOP_TX;
 
							-- End of Write Slave Addr Cycle
							elsif (bit_counter_end = '1') then
 
								-- Write Command
								if (i_cmd_enable = '1') then
									next_state <= WRITE_CMD_VALUE;
 
								-- No Byte to Write
								elsif (i_data_write_byte_number = 0) then
									next_state <= STOP_TX;
 
								-- Write Value
								else
									next_state <= WRITE_REG_VALUE;
								end if;
 
							-- Master Loses Arbitration (during Write Cycle)
							elsif (smbus_arbitration = '0') then
								next_state <= IDLE;
 
							else
								next_state <= WRITE_SLAVE_ADDR_W;
							end if;
 
			-- Write Command Value (Write Mode)
			when WRITE_CMD_VALUE =>
							-- Error
							if (smbus_error = '1') then
								next_state <= STOP_TX;
 
							-- End of Write Command Value Cycle
							elsif (bit_counter_end = '1') then
 
								-- At least 1 Byte to Write
								if (i_data_write_byte_number /= 0) then
									next_state <= WRITE_REG_VALUE;
 
								-- Write-then-Read Mode
								elsif (i_mode = SMBUS_WRITE_THEN_READ_MODE) then
									next_state <= RE_START_TX;
 
								-- End of Transmission (Should NOT Happen)
								else
									next_state <= STOP_TX;
								end if;
 
							-- Master Loses Arbitration (during Write Cycle)
							elsif (smbus_arbitration = '0') then
								next_state <= IDLE;
 
							else
								next_state <= WRITE_CMD_VALUE;
							end if;
 
			-- Write Register Value (Write Mode)
			when WRITE_REG_VALUE =>
							-- Error
							if (smbus_error = '1') then
								next_state <= STOP_TX;
 
							-- End of Write Value Cycle
							elsif (byte_counter_end = '1') then
 
								-- Write-then-Read Mode
								if (i_mode = SMBUS_WRITE_THEN_READ_MODE) then
									next_state <= RE_START_TX;
 
								-- Write PEC Value
								elsif (i_pec_enable = '1') then
									next_state <= WRITE_PEC_VALUE;
 
								-- Stop Transmission
								else
									next_state <= STOP_TX;
								end if;
 
							-- Master Loses Arbitration (during Write Cycle)
							elsif (bit_counter_end = '0') and (smbus_arbitration = '0') then
								next_state <= IDLE;
 
							else
								next_state <= WRITE_REG_VALUE;
							end if;
 
			-- Write PEC Value (Write Mode)
			when WRITE_PEC_VALUE =>
							-- End of Write PEC Value Cycle (Stop Transmission)
							if (bit_counter_end = '1') then
								next_state <= STOP_TX;
 
							-- Master Loses Arbitration (during Write Cycle)
							elsif (smbus_arbitration = '0') then
								next_state <= IDLE;
 
							else
								next_state <= WRITE_PEC_VALUE;
							end if;
 
			-- Re-Start Transmission
			when RE_START_TX => next_state <= WRITE_SLAVE_ADDR_R;
 
			-- Write Slave Address (Read Mode)
			when WRITE_SLAVE_ADDR_R =>
							-- Error
							if (smbus_error = '1') then
								next_state <= STOP_TX;
 
							-- End of Write Slave Addr Cycle
							elsif (bit_counter_end = '1') then
 
								-- No Byte to Write
								if (i_data_read_byte_number = 0) then
									next_state <= STOP_TX;
								else
									next_state <= READ_REG_VALUE;
								end if;
 
							-- Master Loses Arbitration (during Write Cycle)
							elsif (smbus_arbitration = '0') then
								next_state <= IDLE;
 
							else
								next_state <= WRITE_SLAVE_ADDR_R;
							end if;
 
			-- Read Register Value (Read Mode)
			when READ_REG_VALUE =>
							-- End of Read Value Cycle
							if (byte_counter_end = '1') then
 
								-- Read PEC Value
								if (i_pec_enable = '1') then
									next_state <= READ_PEC_VALUE;
 
								-- Stop Transmission
								else
									next_state <= STOP_TX;
								end if;
 
							else
								next_state <= READ_REG_VALUE;
							end if;
 
			-- Read PEC Value (Read Mode)
			when READ_PEC_VALUE =>
							-- End of Read PEC Value Cycle
							if (bit_counter_end = '1') then
								next_state <= STOP_TX;
							else
								next_state <= READ_PEC_VALUE;
							end if;
 
			-- Stop Transmission
			when others => next_state <= IDLE;
		end case;
	end process;
 
	-----------------------
	-- SMBus Bit Counter --
	-----------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Clock Enable
			if (clock_enable = '1') then
 
				-- Reset Counter
				if (state = IDLE) or (state = START_TX) or (state = RE_START_TX) or (state = STOP_TX) or (bit_counter_end = '1') then
					bit_counter <= (others => '0');
 
				-- Increment Counter
				else
					bit_counter <= bit_counter +1;
				end if;
			end if;
		end if;
    end process;
 
	-- Bit Counter End
	bit_counter_end <= bit_counter(3);
 
	------------------------
	-- SMBus Byte Counter --
	------------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Clock Enable
			if (clock_enable = '1') then
 
				-- Reset Byte Counter
				if (state = START_TX) or (state = RE_START_TX) then
					byte_counter <= 0;
 
				-- Increment Byte Counter (bii counter to 7 for anticipation)
				elsif ((state = WRITE_REG_VALUE) or (state = READ_REG_VALUE)) and (bit_counter = SMBUS_BIT_COUNTER_END) then
					byte_counter <= byte_counter +1;
				end if;
			end if;
		end if;
	end process;
 
	-- Byte Counter End
	byte_counter_end <= '1' when (state = WRITE_REG_VALUE) and (byte_counter = i_data_write_byte_number) else
						'1' when (state = READ_REG_VALUE) and (byte_counter = i_data_read_byte_number) else
						'0';
 
	----------------------------------
	-- SMBus SMBCLK Output Register --
	----------------------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- SMBCLK High ('Z')
			if (clock_enable_1_4 = '1') or (state = IDLE) then
				smbclk_out_reg <= '1';
 
			-- SMBCLK Low ('0')
			elsif (clock_enable_3_4 = '1') and (state /= RE_START_TX) and (state /= STOP_TX) then
				smbclk_out_reg <= '0';
			end if;
		end if;
	end process;
	smbclk_out <= smbclk_out_reg;
 
	---------------------------------
	-- SMBus SMBDAT Write Register --
	---------------------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Clock Enable
			if (clock_enable = '1') then
 
				-- Load Slave Address (Write/Read Mode)
				if (state = START_TX) then
 
					if (i_mode = SMBUS_READ_ONLY_MODE) then
						-- Load Slave Address (Read Mode)
						smbdat_out_reg(MAX_DATA_BIT_LENGTH-1 downto MAX_DATA_BIT_LENGTH-8) <= i_slave_addr & SMBUS_READ_MODE;
					else
						-- Load Slave Address (Write Mode)
						smbdat_out_reg(MAX_DATA_BIT_LENGTH-1 downto MAX_DATA_BIT_LENGTH-8) <= i_slave_addr & SMBUS_WRITE_MODE;
					end if;
 
				-- Load Slave Address (Read Mode)
				elsif (state = RE_START_TX) then
					smbdat_out_reg(MAX_DATA_BIT_LENGTH-1 downto MAX_DATA_BIT_LENGTH-8) <= i_slave_addr & SMBUS_READ_MODE;
 
				-- Load Write Command value / Write Register Value
				elsif (state = WRITE_SLAVE_ADDR_W) and (bit_counter_end = '1') then
 
					-- Load Write Command value
					if (i_cmd_enable = '1') then
						smbdat_out_reg(MAX_DATA_BIT_LENGTH-1 downto MAX_DATA_BIT_LENGTH-8) <= i_cmd;
 
					-- Load Write Register Value
					else
						smbdat_out_reg <= i_data_write;
					end if;
 
				-- Load Write Register Value
				elsif (state = WRITE_CMD_VALUE) and (bit_counter_end = '1') then
					smbdat_out_reg <= i_data_write;
 
				-- Load Write PEC Value
				elsif (state = WRITE_REG_VALUE) and (i_pec_enable = '1') and (byte_counter_end = '1') then
					smbdat_out_reg(MAX_DATA_BIT_LENGTH-1 downto MAX_DATA_BIT_LENGTH-8) <= smbus_pec_value;
 
				-- Left-Shift
				else
					smbdat_out_reg <= smbdat_out_reg(MAX_DATA_BIT_LENGTH-2 downto 0) & LEFT_SHIFT_EMPTY_VALUE;
				end if;
			end if;
		end if;
	end process;
 
	-------------------------------
	-- SMBus SMBDAT Output Value --
	-------------------------------
	process(state, bit_counter_end, byte_counter_end, smbdat_out_reg)
	begin
		-- Start & Stop Transmission
		if (state = START_TX) or (state = STOP_TX) then
			smbdat_out <= TRANSMISSION_START_BIT;
 
		-- Read Cycles
		elsif (state = READ_REG_VALUE) or (state = READ_PEC_VALUE) then
 
			-- Last Read Cycle
			if (byte_counter_end = '1') then
				smbdat_out <= TRANSMISSION_NACK_BIT;
 
			-- End of Read Phase
			elsif (bit_counter_end = '1') then
				smbdat_out <= TRANSMISSION_ACK_BIT;
 
			-- Read In-Progress
			else
				smbdat_out <= TRANSMISSION_DONT_CARE_BIT;
			end if;
 
		-- IDLE / Re-Start Cycles / End of Write Cycles
		elsif (state = IDLE) or (state = RE_START_TX) or (bit_counter_end = '1') then
			smbdat_out <= TRANSMISSION_DONT_CARE_BIT;
 
		-- Write Cycles
		else
			smbdat_out <= smbdat_out_reg(7);
		end if;
	end process;
 
	-------------------------------
	-- SMBus SMBCLK & SMBDAT IOs --
	-------------------------------
	-- SMBus SMBCLK Input (for Simulation only: '0' when io_smbclk = '0' else '1')
	smbclk_in <= io_smbclk;
 
	-- SMBus SMBCLK Output ('0' or 'Z' values)
	io_smbclk <= '0' when smbclk_out = '0' else TRANSMISSION_IDLE;
 
	-- SMBus SMBDAT Input (for Simulation only: '0' when io_smbdat = '0' else '1')
	smbdat_in <= '0' when io_smbdat = '0' else '1';
 
	-- SMBus SMBDAT Output ('0' or 'Z' values)
	io_smbdat <= '0' when smbdat_out = '0' else TRANSMISSION_IDLE;
 
	--------------------
	-- SMBus Analyzer --
	--------------------
	inst_smbusAnalyzer: SMBusAnalyzer
		generic map (
			INPUT_CLOCK_FREQ => INPUT_CLOCK_FREQ,
			SMBUS_CLASS => SMBUS_CLASS)
 
		port map (
			i_clock => i_clock,
			i_reset => i_reset,
			i_smbclk_controller => smbclk_out,
			i_smbclk_line => smbclk_in,
			i_smbdat_controller => smbdat_out,
			i_smbdat_line => smbdat_in,
			o_smbus_busy => smbus_busy,
			o_smbus_timeout => smbus_timeout,
			o_smbus_arbitration => smbus_arbitration,
			o_smbclk_stretching => smbclk_stretching);
 
	----------------------
	-- SMBus Read Value --
	----------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Clock Enable
			if (clock_enable = '1') then
 
				-- Not End of Read Register Value Phase
				if (state = READ_REG_VALUE) and (bit_counter_end = '0') then
					smbdat_in_reg <= smbdat_in_reg(MAX_DATA_BIT_LENGTH-2 downto 0) & smbdat_in;
				end if;
			end if;
		end if;
	end process;
	o_data_read <= smbdat_in_reg;
 
	----------------------------
	-- SMBus Read Value Valid --
	----------------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- End of Transmission
			if (state = STOP_TX) then
 
				-- Read Value with PEC
				if (i_pec_enable = '1') then
 
					-- Verify PEC (Read Value Valid only if PEC OK)
					if (smbus_pec_value = smbus_pec_received) then
						smbdat_in_valid <= '1';
					else
						smbdat_in_valid <= '0';
					end if;
 
				-- Read Value without PEC
				else
					smbdat_in_valid <= '1';
				end if;
 
			-- Disable Read Value Valid (New cycle)
			elsif (state /= IDLE) then
				smbdat_in_valid <= '0';
			end if;
 
		end if;
	end process;
	o_data_read_valid <= smbdat_in_valid;
 
	--------------------------
	-- SMBus PEC Controller --
	--------------------------
	-- SMBus PEC Reset (when Start Transmission)
	smbus_pec_reset <= '1' when state = START_TX else '0';
 
	-- SMBus PEC Enable Handler
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Clock Enable & PEC Enable & Write / Read Cycles
			if (clock_enable_1_4 = '1') and (i_pec_enable = '1') and (
				(state = WRITE_SLAVE_ADDR_W) or (state = WRITE_CMD_VALUE) or (state = WRITE_REG_VALUE) or 
				(state = WRITE_SLAVE_ADDR_R) or (state = READ_REG_VALUE)) and (bit_counter_end = '1') then
 
				smbus_pec_enable <= '1'; 
			else
				smbus_pec_enable <= '0';
			end if;
		end if;
	end process;
 
	-- SMBus PEC Input Handler
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Clock Enable
			if (clock_enable = '1') then
 
				-- Write Cycles
				if (state = WRITE_SLAVE_ADDR_W) or (state = WRITE_CMD_VALUE) or (state = WRITE_REG_VALUE) or (state = WRITE_SLAVE_ADDR_R) then
					smbus_pec_input <= smbus_pec_input(6 downto 0) & smbdat_out;
 
				-- Read Cycles
				elsif (state = READ_REG_VALUE) then
					smbus_pec_input <= smbus_pec_input(6 downto 0) & smbdat_in;
				end if;
			end if;
		end if;
	end process;
 
	-- SMBus Received PEC Handler
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Clock Enable
			if (clock_enable = '1') then
 
				-- Not End of Read PEC Value Phase
				if (i_pec_enable = '1') and (bit_counter_end = '0') then
					smbus_pec_received <= smbus_pec_received(6 downto 0) & smbdat_in;
				end if;
			end if;
		end if;
	end process;
 
	------------------------
	-- SMBus PEC Instance --
	------------------------
	inst_smbusPec: SMBusPec
		port map (
			i_clock => i_clock,
			i_reset => smbus_pec_reset,
			i_enable => smbus_pec_enable,
			i_data => smbus_pec_input,
			o_pec => smbus_pec_value);
 
	------------------------
	-- SMBus Ready Status --
	------------------------
	o_ready <= '1' when state = IDLE and smbus_busy = '0' else '0';
 
	------------------------
	-- SMBus Busy Status --
	------------------------
	o_busy <= smbus_busy;
 
	------------------------
	-- SMBus Error Status --
	------------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Disable Error Flag (New cycle)
			if (state = START_TX) then
				smbus_error <= '0';
 
			-- Timeout
			elsif (smbus_timeout = '1') then
				smbus_error <= '1';
 
			-- Write Cycles
			elsif (state = WRITE_SLAVE_ADDR_W) or (state = WRITE_CMD_VALUE) or (state = WRITE_REG_VALUE) or (state = WRITE_PEC_VALUE) or (state = WRITE_SLAVE_ADDR_R) then
 
				-- No ACK at the End of Write Cycle
				if (bit_counter_end = '1') and (smbdat_in /= TRANSMISSION_ACK_BIT) then
					smbus_error <= '1';
				end if;
 
			-- Received PEC Error (only in Read or Write-then-Read modes)
			elsif (state = STOP_TX) and (i_pec_enable = '1') and (smbus_pec_value /= smbus_pec_received) then
				smbus_error <= '1';
			end if;
		end if;
	end process;
	o_error <= smbus_error;
 
end Behavioral;

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.