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

INCLUDE "lpm_counter.inc";

% Relatively simple FSM which implements two functions for the ZR36050 %
% JPEG compressor: initialization of the chip's registers and generation %
% of the GO signal which initiates a compression operation. %

SUBDESIGN jpeg_ss
(
	% Inputs %
	/clk		: INPUT;	% Source = LLC. ~80ns clock cycle. %
	reset_ss	: INPUT;
	init		: INPUT;
	go			: INPUT;

	% Outputs %
	busy		: OUTPUT;
	clken		: OUTPUT;
	addr[10..1]	: OUTPUT;
	/wr			: OUTPUT;
	/cs			: OUTPUT;
	/reset		: OUTPUT;	
	/oe			: OUTPUT;	% Output enable AND chip select for the DATA 28F256A %
)
VARIABLE
	ss			: MACHINE WITH STATES (
		ResetState,
		EnableClock,
		IdleState_0,
		Init_0, Init_1, Init_2, Init_3, Init_4,
		Go_0, Go_1, Go_2, Go_3
	);
	addr_ctr	: lpm_counter WITH (
		LPM_WIDTH = 10,	% Big enough to enumerate all table entries %
		LPM_DIRECTION = "UP"
	);
	delay_ctr	: lpm_counter WITH (
		LPM_WIDTH = 13,		% Big enough to count to 5000, to allow PLL %
							% to stabilize after CLKEN is asserted high %
		LPM_DIRECTION = "UP"
	);
BEGIN
	DEFAULTS
		clken = VCC;	% This is pulled low once, then set high thereafter %
		busy = VCC;
		/cs = VCC;
		/wr = VCC;
		/reset = VCC;
		/oe = VCC;
	END DEFAULTS;

	ss.reset = reset_ss;
	ss.clk = /clk;
	addr_ctr.clock = /clk;
	delay_ctr.clock = /clk;

	addr[] = addr_ctr.q[];
	addr_ctr.data[] = H"1";	% So we can load the address counter to begin %
							% initializing the registers %
	
	CASE ss IS
		WHEN ResetState =>
			% This assumes we stay in the reset state for a few clock %
			% cycles upon bootup, so CLKEN stays low for the required time %
			busy = GND;
			delay_ctr.aclr = VCC;
			clken = GND;
			ss = EnableClock;
		WHEN EnableClock =>
			delay_ctr.cnt_en = VCC;
			busy = GND;		% Don't mislead the main FSM into thinking %
							% we're handling the "init" command yet %
			IF (delay_ctr.q[] == H"1388") THEN	% 5000 clock cycles %
%			IF (delay_ctr.q[] == H"5") THEN	%	% Testing %
				delay_ctr.sclr = VCC;
				ss = IdleState_0;
			ELSE
				ss = EnableClock;
			END IF;
		WHEN IdleState_0 =>
			busy = GND;
			delay_ctr.aclr = VCC;
			IF (init == VCC) THEN
				addr_ctr.sload = VCC;
				ss = Init_0;
			ELSIF (go == VCC) THEN
				addr_ctr.aclr = VCC;
				ss = Go_0;
			ELSE
				ss = IdleState_0;
			END IF;
			
		% Initialization sequence %
		WHEN Init_0 =>
			% Assert /RESET for four clock cycles %
			/reset = GND;			
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[] == H"03") THEN
				/oe = GND;
				ss = Init_1;
			ELSE
				ss = Init_0;
			END IF;
		WHEN Init_1 =>
			% Assert /CS; correct address is already on the bus %
			/cs = GND;
			% Drive the data into the Zoran's data bus %
			/oe = GND;
			delay_ctr.aclr = VCC;
			ss = Init_2;
		WHEN Init_2 =>
			% Assert /CS and /WR; hold /WR for 3 clock cycles %
			/cs = GND;
			/wr = GND;
			/oe = GND;
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[] == H"02") THEN
				ss = Init_3;
			ELSE
				ss = Init_2;
			END IF;
		WHEN Init_3 =>
			% Maintain the data and /CS for a cycle, to be safe %
			/oe = GND;
			/cs = GND;
			addr_ctr.cnt_en = VCC;
			% *** NOTE *** Nasty inter-chip dependency here! %
			% The address/data tables have 0x258 entries in them. %
			% These tables are stored in off-chip 28F256As (EEPROMs). %
			IF (addr_ctr.q[] == H"258") THEN		% Last entry in table %
				ss = IdleState_0;
			ELSE
				delay_ctr.aclr = VCC;
				ss = Init_4;
			END IF;
		WHEN Init_4 =>
			% No-op state to allow /CS to go high and the Intel %
			% EEPROMs to stabilize their outputs % 
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[] == H"02") THEN
				ss = Init_4;
			ELSE
				ss = Init_1;
			END IF;
		
		% Generation of GO signal for ZR36050 %
		WHEN Go_0 =>
			% Don't take any chances. Let the addresses stabilize %
			% coming out of the 28F256A %
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[] == H"02") THEN
				ss = Go_1;
			ELSE
				ss = Go_0;
			END IF;
		WHEN Go_1 =>
			/cs = GND;
			delay_ctr.aclr = VCC;
			ss = Go_2;
		WHEN Go_2 =>
			/cs = GND;
			/wr = GND;
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[] == H"02") THEN
				ss = Go_3;
			ELSE
				ss = Go_2;
			END IF;
		WHEN Go_3 =>
			/cs = GND;
			ss = IdleState_0;
	END CASE;
END;