OpenCores
URL https://opencores.org/ocsvn/scab-master/scab-master/trunk

Subversion Repositories scab-master

[/] [scab-master/] [trunk/] [hw/] [sources/] [SCCBMaster.vhd] - Rev 5

Compare with Previous | Blame | View Log

------------------------------------------------------------------------
-- Engineer:    Dalmasso Loic
-- Create Date: 07/10/2024
-- Module Name: SCCBMaster
-- Description:
--      Serial Camera Control Bus (SCCB) Master Controller for OV7670 Camera Module.
--		Supports:
--			- Write Mode: 3-Phase Write Transmission
--			- Read Mode: 2-Phase Write Transmission, then 2-Phase Read Transmission
--
-- WARNING: /!\ Require Pull-Up on SCL and SDA pins /!\
--
-- Usage:
--		1. Set the inputs (keep unchanged until Ready signal is de-asserted)
--			* Mode (Read/Write)
--			* SCCB Slave Addresss
--			* Register Address
--			* Register Value (Write Mode only)
--		2. Asserts Start input (available only when the SCCB Master is Ready)
--		3. SCCB Master de-asserts the Ready signal
--		4. SCCB Master re-asserts the Ready signal at the end of transmission (master is ready for a new transmission)
--		5. In Read mode only, the read value is available when its validity signal is asserted
--
-- Generics
--		Input	-	input_clock: Module Input Clock Frequency
--		Output	-	sccb_clock: SCCB Serial Clock Frequency
-- Ports
--		Input 	-	i_clock: Module Input Clock
--		Input 	-	i_mode: Read or Write Mode ('0': Write, '1': Read)
--		Input 	-	i_slave_addr: Address of the SCCB Slave (7 bits)
--		Input 	-	i_reg_addr: Address of the Register to Read/Write
--		Input 	-	i_reg_value: Value of the Register to Write
--		Input 	-	i_start: Start SCCB Transmission ('0': No Start, '1': Start)
--		Output 	-	o_ready: Ready State of SCCB Master ('0': Not Ready, '1': Ready)
--		Output 	-	o_read_value_valid: Validity of value of the SCCB Slave Register ('0': Not Valid, '1': Valid)
--		Output 	-	o_read_value: Value of the SCCB Slave Register
--		Output 	-	o_scl: SCCB Serial Clock ('0'-'Z'(as '1') values, working with Pull-Up)
--		In/Out 	-	io_sda: SCCB 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 SCCBMaster is
 
GENERIC(
	input_clock: INTEGER := 12_000_000;
	sccb_clock: INTEGER := 100_000
);
 
PORT(
	i_clock: IN STD_LOGIC;
	i_mode: IN STD_LOGIC;
	i_slave_addr: IN STD_LOGIC_VECTOR(6 downto 0);
	i_reg_addr: IN STD_LOGIC_VECTOR(7 downto 0);
	i_reg_value: IN STD_LOGIC_VECTOR(7 downto 0);
	i_start: IN STD_LOGIC;
	o_ready: OUT STD_LOGIC;
	o_read_value_valid: OUT STD_LOGIC;
	o_read_value: OUT STD_LOGIC_VECTOR(7 downto 0);
	o_scl: OUT STD_LOGIC;
	io_sda: INOUT STD_LOGIC
);
 
END SCCBMaster;
 
ARCHITECTURE Behavioral of SCCBMaster is
 
------------------------------------------------------------------------
-- Constant Declarations
------------------------------------------------------------------------
-- SCCB Clock Dividers
constant CLOCK_DIV: INTEGER := input_clock / sccb_clock;
constant CLOCK_DIV_X2: INTEGER := CLOCK_DIV /2;
 
-- SCCB Modes ('0': Write, '1': Read)
constant SCCB_WRITE_MODE: STD_LOGIC := '0';
constant SCCB_READ_MODE: STD_LOGIC := '1';
 
-- SCCB Transmission Start Bit
constant TRANSMISSION_START_BIT: STD_LOGIC := '0';
 
-- SCCB Transmission Don'T Care Bit ('Z' with Pull-Up)
constant TRANSMISSION_DONT_CARE_BIT: STD_LOGIC := '1';
 
------------------------------------------------------------------------
-- Signal Declarations
------------------------------------------------------------------------
-- SCCB Master States
TYPE sccbState is (IDLE, START_TX, WRITE_SLAVE_ADDR, REGISTER_WRITE, REGISTER_READ, WRITE_REG_VALUE, PREP_READ_PHASE, END_TX);
signal state: sccbState := IDLE;
signal next_state: sccbState;
 
-- SCCB Input Registers
signal mode: STD_LOGIC_VECTOR(1 downto 0) := "00";
signal slave_addr: STD_LOGIC_VECTOR(7 downto 0) := (others => '0');
signal reg_addr: STD_LOGIC_VECTOR(7 downto 0) := (others => '0');
signal reg_value: STD_LOGIC_VECTOR(7 downto 0) := (others => '0');
signal start: STD_LOGIC := '0';
 
-- SCCB Transmission Bit Counter (8 cycles per phase)
signal bit_counter: UNSIGNED(3 downto 0) := (others => '0');
 
-- SCCB Clock Divider
signal clock_divider: INTEGER range 0 to CLOCK_DIV-1 := 0;
signal clock_enable: STD_LOGIC := '0';
signal clock_enable_x2: STD_LOGIC := '0';
 
-- SCCB SCL Register
signal scl_reg: STD_LOGIC := '0';
signal scl_enable: STD_LOGIC := '0';
 
-- SCCB SDA Output
signal sda_out: STD_LOGIC := TRANSMISSION_DONT_CARE_BIT;
 
-- SCCB SDA Input (Valid & Value)
signal sda_in_valid: STD_LOGIC := '0';
signal sda_in_enable: STD_LOGIC := '0';
signal sda_in_reg: STD_LOGIC_VECTOR(7 downto 0) := (others => '0');
 
------------------------------------------------------------------------
-- Module Implementation
------------------------------------------------------------------------
begin
 
	------------------------
	-- SCCB Clock Divider --
	------------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Reset Clock Divider
			if (clock_divider = CLOCK_DIV-1) then
				clock_divider <= 0;
 
			-- Increment Clock Divider
			else
				clock_divider <= clock_divider +1;
			end if;
		end if;
	end process;
 
	------------------------
	-- SCCB Clock Enables --
	------------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Clock Enable
			if (clock_divider = CLOCK_DIV-1) then
				clock_enable <= '1';
				clock_enable_x2 <= '1';
			else
				clock_enable <= '0';
				clock_enable_x2 <= '0';
			end if;
 
			-- Clock Enable x2
			if (clock_divider = CLOCK_DIV_X2-1) then
				clock_enable_x2 <= '1';
			end if;
 
		end if;
	end process;
 
	------------------------
	-- SCCB Start Handler --
	------------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Handle Pulse (clear value when clock_enable at '1')
			if (state = IDLE) then	
				start <= (i_start or start) and not (clock_enable);
			end if;
		end if;
	end process;
 
	------------------------
	-- SCCB State Machine --
	------------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Next State (when Clock Enable)
			if (clock_enable = '1') then
				state <= next_state;
			end if;
		end if;
	end process;
 
	-- SCCB Next State
	process(state, start, bit_counter, mode)
	begin
 
		case state is
			when IDLE => 	if (start = '1') then
								next_state <= START_TX;
							else
								next_state <= IDLE;
							end if;
 
			-- Start of Transmission
			when START_TX => next_state <= WRITE_SLAVE_ADDR;
 
			-- Write Slave Address
			when WRITE_SLAVE_ADDR =>
							if (bit_counter(3) = '1') then
 
								-- Read Mode (Current Phase)
								if (mode(0) = SCCB_READ_MODE) then
									next_state <= REGISTER_READ;
 
								-- Write Mode
								else
									next_state <= REGISTER_WRITE;
								end if;
							else
								next_state <= WRITE_SLAVE_ADDR;
							end if;
 
			-- Write Register Address
			when REGISTER_WRITE =>
							if (bit_counter(3) = '1') then
 
								-- Read Mode (for Next Phase)
								if (mode(1) = SCCB_READ_MODE) then
									next_state <= END_TX;
 
								-- Write Mode
								else
									next_state <= WRITE_REG_VALUE;
								end if;
							else
								next_state <= REGISTER_WRITE;
							end if;
 
			-- Write Register Value
			when WRITE_REG_VALUE =>
							if (bit_counter(3) = '1') then
								next_state <= END_TX;
							else
								next_state <= WRITE_REG_VALUE;
							end if;
 
			-- Read Register Value
			when REGISTER_READ =>
							if (bit_counter(3) = '1') then
								next_state <= END_TX;
							else
								next_state <= REGISTER_READ;
							end if;
 
			-- End of Transmission
			when END_TX => 	-- Read Mode (for Next Phase)
							if (mode(1) = SCCB_READ_MODE) then
								next_state <= PREP_READ_PHASE;
 
							else
								next_state <= IDLE;
							end if;
 
			-- Prepare Read Phase
			when PREP_READ_PHASE => next_state <= START_TX;
 
			when others => next_state <= IDLE;
		end case;
	end process;
 
	----------------------
	-- SCCB Bit Counter --
	----------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Clock Enable
			if (clock_enable = '1') then
 
				-- Reset Counter
				if (state = START_TX) or (bit_counter(3) = '1') then
					bit_counter <= (others => '0');
				else
					bit_counter <= bit_counter +1;
				end if;
			end if;
        end if;
    end process;
 
	----------------------------
	-- Slave Address Register --
	----------------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Clock Enable
			if (clock_enable = '1') then
 
				-- Load Slave Address Input (Handle continuous left-shift)
				if (state = IDLE) then
					slave_addr <= SCCB_WRITE_MODE & i_slave_addr;
 
				-- Slave Address Read Mode (Handle continuous left-shift)
				elsif (state = PREP_READ_PHASE) then
					slave_addr <= SCCB_READ_MODE & slave_addr(2 downto 0) & slave_addr(7 downto 4);
 
				-- Left-Shift
				else
					slave_addr <= slave_addr(6 downto 0) & slave_addr(7);
				end if;
			end if;
		end if;
	end process;
 
	-------------------------------
	-- Register Address Register --
	-------------------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Clock Enable
			if (clock_enable = '1') then
 
				-- Load Register Address Input (Handle continuous left-shift)
				if (state = IDLE) then
					reg_addr <= i_reg_addr(1 downto 0) & i_reg_addr(7 downto 2);
 
				-- Left-Shift
				else
					reg_addr <= reg_addr(6 downto 0) & reg_addr(7);
				end if;
			end if;
		end if;
	end process;
 
	-----------------------------
	-- Register Value Register --
	-----------------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Clock Enable
			if (clock_enable = '1') then
 
				-- Load Register Value Input (Handle continuous left-shift)
				if (state = IDLE) then
					reg_value <= i_reg_value(2 downto 0) & i_reg_value(7 downto 3);
 
				-- Left-Shift
				else
					reg_value <= reg_value(6 downto 0) & reg_value(7);
				end if;
			end if;
		end if;
	end process;
 
	-------------------
	-- Mode Register --
	-------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Clock Enable
			if (clock_enable = '1') then
 
				-- Load Mode Input
				if (state = IDLE) then
					mode <= i_mode & SCCB_WRITE_MODE;
 
				-- Right-Shift
				elsif (state = END_TX) then
					mode <= SCCB_WRITE_MODE & mode(1);
				end if;
			end if;
		end if;
	end process;
 
	-----------------------
	-- SCCB Master Ready --
	-----------------------
	o_ready <= '1' when state = IDLE else '0';
 
	-----------------------
	-- SCCB SCL Register --
	-----------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Toggle SCL Reg
			if (clock_enable_x2 = '1') then
				scl_reg <= not(scl_reg);
			end if;
		end if;
	end process;
 
	---------------------
	-- SCCB SCL Enable --
	---------------------
	scl_enable <= '0' when state = IDLE or state = START_TX or state = PREP_READ_PHASE else '1';
 
	--------------
	-- SCCB SCL --
	--------------
	-- ('0' or 'Z' values)
	o_scl <= '0' when scl_reg = '0' and scl_enable = '1' else 'Z';
 
	-----------------------
	-- SCCB SDA Selector --
	-----------------------
	sda_out <=	TRANSMISSION_START_BIT when state = START_TX or state = END_TX else
				TRANSMISSION_DONT_CARE_BIT when bit_counter(3) = '1' else
				slave_addr(7) when state = WRITE_SLAVE_ADDR else
				reg_addr(7) when state = REGISTER_WRITE else
				reg_value(7) when state = WRITE_REG_VALUE else
				TRANSMISSION_DONT_CARE_BIT;
 
	---------------------
	-- SCCB SDA Output --
	---------------------
	-- ('0' or 'Z' values)
	io_sda <= '0' when sda_out = '0' else 'Z';
 
	---------------------------
	-- SCCB SDA Input Enable --
	---------------------------
	sda_in_enable <= '1' when (state = REGISTER_READ) and (bit_counter(3) = '0') else '0';
 
	--------------------------
	-- SCCB SDA Input Value --
	--------------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- SDA Input Enable
			if (clock_enable = '1') and (sda_in_enable = '1') then
				sda_in_reg <= sda_in_reg(6 downto 0) & io_sda;
			end if;
		end if;
	end process;
 
	---------------------------
	-- SCCB Read Value Valid --
	---------------------------
	process(i_clock)
	begin
		if rising_edge(i_clock) then
 
			-- Disable SDA Valid Data (New cycle)
			if (state = START_TX) then
				sda_in_valid <= '0';
 
			-- Read Value valid
			elsif (mode(0) = SCCB_READ_MODE) and (state = END_TX) then
				sda_in_valid <= '1';
			end if;
		end if;
	end process;
	o_read_value_valid <= sda_in_valid;
 
	---------------------
	-- SCCB Read Value --
	---------------------
	o_read_value <= sda_in_reg when sda_in_valid = '1' else (others => '0');
 
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.