% 6.111 Final Project - Fall 1996 - "PIC-Cam" %
% Kenneth B. Russell <kbrussel@mit.edu> %

include "lpm_counter.inc";

% The macros below go unused because the Altera compiler %
%    won't allow evaluated functions in a table %
%    Therefore, the code is strewn with magic numbers (usually %
%    marked as such explicitly) %
CONSTANT BASE_ADDR_8584 = H"00";
DEFINE PCF_ADDR(x) = (x + BASE_ADDR_8584);
CONSTANT BASE_ADDR_7110 = H"08";
DEFINE SAA_ADDR(x) = x + BASE_ADDR_7110;

CONSTANT PLACE_128 = 7;		% Place value of 128s from 0..7. Should be 7. %
							% Can be made smaller for simulation. %
CONSTANT PLACE_64 = 6;		% Place value of 64s from 0..7. Should be 6. %
							% Can be made smaller for simulation. %	
CONSTANT PLACE_8 = 3;		% Place value of 8s from 0..7. Should be 3. %
							% Can be made smaller for simulation. %	

% FSM which speaks the I^2C bus protocol to the 7110 chip. %
SUBDESIGN 7110ss_stdaln
(
	% Inputs to the FSM %
	reset_ss	: INPUT;
	/clk		: INPUT;	% Source = LLC. ~80ns clock period. %
	go			: INPUT;
	sda_in		: INPUT;
	% Outputs %
	sda			: OUTPUT;
	scl			: OUTPUT;
	busy		: OUTPUT;
	done		: OUTPUT;
	error		: OUTPUT;
)
VARIABLE
	delay_ctr	: lpm_counter WITH (
		LPM_WIDTH=8,
		LPM_DIRECTION = "UP"
	);
	addr_ctr	: lpm_counter WITH (
		LPM_WIDTH = 7,
		LPM_DIRECTION = "UP"
	);
	ss			: MACHINE WITH STATES (
		ResetState,
		SendStart_0, SendStart_1,
		SendBit8_0, SendBit8_1, SendBit8_2,
		SendBit7_0, SendBit7_1, SendBit7_2,
		SendBit6_0, SendBit6_1, SendBit6_2,
		SendBit5_0, SendBit5_1, SendBit5_2,
		SendBit4_0, SendBit4_1, SendBit4_2,
		SendBit3_0, SendBit3_1, SendBit3_2,
		SendBit2_0, SendBit2_1, SendBit2_2,
		SendBit1_0, SendBit1_1, SendBit1_2,
		GetAck_0, GetAck_1, GetAck_2, GetAck_3,
		SendStop_0, SendStop_1, SendStop_2, SendStop_3,
		Done, Error_NACK
	);

	% Nodes for the data for the 7110 %
	db_out[8..1]	: NODE;

	% Registers for data bus output (to avoid glitches) %
	sda_reg		: DFF;
	scl_reg		: DFF;
	sda_node	: NODE;
	scl_node	: NODE;
	
	% Clock divider %
	clk_div		: TFF;

BEGIN
	DEFAULTS
		busy = VCC;
		delay_ctr.cnt_en = GND;
		addr_ctr.cnt_en = GND;
		
		sda_node = VCC;
		scl_node = VCC;
	END DEFAULTS;

	% Set up the clock divider %
	clk_div.clk = /clk;
	clk_div.t = VCC;

	% Set up the serial data bus %
	sda_reg.d = sda_node;
	scl_reg.d = scl_node;
	sda = sda_reg.q;
	scl = scl_reg.q;

	ss.reset = reset_ss;

	% Set up all clocks - coming off the clock divider %
	sda_reg.clk = clk_div.q;
	scl_reg.clk = clk_div.q;
	ss.clk = clk_div.q;
	delay_ctr.clock = clk_div.q;
	addr_ctr.clock = clk_div.q;

	TABLE
		addr_ctr.q[]	=>	db_out[8..1];
		
		% Next two addresses cause SAA7110 to be addressed %
		H"00"	=>	H"9C";	% Write address for the SAA7110; should be 9C %

		% Next entry is the subaddress of the SAA7110 we're going to write %
		%    Note that the subaddress gets automatically incremented upon %
		%    consecutive I^2C writes to the 7110 %
		H"01"	=>	H"00";  % Start at subaddress 00 %
		
		% Rest of the addresses are the data for the SAA7110 %
		H"02"	=>	H"4C";
		H"03"	=>	H"3C";
		H"04"	=>	H"0D";
		H"05"	=>	H"EF";
		H"06"	=>	H"BD";
		H"07"	=>	H"F0";
		H"08"	=>	H"00";
		H"09"	=>	H"00";
		H"0A"	=>	H"F8";
		H"0B"	=>	H"F8";
		H"0C"	=>	H"60";
		H"0D"	=>	H"60";
		H"0E"	=>	H"00";
		H"0F"	=>	H"06";
		H"10"	=>	H"18";  % was H"18" % % "98" causes PLL to be opened %
		H"11"	=>	H"90";  % was H"90" % % "50" causes NTSC mode to be fixed %
		H"12"	=>	H"00";
		H"13"	=>	H"2C";
		H"14"	=>	H"40";
		H"15"	=>	H"46";
		H"16"	=>	H"42";
		H"17"	=>	H"1A";
		H"18"	=>	H"FF";
		H"19"	=>	H"DA";
		H"1A"	=>	H"F0";
		H"1B"	=>	H"8B";
		H"1C"	=>	H"00";
		H"1D"	=>	H"00";
		H"1E"	=>	H"00";
		H"1F"	=>	H"00";
		H"20"	=>	H"00";
		H"21"	=>	H"00";
		H"22"	=>	H"D9";
		H"23"	=>	H"17";
		H"24"	=>	H"40";
		H"25"	=>	H"41";
		H"26"	=>	H"80";
		H"27"	=>	H"41";
		H"28"	=>	H"80";
		H"29"	=>	H"4F";
		H"2A"	=>	H"FE";
		H"2B"	=>	H"01";
		H"2C"	=>	H"CF";
		H"2D"	=>	H"0F";
		H"2E"	=>	H"03";
		H"2F"	=>	H"01";
		H"30"	=>	H"81";
		H"31"	=>	H"03";
		H"32"	=>	H"44";
		H"33"	=>	H"75";
		H"34"	=>	H"01";
		H"35"	=>	H"8C";
		H"36"	=>	H"03";

		% Now prepare to write value 16 to SAA7110 subaddress 21. Apparently %
		%    this ends the precharge period for the reference level clamping %
		%    capacitor. %
		H"37"	=>	H"9C";
		H"38"	=>	H"21";
		H"39"	=>	H"16";
	END TABLE;

	CASE ss IS
		WHEN ResetState =>
			busy = GND;
			IF (go) THEN
				ss = SendStart_0;
				delay_ctr.aclr = VCC;
				addr_ctr.aclr = VCC;
			ELSE
				ss = ResetState;
			END IF;
		WHEN SendStart_0 =>
			% Let the bus go high for the setup time of the START signal %
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_128] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendStart_1;
			ELSE
				ss = SendStart_0;
			END IF;
		WHEN SendStart_1 =>
			% Send the start signal on the I^2C bus. %
			sda_node = GND;
			% Hold this for ~128 clock cycles. %
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_128] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendBit8_0;
			ELSE
				ss = SendStart_1;
			END IF;

		WHEN SendBit8_0 =>
			% Pull clock line low %
			scl_node = GND;
			% Hold low for 128 clock cycles; get data line ready %
			sda_node = db_out[8];
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_128] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendBit8_1;
			ELSE
				ss = SendBit8_0;
			END IF;
		WHEN SendBit8_1 =>
			% Bring clock line back high %
			% Hold high for 128 clock cycles; maintain data %
			sda_node = db_out[8];
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_128] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendBit8_2;
			ELSE
				ss = SendBit8_1;
			END IF;
		WHEN SendBit8_2 =>
			% Bring clock line low again %
			scl_node = GND;
			% Hold time for data: ~8 clock cycles %
			sda_node = db_out[8];
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_8] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendBit7_0;
			ELSE
				ss = SendBit8_2;
			END IF;
		
		WHEN SendBit7_0 =>
			% Pull clock line low %
			scl_node = GND;
			% Hold low for 128 clock cycles; get data line ready %
			sda_node = db_out[7];
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_128] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendBit7_1;
			ELSE
				ss = SendBit7_0;
			END IF;
		WHEN SendBit7_1 =>
			% Bring clock line back high %
			% Hold high for 128 clock cycles; maintain data %
			sda_node = db_out[7];
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_128] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendBit7_2;
			ELSE
				ss = SendBit7_1;
			END IF;
		WHEN SendBit7_2 =>
			% Bring clock line low again %
			scl_node = GND;
			% Hold time for data: ~8 clock cycles %
			sda_node = db_out[7];
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_8] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendBit6_0;
			ELSE
				ss = SendBit7_2;
			END IF;

		WHEN SendBit6_0 =>
			% Pull clock line low %
			scl_node = GND;
			% Hold low for 128 clock cycles; get data line ready %
			sda_node = db_out[6];
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_128] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendBit6_1;
			ELSE
				ss = SendBit6_0;
			END IF;
		WHEN SendBit6_1 =>
			% Bring clock line back high %
			% Hold high for 128 clock cycles; maintain data %
			sda_node = db_out[6];
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_128] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendBit6_2;
			ELSE
				ss = SendBit6_1;
			END IF;
		WHEN SendBit6_2 =>
			% Bring clock line low again %
			scl_node = GND;
			% Hold time for data: ~8 clock cycles %
			sda_node = db_out[6];
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_8] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendBit5_0;
			ELSE
				ss = SendBit6_2;
			END IF;

		WHEN SendBit5_0 =>
			% Pull clock line low %
			scl_node = GND;
			% Hold low for 128 clock cycles; get data line ready %
			sda_node = db_out[5];
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_128] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendBit5_1;
			ELSE
				ss = SendBit5_0;
			END IF;
		WHEN SendBit5_1 =>
			% Bring clock line back high %
			% Hold high for 128 clock cycles; maintain data %
			sda_node = db_out[5];
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_128] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendBit5_2;
			ELSE
				ss = SendBit5_1;
			END IF;
		WHEN SendBit5_2 =>
			% Bring clock line low again %
			scl_node = GND;
			% Hold time for data: ~8 clock cycles %
			sda_node = db_out[5];
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_8] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendBit4_0;
			ELSE
				ss = SendBit5_2;
			END IF;

		WHEN SendBit4_0 =>
			% Pull clock line low %
			scl_node = GND;
			% Hold low for 128 clock cycles; get data line ready %
			sda_node = db_out[4];
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_128] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendBit4_1;
			ELSE
				ss = SendBit4_0;
			END IF;
		WHEN SendBit4_1 =>
			% Bring clock line back high %
			% Hold high for 128 clock cycles; maintain data %
			sda_node = db_out[4];
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_128] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendBit4_2;
			ELSE
				ss = SendBit4_1;
			END IF;
		WHEN SendBit4_2 =>
			% Bring clock line low again %
			scl_node = GND;
			% Hold time for data: ~8 clock cycles %
			sda_node = db_out[4];
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_8] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendBit3_0;
			ELSE
				ss = SendBit4_2;
			END IF;

		WHEN SendBit3_0 =>
			% Pull clock line low %
			scl_node = GND;
			% Hold low for 128 clock cycles; get data line ready %
			sda_node = db_out[3];
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_128] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendBit3_1;
			ELSE
				ss = SendBit3_0;
			END IF;
		WHEN SendBit3_1 =>
			% Bring clock line back high %
			% Hold high for 128 clock cycles; maintain data %
			sda_node = db_out[3];
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_128] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendBit3_2;
			ELSE
				ss = SendBit3_1;
			END IF;
		WHEN SendBit3_2 =>
			% Bring clock line low again %
			scl_node = GND;
			% Hold time for data: ~8 clock cycles %
			sda_node = db_out[3];
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_8] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendBit2_0;
			ELSE
				ss = SendBit3_2;
			END IF;

		WHEN SendBit2_0 =>
			% Pull clock line low %
			scl_node = GND;
			% Hold low for 128 clock cycles; get data line ready %
			sda_node = db_out[2];
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_128] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendBit2_1;
			ELSE
				ss = SendBit2_0;
			END IF;
		WHEN SendBit2_1 =>
			% Bring clock line back high %
			% Hold high for 128 clock cycles; maintain data %
			sda_node = db_out[2];
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_128] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendBit2_2;
			ELSE
				ss = SendBit2_1;
			END IF;
		WHEN SendBit2_2 =>
			% Bring clock line low again %
			scl_node = GND;
			% Hold time for data: ~8 clock cycles %
			sda_node = db_out[2];
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_8] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendBit1_0;
			ELSE
				ss = SendBit2_2;
			END IF;

		WHEN SendBit1_0 =>
			% Pull clock line low %
			scl_node = GND;
			% Hold low for 128 clock cycles; get data line ready %
			sda_node = db_out[1];
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_128] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendBit1_1;
			ELSE
				ss = SendBit1_0;
			END IF;
		WHEN SendBit1_1 =>
			% Bring clock line back high %
			% Hold high for 128 clock cycles; maintain data %
			sda_node = db_out[1];
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_128] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendBit1_2;
			ELSE
				ss = SendBit1_1;
			END IF;
		WHEN SendBit1_2 =>
			% Bring clock line low again %
			scl_node = GND;
			% Hold time for data: ~8 clock cycles %
			sda_node = db_out[1];
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_8] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = GetAck_0;
			ELSE
				ss = SendBit1_2;
			END IF;

		WHEN GetAck_0 =>
			% Hold clock line low for ~128 clock cycles; %
			% let data line float back high %
			scl_node = GND;
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_128] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = GetAck_1;
			ELSE
				ss = GetAck_0;
			END IF;
		WHEN GetAck_1 =>
			% Bring clock line high, wait for 64 clock cycles %
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_64] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = GetAck_2;
			ELSE
				ss = GetAck_1;
			END IF;
		WHEN GetAck_2 =>
			% At this point, the SDA bus should be pulled low by the %
			% slave receiver. Sample it, and fail if it isn't low %
			IF (sda_in == VCC) THEN
				ss = Error_NACK;
			ELSE
				delay_ctr.sclr = VCC;
				ss = GetAck_3;
			END IF;
		WHEN GetAck_3 =>
			% Wait for another 64 clock cycles %
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_64] == 1) THEN
				delay_ctr.sclr = VCC;
				IF ((addr_ctr.q[] == H"36") #
					(addr_ctr.q[] == H"39")) THEN
					ss = SendStop_0;
				ELSE
					addr_ctr.cnt_en = VCC;
					ss = SendBit8_0;
				END IF;
			ELSE
				ss = GetAck_3;
			END IF;

		WHEN SendStop_0 =>
			% Pull clock line low %
			scl_node = GND;
			% Hold low for 64 clock cycles %
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_64] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendStop_1;
			ELSE
				ss = SendStop_0;
			END IF;
		WHEN SendStop_1 =>
			% Pull clock line low, bring SDA low %
			scl_node = GND;
			sda_node = GND;
			% Hold low for 64 clock cycles %
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_64] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendStop_2;
			ELSE
				ss = SendStop_1;
			END IF;
		WHEN SendStop_2 =>
			% Raise SCL without raising SDA; hold for 128 clock cycles %
			sda_node = GND;
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_128] == 1) THEN
				delay_ctr.sclr = VCC;
				ss = SendStop_3;
			ELSE
				ss = SendStop_2;
			END IF;
		WHEN SendStop_3 =>
			% Now raise SDA. Hold idle state for 128 clock cycles. %
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[PLACE_128] == 1) THEN
				delay_ctr.sclr = VCC;
				% Magic Number. If we're not done, start next sequence %
				IF (addr_ctr.q[] == H"36") THEN
					addr_ctr.cnt_en = VCC;
					ss = SendStart_0;
				ELSE
					ss = Done;
				END IF;
			ELSE
				ss = SendStop_3;
			END IF;
			
		WHEN Done =>
			busy = GND;
			done = VCC;
			ss = Done;

		WHEN Error_NACK =>
			error = VCC;
			ss = Error_NACK;
	END CASE;			
END;