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

INCLUDE "lpm_counter.inc";
INCLUDE "pc_const.inc";
INCLUDE "dram_refresh.inc";

% The top-level file for the main control logic, including DRAM %
% refresh circuitry. This was going to be just the main state %
% machine, but it would have been so much effort to write a %
% wrapper file just to register a few inputs that I decided to %
% modify this file. Mea culpa. %

SUBDESIGN main_ss
(
	%--------%
	% INPUTS %
	%--------%
	
	% Clocks %
	/clk		: INPUT;	% Main system clock %
	/clk2		: INPUT;	% Half speed clock %
	
	% The main commands %
	digitize	: INPUT;
	transmit	: INPUT;

	% Status inputs from SAA7110 (digitizer) %
	s7_vs		: INPUT;	% Vertical sync: high during vert. blanking %
	s7_href		: INPUT;	% Horiz. sync; high when valid data on YUV bus %
	/s7_reset	: INPUT;	% Master RESET signal generated by 7110. %
							% We will be responsible for distributing this %
							% to the other FSMs in the board %
	
	% Status input from the 7110Init chip %
	7i_busy		: INPUT;
	
	% Status input from the UARTCtl chip %
	uc_busy		: INPUT;
	
	% Status input from the JPEGCtl chip %
	jc_busy		: INPUT;
	
	% Status inputs from the ZR36050 chip %
	/zr_stop	: INPUT;	% Stop sending data to JPEG chip %
	/zr_end		: INPUT;	% Normal end of compression %
	/zr_dreq	: INPUT;	% Request by ZR36050 to write to DRAM %
	/zr_int		: INPUT;	% Unnecessary? %
	
	% Status inputs from counters %
	cj_full		: INPUT;
	c12_equal	: INPUT;
	% The next two pins are connected to the q[2] and q[1] pins of the %
	% counter's 13-bit address port, in that order. %
	ct_hwctr	: INPUT;	% "Half word" counter %
	ct_bytectr	: INPUT;	% Byte counter %
	
	% Data Terminal Ready signal, to let us know whether %
	% the machine is ready to receive data at all %
	/dtr		: INPUT;

	%---------%
	% OUTPUTS %
	%---------%
	
	% To SAA7110 %
	/s7_fein	: OUTPUT;	% Drive YUV data onto data bus %
	
	% To 7110Init %
	7i_reset	: OUTPUT;
	7i_go		: OUTPUT;
	
	% To UARTCtl %
	uc_reset	: OUTPUT;
	uc_init		: OUTPUT;
	uc_send		: OUTPUT;
	uc_hi_sel	: OUTPUT;
	
	% To DRAM bus transceivers (RB = RAM Bus) %
	% High two transceivers (HIHBUS, HILBUS): %
	rb_hiclk	: OUTPUT;	% Connected to CLKAB, CLKBA %
	rb_histo	: OUTPUT;	% Connected to SAB, SBA %
	rb_hidir	: OUTPUT;	% Connected to DIR %
	/rb_hioe	: OUTPUT;
	% Low two transceivers (LOHBUS, LOLBUS): %
	rb_loclk	: OUTPUT;	% Connected to CLKAB, CLKBA %
	rb_losto	: OUTPUT;	% Connected to SAB, SBA %
	rb_lodir	: OUTPUT;	% Connected to DIR %
	/rb_looe	: OUTPUT;
	
	% To DRAM %
	/cas, /ras	: OUTPUT;	% These are registered. /dr_ras and /dr_cas %
							% are combinatoric nodes in the variable section %
	/dr_we		: OUTPUT;	% This is combinatoric %

	% To JPEG bus transceivers %
	/jb_oeih	: OUTPUT;	% Output enable for high byte of input %
	/jb_oeil	: OUTPUT;	% Output enable for low byte of input %
	/jb_oeo		: OUTPUT;	% Output enable for both bytes of output %
	
	% To JPEGCtl %
	jc_reset	: OUTPUT;	% Reset JPEGCtl chip %
	jc_init		: OUTPUT;	% Initialize ZR36050s registers %
	jc_go		: OUTPUT;	% Send GO signal to ZR36050 %
	
	% To ZR36050 %
	/zr_dsync	: OUTPUT;	% Activated at start of data block %
	/zr_eos		: OUTPUT;	% End Of Scan (end of 8x8 data block) %
	/zr_dack	: OUTPUT;	% Acknowledge for DMA data transfer %
							% of compressed data %
	/zr_rd		: OUTPUT;	% Read data during DMA transfer %

	% To digitizing counters %
	% (These are also used to address the DRAM during compression; %
	% this is wasteful, since they waste (256-160) out of every 256 %
	% column addresses, but uses less circuitry. %
	% Commented out lines below are outputs which are never used. %
	% Those enable inputs must be grounded in the counter chip %
	c1_aclr		: OUTPUT;
	c1_cnt_en	: OUTPUT;
%	c1_cnt2_en	: OUTPUT; %
%	c1_cnt4_en	: OUTPUT; %
	c1_sload	: OUTPUT;
	c2_aclr		: OUTPUT;
%	c2_cnt_en	: OUTPUT; %
	c2_cnt2_en	: OUTPUT;
	c2_cnt4_en	: OUTPUT;
	c2_sload	: OUTPUT;
	cj_aclr		: OUTPUT;
	cj_cnt_en	: OUTPUT;
	ctr_sel[3..1]	: OUTPUT;
	
	% Debugging outputs %
	debug_sel[2..1]	: OUTPUT;
)
VARIABLE
	ss			: MACHINE WITH STATES (
		ResetState,
		InitAuxCtrls_0, InitAuxCtrls_1, InitAuxCtrls_2, InitAuxCtrls_3, 
		IdleState,
		Digitize_0, Digitize_1, Digitize_2, Digitize_3, Digitize_4, Digitize_5, Digitize_6, 
			Digitize_7, Digitize_8, Digitize_9, Digitize_10,
		Transmit_0, Transmit_1, Transmit_2, Transmit_3
	);
	field_ctr	: lpm_counter WITH (
		LPM_WIDTH = 5,	% Needs to be able to count to NUM_FIELDS %
		LPM_DIRECTION = "UP"
	);
	line_ctr	: lpm_counter WITH (
		LPM_WIDTH = 8,	% Needs to be able to count to NUM_LINES %
		LPM_DIRECTION = "UP"
	);
	pixel_ctr	: lpm_counter WITH (
		LPM_WIDTH = 8,	% Needs to be able to count to PIXELS_PER_ROW. %
						% Note that there are 160 pixels per line (though there %
						% are 2 luminance components), and those are what this %
						% counter is counting. %
		LPM_DIRECTION = "UP"
	);
	delay_ctr	: lpm_counter WITH (
		LPM_WIDTH = 4,
		LPM_DIRECTION = "UP"
	);
	
	% Synchronizers for incoming control signals from the real world %
	digitize_sync	: DFF;
	transmit_sync	: DFF;

	% DRAM refresh related variables %
	rfss		: dram_refresh;		% The actual state machine %
	rf_disable	: NODE;	% Disables the on-chip refresh circuitry %
	/dr_cas		: NODE;	% These are combinatoric nodes %
	/dr_ras		: NODE;

	% Synchronizer for the /DTR signal %
	/dtr_sync	: DFF;
BEGIN
	DEFAULTS
		rf_disable = VCC;	% By default, DISABLE refresh circuitry. %
							% During the action, we are not going to want %
							% to have to remember to disable this %
		/dr_ras = VCC;
		/dr_cas = VCC;
		/dr_we = VCC;

		% Make sure we don't cause any bus contention by accident %
		% and fry the SAA7110 %
		/s7_fein = VCC;

		rb_hidir = VCC;		% The DRAM's bus defaults to driving data from %
		rb_lodir = VCC;		% the main data bus into the DRAM %
		/rb_hioe = VCC;
		/rb_looe = VCC;
		rb_histo = GND;		% These two are currently unused %
		rb_losto = GND;

		/zr_dsync = VCC;
		/zr_eos = VCC;
		/zr_dack = VCC;
		/zr_rd = VCC;

		/jb_oeih = VCC;
		/jb_oeil = VCC;
		/jb_oeo = VCC;

		% Defaults to allow now unused nodes to have values %
		jc_init = GND;
		jc_go = GND;
		c1_sload = GND;
		c2_cnt2_en = GND;
		c2_sload = GND;
		cj_cnt_en = GND;
	END DEFAULTS;

	% Hook up the master reset signal %
	ss.reset = (!/s7_reset);
	
	% All sequential devices in this chip are driven off the master clock %
	ss.clk = /clk;
	delay_ctr.clock = /clk;
	field_ctr.clock = /clk;
	line_ctr.clock = /clk;
	pixel_ctr.clock = /clk;

	% Synchronized versions of control signals %
	digitize_sync.d = digitize;
	digitize_sync.clk = /clk;
	transmit_sync.d = transmit;
	transmit_sync.clk = /clk;

	% Refresh circuitry logic. %
	rfss.clk = /clk;
	rfss.reset = (!/s7_reset);
	rfss.disable = rf_disable;
	/ras = DFF((rfss./ras & /dr_ras), /clk, VCC, VCC);
	/cas = DFF((rfss./cas & /dr_cas), /clk, VCC, VCC);

	% Synchronizer for /DTR %
	/dtr_sync.d = /dtr;
	/dtr_sync.clk = /clk;
	
	CASE ss IS
		%                      %
		% Initialization steps %
		%                      %
		WHEN ResetState =>
			7i_reset = VCC;
			uc_reset = VCC;
			jc_reset = VCC;
			ss = InitAuxCtrls_0;
		% Handshake the reset signals. %
		% We do each chip separately to avoid missing any one's busy %
		% signal. For example, the JPEG controller takes a very long time %
		% to get ready after the reset signal has been asserted. %
		WHEN InitAuxCtrls_0 =>
			% Initialize the 7110 first %
			7i_go = VCC;
			IF (7i_busy == VCC) THEN
				ss = InitAuxCtrls_1;
			ELSE
				ss = InitAuxCtrls_0;
			END IF;
		WHEN InitAuxCtrls_1 =>
			% Now get the UART controller started %
			uc_init = VCC;
			IF (uc_busy == VCC) THEN
				ss = InitAuxCtrls_2;
			ELSE
				ss = InitAuxCtrls_1;
			END IF;
		WHEN InitAuxCtrls_2 =>
			% Skip JPEG chip initialization %
			ss = InitAuxCtrls_3;
		WHEN InitAuxCtrls_3 =>
			IF ((7i_busy == VCC) # (uc_busy == VCC) # (jc_busy == VCC)) THEN
				ss = InitAuxCtrls_3;
			ELSE
				ss = IdleState;
			END IF;
		
		% Idle state: refresh DRAM, wait for commands. %
		WHEN IdleState =>
			% Debugging outputs %
			debug_sel[] = H"1";
			rf_disable = GND;
			IF (digitize_sync.q == VCC) THEN
				% Digitization request. To initialize, clear out counters. %
				field_ctr.aclr = VCC;
				c1_aclr = VCC;
				c2_aclr = VCC;
				cj_aclr = VCC;
				ss = Digitize_0;
			ELSIF (transmit_sync.q == VCC) THEN
				c1_aclr = VCC;
				delay_ctr.aclr = VCC;
				ss = Transmit_0;
			ELSE
				ss = IdleState;
			END IF;
		
		% Digitization loop %
		WHEN Digitize_0 =>
			% Debugging output %
			debug_sel[] = H"2";
			% Refresh during vertical retrace %
			rf_disable = GND;
			% Synchronize to start of field %
			IF (s7_vs == GND) THEN
				ss = Digitize_0;
			ELSE
				ss = Digitize_1;
			END IF;
		WHEN Digitize_1 =>
			% Debugging output %
			debug_sel[] = H"2";
			rf_disable = GND;
			IF (s7_vs == VCC) THEN
				ss = Digitize_1;
			ELSE
				line_ctr.aclr = VCC;
				ss = Digitize_2;
			END IF;
		WHEN Digitize_2 =>
			% Debugging output %
			debug_sel[] = H"2";
			% Enable YUV pixel data on the data bus %
			/s7_fein = GND;
			% Sync to rising edge of HREF. %
			rf_disable = GND;
			pixel_ctr.aclr = VCC;
			IF (s7_href == VCC) THEN
				ss = Digitize_2;
			ELSE
				ss = Digitize_3;
			END IF;
		WHEN Digitize_3 =>
			% Debugging output %
			debug_sel[] = H"2";
			% Enable YUV pixel data on the data bus %
			/s7_fein = GND;
			IF (s7_href == GND) THEN
				rf_disable = GND;
				ss = Digitize_3;
			ELSE
				% Stop refreshing the DRAM now %
				delay_ctr.aclr = VCC;
				ss = Digitize_4;
			END IF;
		WHEN Digitize_4 =>
			% Debugging output %
			debug_sel[] = H"2";
			% Enable YUV pixel data on the data bus %
			/s7_fein = GND;
			% Okay. When we enter this state, pixel 0's data (i.e., Y0/U0) %
			% will be coming out of the 7110 for the second clock cycle. %
			% We need to remain in this state for THREE clock cycles. %
			
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[] == H"3") THEN
				delay_ctr.sclr = VCC;
				ss = Digitize_5;
			ELSE
				ss = Digitize_4;
			END IF;
		WHEN Digitize_5 =>
			% Debugging output %
			debug_sel[] = H"2";
			% Enable YUV pixel data on the data bus %
			/s7_fein = GND;
			% *** NOTE *** VERY IMPORTANT *** %
			% This is actually where the data ordering in the DRAM is decided. %
			% We could store the YU pair in EITHER the low or high data bits by %
			% changing which register we clock. Here we make the CONVENTION that %
			% we store the YU pair in the **HIGH** 16 bits and the YV pair in the %
			% **LOW** 16 bits. This is VERY important to remember during the JPEG %
			% compression. This is a BIG ENDIAN convention. Observe the following %
			% data chart: %
			% BIT:  33222222222211111111110000000000   %
			%       10987654321098765432109876543210   %
			% DAYA: YYYYYYYYUUUUUUUUYYYYYYYYVVVVVVVV   %
			%       76543210765432107654321076543210   %
			% Rationale: this data storage method is probably easiest for humans to %
			% visualize, or at least those used to a numerical system where the most %
			% significant places are on the left. %

			% NOTE: The YUV data shows up on the data bus for two of our clock %
			% cycles, but is changing at the beginning of the first cycle. Right %
			% now this code is designed to latch the data sometime during the %
			% first cycle, but it might be safer to wait until the next clock cycle %
			% (and we'll have to if we find we're getting garbage data). To implement %
			% this delay, we'll just have to stay in state Digitize_4 for another clock %
			% cycle; the delay times below do not change. We have a total of eight %
			% clock cycles per pair of pixels (since we skip every other pair). %
			delay_ctr.cnt_en = VCC;
			% Grab this pixel with the HIGH register, and pause 1 clock cycle. %
			IF (delay_ctr.q[] == H"0") THEN
				rb_hiclk = VCC;
				ss = Digitize_5;
			ELSE
				ss = Digitize_6;
			END IF;
		WHEN Digitize_6 =>
			% Debugging output %
			debug_sel[] = H"2";
			% Enable YUV pixel data on the data bus %
			/s7_fein = GND;
			% Grab the next pixel (YV pair) with the LOW register %
			rb_loclk = VCC;
			ss = Digitize_7;
		WHEN Digitize_7 =>
			% Debugging output %
			debug_sel[] = H"2";
			% Enable YUV pixel data on the data bus %
			/s7_fein = GND;
			% Prepare to write the 32-bit word into DRAM. %
			% Note that at this point we have FIVE clock cycles before %
			% we have to jump back into the Digitize_5 state. %
			
			% FIRST clock cycle. Select the row address of counter 2 %
			% as the address into the DRAM. This will be stable at the %
			% NEXT rising clock edge, and hopefully will be held for 10 ns %
			% afterward. Also assert /RAS, which will be registered and %
			% asserted low at the next rising clock edge. %
			ctr_sel[] = H"2";
			/dr_ras = GND;
			ss = Digitize_8;
		WHEN Digitize_8 =>
			% Debugging output %
			debug_sel[] = H"2";
			% Enable YUV pixel data on the data bus %
			/s7_fein = GND;
			% SECOND clock cycle. Select the column address of counter 2 %
			% as the DRAM address. Assert both /RAS and /CAS, and ALSO assert %
			% /WE, which will go high sometime before /CAS. Assert the output %
			% enables of the two transceivers. Note that the transceivers default %
			% to driving data into the DRAM. %
			ctr_sel[] = H"3";
			/dr_ras = GND;
			/dr_cas = GND;
			/dr_we = GND;
			/rb_looe = GND;
			/rb_hioe = GND;
			delay_ctr.aclr = VCC;
			ss = Digitize_9;
		WHEN Digitize_9 =>
			% Debugging output %
			debug_sel[] = H"2";
			% Enable YUV pixel data on the data bus %
			/s7_fein = GND;
			% Hold down /WE one more cycle after /CAS goes low; %
			% enable data into DRAM during this time %
			/dr_we = GND;
			/rb_looe = GND;
			/rb_hioe = GND;
			% THIRD clock cycle. We're done with the DRAM write! Let % 
			% everything go high, and increment counter 2's address by 4. %
			% Increment pixel and/or line counters. %
			c2_cnt4_en = VCC;
			IF ((pixel_ctr.q[] == (PIXELS_PER_ROW - 1)) &
				(line_ctr.q[] == (NUM_LINES - 1)) &
				(field_ctr.q[] == (NUM_FIELDS - 1))) THEN
				% Digitization complete. %
				% Don't do JPEG compression %
				ss = IdleState;
			ELSIF ((pixel_ctr.q[] == (PIXELS_PER_ROW - 1)) &
				   (line_ctr.q[] == (NUM_LINES - 1))) THEN
				% End of field. Increment field counter, %
				% wait for next field (and do DRAM refresh) %
				field_ctr.cnt_en = VCC;
				ss = Digitize_0;
			ELSIF (pixel_ctr.q[] == (PIXELS_PER_ROW - 1)) THEN
				% End of line. Increment line counter, wait %
				% for next HREF. %
				line_ctr.cnt_en = VCC;
				ss = Digitize_2;
			ELSE
				% Increment pixel counter %
				pixel_ctr.cnt_en = VCC;
				% Wait for two more clock cycles %
				delay_ctr.aclr = VCC;
				ss = Digitize_10;
			END IF;
		WHEN Digitize_10 =>
			% Debugging output %
			debug_sel[] = H"2";
			% Enable YUV pixel data on the data bus %
			/s7_fein = GND;
			% Pause for two clock cycles, then fall back into %
			% the Digitize_5 state (to grab the next pair of pixels). %
			delay_ctr.cnt_en = VCC;
			IF (delay_ctr.q[] == H"1") THEN
				delay_ctr.sclr = VCC;
				ss = Digitize_5;
			ELSE
				ss = Digitize_10;
			END IF;
		
		% Transmission loop. %
		% Uses counter 1 as the index; compares to counter 2 for termination %
		% condition. %
		WHEN Transmit_0 =>
			% Debugging output %
			debug_sel[] = H"3";
			% Wait for DRAM refresh to turn off for sure %
			delay_ctr.cnt_en = VCC;
			ctr_sel[] = H"0";
			IF (delay_ctr.q[] == H"4") THEN
				/dr_ras = GND;
				ss = Transmit_1;
			ELSE
				ss = Transmit_0;
			END IF;
		WHEN Transmit_1 =>
			% Debugging output %
			debug_sel[] = H"3";
			% Drive the next 2 bytes onto the data bus %
			ctr_sel[] = H"1";
			/dr_ras = GND;
			/dr_cas = GND;
			IF (ct_hwctr == VCC) THEN
				/rb_looe = GND;
				rb_lodir = GND;
			ELSE
				/rb_hioe = GND;
				rb_hidir = GND;
			END IF;
			% Wait one more clock cycle in this state? %
			ss = Transmit_2;
		WHEN Transmit_2 =>
			% Debugging output %
			debug_sel[] = H"3";
			ctr_sel[] = H"1";
			/dr_ras = GND;
			/dr_cas = GND;
			uc_send = VCC;
			IF (ct_hwctr == VCC) THEN
				/rb_looe = GND;
				rb_lodir = GND;
			ELSE
				/rb_hioe = GND;
				rb_hidir = GND;
			END IF;
			% Big-endian byte order, so we send the highest byte first %
			% (when the byte counter is clear) %
			IF (ct_bytectr == VCC) THEN
				uc_hi_sel = GND;
			ELSE
				uc_hi_sel = VCC;
			END IF;	
			IF (uc_busy == GND) THEN
				ss = Transmit_2;
			ELSE
				c1_cnt_en = VCC;
				ss = Transmit_3;
			END IF;
		WHEN Transmit_3 =>
			% Debugging output %
			debug_sel[] = H"3";
			% Start up the DRAM refresh during transmission %
			rf_disable = GND;
			IF (uc_busy == VCC) THEN
				ss = Transmit_3;
			END IF;
			% *** NOTE *** This requires c2 to actually point to the %
			% NEXT FREE WORD/HALFWORD after the compressed or uncompressed %
			% picture data. %
			% Watching /DTR allows us to jump back to the idle state if the host %
			% machine hung up. %
			% ** NOTE ** /dtr detection disabled for now, doesn't seem to get set %
			% by the HP %
%			IF (((uc_busy == GND) & %
%				 (c12_equal == VCC)) # %
%				/dtr_sync.q == VCC) THEN %
%				ss = IdleState; %
%			END IF; %

			% DEBUG: uc_busy ignored %
%			IF (c12_equal == GND) THEN %
%				ss = Transmit_0; %
%			ELSE %
%				ss = IdleState; %
%			END IF; %

			IF ((uc_busy == GND) & (c12_equal == GND)) THEN
				ss = Transmit_0;
			END IF;
			IF ((uc_busy == GND) &
				(c12_equal == VCC)) THEN
				ss = IdleState;
			END IF;
	END CASE;
END;