
-- Version: G3SOC 6.3.0.0

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
library fusion;


entity RTC_calendar is

	port(SYS_RESET 	: in std_logic; 
		SYS_CLOCK 	: in std_logic;     -- ACM clock - 10Mhz
		INIT_DONE 	: in std_logic;     -- this module is active when Init_done = '1'
		POWER_down	: in std_logic;	-- assume async to 10Mhz clock
		clk1hz		: in std_logic;
				
          ACMRDATA    	: in std_logic_vector(7 downto 0);
          ACMADDR		: out std_logic_vector(7 downto 0);
          ACMWDATA  	: out std_logic_vector(7 downto 0);
	     ACMWEN		: out std_logic;
	-- used to initialize the RTC_calendar 	 
		LOAD_rtc		: in std_logic;	-- assume async to 10Mhz clock   		 
	     LOAD_year		: in std_logic_vector(7 downto 0);
	     LOAD_month	: in std_logic_vector(7 downto 0);
	     LOAD_date		: in std_logic_vector(7 downto 0);
	     LOAD_day		: in std_logic_vector(7 downto 0);
	     LOAD_hours	: in std_logic_vector(7 downto 0);
	     LOAD_minutes	: in std_logic_vector(7 downto 0);
	     LOAD_seconds	: in std_logic_vector(7 downto 0);	
  	     	     	     
	     RTC_act		: out std_logic;
	     RTC_year		: out std_logic_vector(7 downto 0);
	     RTC_month		: out std_logic_vector(7 downto 0);
	     RTC_date		: out std_logic_vector(7 downto 0);
	     RTC_day		: out std_logic_vector(7 downto 0);
	     RTC_hours		: out std_logic_vector(7 downto 0);
	     RTC_minutes	: out std_logic_vector(7 downto 0);
	     RTC_seconds	: out std_logic_vector(7 downto 0)	     	     	     	     	     
       
        );

end RTC_calendar;

architecture DEF_ARCH of RTC_calendar is

component rip_count32
    port( Data 	: in std_logic_vector(31 downto 0);
    		Enable	: in std_logic; 
    		Sload	: in std_logic; 
        	Aclr		: in std_logic; 
        	Clock 	: in std_logic; 
        	Q 		: out std_logic_vector(31 downto 0); 
        	Tcnt 	: out std_logic
        	);
end component;

--Initialize the counter to hardwired value of Tues. Jan 17th 2006 at 09:30:10 am
signal  count_year		: std_logic_vector(7 downto 0);
signal  count_month		: std_logic_vector(7 downto 0);
signal  count_date		: std_logic_vector(7 downto 0);
signal  count_day		: std_logic_vector(7 downto 0);
signal  count_hours		: std_logic_vector(7 downto 0);
signal  count_minutes	: std_logic_vector(7 downto 0);
signal  count_seconds	: std_logic_vector(7 downto 0); 			    
signal  RTC_count 		: std_logic_vector(31 downto 0); 
signal  RTC_load 		: std_logic_vector(31 downto 0); 
signal  RTC_match 		: std_logic_vector(39 downto 0);

signal  reg_loadrtc		: std_logic;
signal  rr_loadrtc		: std_logic; 
signal  rreg_loadrtc	: std_logic;
signal  reg_pwrdwn		: std_logic;
signal  rr_pwrdwn		: std_logic;
signal  match_ld		: std_logic_vector(4 downto 0);
signal  count_ld		: std_logic_vector(4 downto 0);
signal  rtc_ld			: std_logic;
signal  rtc_init		: std_logic;
signal  rtc_act_int		: std_logic;
signal  reg_INIT_done	: std_logic;  -- register the value of the INIT_done      
signal  reg_rtc7		: std_logic;
signal  inc_sec		: std_logic;  
signal  trigger		: std_logic;
signal  inc_min		: std_logic;
signal  inc_hour		: std_logic;
signal  inc_day		: std_logic;
signal  inc_month		: std_logic;
signal  inc_year		: std_logic;
signal  numdays_month	: std_logic_vector(7 downto 0);
signal  leap_year		: std_logic;
signal  dec_rtc	     : std_logic;
signal  sec_flag	     : std_logic;
signal  clr_sec_flag     : std_logic;
signal  rtc_eq0		: std_logic;
--signal  r_toggle		: std_logic;


type RTCcal_type is (idle,match_r0a,match_r0b,match_r1,match_r2,match_r3,match_r4,match_r5,
				 count_r0a,count_r0b,count_r1,count_r2,count_r3,count_r4,count_r5,
                     branch,load_rtc0,load_rtc1,rtc_active,pwrdwn_active0,pwrdwn_active1,
                     pwrdwn_active2,pwrdwn_active3,pwrdwn_active4,pwrdwn_active5,
                     pwrdwn_active6,elapsed_time0,pwrdwn_recovery,init_rtc);

signal RTCcal_present : RTCcal_type;
signal RTCcal_next    : RTCcal_type;

begin

	RTC_act		<= rtc_act_int;
	RTC_year		<= count_year;		
	RTC_month		<= count_month;		
	RTC_date		<= count_date;		
	RTC_day		<= count_day;		     
	RTC_hours		<= count_hours;		
	RTC_minutes    <= count_minutes;       
	RTC_seconds    <= count_seconds;     
	
--	toggle		<= r_toggle;
	 	  
--
--********************************************************************************
-- RTC state machine
--********************************************************************************
--	
-- Async. portion of the RTC calendar state machine
--

process (INIT_done, RTCcal_present, rr_loadrtc, rr_pwrdwn, reg_INIT_done,
         rtc_match(39), count_seconds, count_minutes, count_hours, count_day,
		 count_date, count_month, count_year, sec_flag, rtc_eq0)
begin

	ACMADDR		<= x"00";
	ACMWDATA  	<= x"00";
	ACMWEN		<= '0';
	match_ld		<= "00000"; --
	count_ld		<= "00000"; -- 
	rtc_ld		<= '0'; --
	rtc_init		<= '0'; --
	dec_rtc		<= '0';
	trigger		<= '0';
	clr_sec_flag	<= '0';
	rtc_act_int	<= '0';
--	RTC_idle		<= '0';


	case RTCcal_present is

		when idle =>
--			RTC_idle		<= '1';
			if (INIT_done = '1') and (reg_INIT_done = '0') then 
			-- or (rr_loadrtc = '1') then
				RTCcal_next	<= init_rtc;
			else									
				RTCcal_next 	<= idle;
			end if;
		when init_rtc	=>
			ACMADDR		<= x"58";
			ACMWDATA  	<= x"4B";
			ACMWEN		<= '1';
			RTCcal_next	<= match_r0a;							
		when match_r0a => 
			ACMADDR		<= x"48"; 
			RTCcal_next	<= match_r0b;
		when match_r0b => 
			ACMADDR		<= x"48"; 
			RTCcal_next	<= match_r1;					  
		when match_r1 => 
			ACMADDR		<= x"49"; 
			RTCcal_next	<= match_r2;
			match_ld		<= "00001";
		when match_r2 => 
			ACMADDR		<= x"4a"; 
			RTCcal_next	<= match_r3;
			match_ld		<= "00010";
		when match_r3 => 
			ACMADDR		<= x"4b"; 
			RTCcal_next	<= match_r4;
			match_ld		<= "00100";
		when match_r4 => 
			ACMADDR		<= x"4c"; 
		     RTCcal_next	<= match_r5;
			match_ld		<= "01000";
		when match_r5 => 
			RTCcal_next	<= branch;
			match_ld		<= "10000";

--AW: ??					
		when branch   =>
			if (rtc_match(39) = '1') then  
				RTCcal_next	<= pwrdwn_recovery;
			else 
				RTCcal_next	<= load_rtc0;  -- do auto load
			end if;
				
		when load_rtc0 =>	
			RTCcal_next	<= load_rtc1;
			rtc_ld		<= '1'; 
			ACMADDR		<= x"58";		-- reset the RTC counter
			ACMWDATA  	<= x"00";
			ACMWEN		<= '1';  
		when load_rtc1	=>
			RTCcal_next	<= rtc_active;			
			ACMADDR		<= x"58";		-- enable the RTC counter
			ACMWDATA  	<= x"4B";
			ACMWEN		<= '1';  			
-- AW: ??	
		when rtc_active	=>
			ACMADDR		<= x"40";		-- read seconds RTC counter
			rtc_act_int	<= '1';
			if (rr_pwrdwn = '1') then				
				RTCcal_next	<= pwrdwn_active0;	
			elsif (rr_loadrtc = '1') then 
				RTCcal_next	<= load_rtc1;	
			else		
				RTCcal_next	<= rtc_active;	
			end if;
			if (sec_flag = '1') then
				trigger		<= '1';	-- set immediately
				clr_sec_flag	<= '1';	-- cleared sync
			end if;				
--												
--****************************************************************************							
-- Need 700ns (7 cycles of the 10MHz of the system clock) to save state of 
-- calendar 1.5V counters in the match register. We can do this because the match 
-- register is powered by 3.3V not 1.5v array power.  All of the logic in this 
-- core is powered off of the 1.5 volts and will be lost when 1.5v is shut down.  
-- We need to remember where we were at the time of power down.
--****************************************************************************	
--												
		when pwrdwn_active0	 => 
			RTCcal_next 	<= pwrdwn_active1;	
			ACMADDR		<= x"58";		-- reset the RTC counter
			ACMWDATA  	<= x"00";
			ACMWEN		<= '1';  
		when pwrdwn_active1	=>
			RTCcal_next	<= pwrdwn_active2;			
			ACMADDR		<= x"58";		-- enable the RTC counter
			ACMWDATA  	<= x"4B";
			ACMWEN		<= '1';  
		when pwrdwn_active2	=>
			RTCcal_next	<= pwrdwn_active3;			
			ACMADDR		<= x"48";		-- store byte0 in match reg
			ACMWDATA  	<= count_minutes(0) & count_seconds(6 downto 0);
			ACMWEN		<= '1';  
		when pwrdwn_active3	=>
			RTCcal_next	<= pwrdwn_active4;			
			ACMADDR		<= x"49";		-- store byte1 in match reg
			ACMWDATA  	<= count_hours(1 downto 0) & count_minutes(6 downto 1);
			ACMWEN		<= '1';  
		when pwrdwn_active4	=>
			RTCcal_next	<= pwrdwn_active5;			
			ACMADDR		<= x"4a";		-- store byte2 in match reg
			ACMWDATA  	<= count_day(2 downto 0) & count_hours(6 downto 2);
			ACMWEN		<= '1';  
		when pwrdwn_active5	=>
			RTCcal_next	<= pwrdwn_active6;			
			ACMADDR		<= x"4b";		-- store byte3 in match reg
			ACMWDATA  	<= count_month(1 downto 0) & count_date(5 downto 0);
			ACMWEN		<= '1';  
		when pwrdwn_active6	=>
			RTCcal_next	<= idle;			
			ACMADDR		<= x"4c";		-- store byte4 in match reg
			ACMWDATA  	<= '1' & count_year(3 downto 0) & count_month(4 downto 2);
			ACMWEN		<= '1';  			
--															
--****************************************************************************	
-- Recovery mode requires the RTC 3.3v counter to be read, and the RTC 3.3v match
-- register to be read.  Once we know the RTC counter value, we know the elapsed time
-- or the duration of the shutdown in total seconds.  From this, we can make the 
-- appropriate adjustments to the calendar 1.5v counters (updated from the match register
-- value that was saved on power-down) to "catch up" to the "live" RTC counter
--****************************************************************************	
--		     		     
		when pwrdwn_recovery => 
			ACMADDR		<= x"4c";		--clear bit 39-32 of the match register
			ACMWDATA  	<= x"00";		--AW: whenever you need to read / write, you need do a dummy read / write on the MSB first??
			ACMWEN		<= '1';			--??
			RTCcal_next	<= count_r0a;	
		when count_r0a => 
			ACMADDR		<= x"40"; 
			RTCcal_next	<= count_r0b;
		when count_r0b => 
			ACMADDR		<= x"40"; 	-- skip reading the LS byte
			RTCcal_next	<= count_r1;					  
		when count_r1 => 
			ACMADDR		<= x"41"; 
			RTCcal_next	<= count_r2;
		when count_r2 => 
			ACMADDR		<= x"42"; 
			RTCcal_next	<= count_r3;
			count_ld		<= "10001";
		when count_r3 => 
			ACMADDR		<= x"43"; 
			RTCcal_next	<= count_r4;
			count_ld		<= "10010";
		when count_r4 => 
			ACMADDR		<= x"44"; 
			RTCcal_next	<= count_r5;
			count_ld		<= "10100";
		when count_r5 => 
			RTCcal_next	<= elapsed_time0;
			count_ld		<= "11000";
			rtc_init		<= '1';	
		-- make adjustments for the amount of elapsed time			
		-- AW: ??
		when elapsed_time0  =>
			ACMADDR		<= x"40";	 -- read seconds RTC counter
			rtc_act_int	<= '1';	
			if (rtc_eq0 = '1') then   --done counting
				RTCcal_next	<= rtc_active;
			elsif (sec_flag = '0') then
				trigger		<= '1';
				dec_rtc		<= '1';									
				RTCcal_next 	<= elapsed_time0;
			else 
			-- this condition means a real INC seconds occurred
			-- as a  result, I don't dec the RTC
				trigger		<= '1';
				clr_sec_flag	<= '1';
				RTCcal_next 	<= elapsed_time0;									
			end if;				
			
		when others =>
			
	end case;
end process;
--
-- Instantiate the RTC counter (used to reduce tile count)
rtccount : rip_count32 port map
    	   ( Data 	=>  rtc_load,	
    		Enable	=>  dec_rtc,	
    		Sload	=>  count_ld(4),
        	Aclr		=>  sys_reset,		
        	Clock 	=>  sys_clock,	
        	Q 		=>  rtc_count,		
        	Tcnt 	=>  rtc_eq0
        	);

--
--********************************************************************************
-- Sync portion of the RTC state machine
--********************************************************************************
--
--

process (sys_clock,sys_reset)
begin
	if (sys_reset = '0') then 
		RTCcal_present	<= idle;
		rtc_match		<= x"0000000000";
		rtc_load 		<= x"00000000";
		count_year		<= x"00";		-- offset from 2000
		count_month		<= x"00";		-- default is 1
		count_date		<= x"00";		-- default is 1
		count_day		<= x"00";		-- default is 1
		count_hours		<= x"00";		-- default is 0
		count_minutes	<= x"00";		-- default is 0
		count_seconds	<= x"00";		-- default is 0  
		numdays_month	<= x"31";
		leap_year		<= '0';			--2008 and 2012
		reg_loadrtc		<= '0';
		rreg_loadrtc	<= '0';   
		rr_loadrtc		<= '0';
		reg_pwrdwn		<= '0';
		rr_pwrdwn	    <= '0';		
		reg_INIT_done	<= '0';
		sec_flag		<= '0';				
		reg_rtc7		<= '0';
          
	elsif rising_edge(sys_clock) then

-- synch. portion of the RTC state machine.
		RTCcal_present	<= RTCcal_next;
-- sync the async signals
		reg_loadrtc	<= LOAD_rtc;
		rreg_loadrtc	<= reg_loadrtc;
		if (reg_loadrtc = '1') and (rreg_loadrtc = '0') then
			rr_loadrtc	<= '1';
		else
			rr_loadrtc	<= '0';
		end if;			
								
		reg_pwrdwn	<= POWER_down;     
		rr_pwrdwn		<= reg_pwrdwn;   
		reg_INIT_done	<= INIT_done; 
		
		case match_ld is
			when "00001"	=> rtc_match(7 downto 0)   <= ACMRDATA;
			when "00010"	=> rtc_match(15 downto 8)  <= ACMRDATA;
			when "00100"	=> rtc_match(23 downto 16) <= ACMRDATA;
			when "01000"	=> rtc_match(31 downto 24) <= ACMRDATA;
			when "10000"	=> rtc_match(39 downto 32) <= ACMRDATA;
			when others    => rtc_match			  <= rtc_match;
		end case;	
		
		case count_ld(3 downto 0) is
			when "0001"	=> rtc_load(7 downto 0)   <= ACMRDATA;  --address 41h
			when "0010"	=> rtc_load(15 downto 8)  <= ACMRDATA;  --address 42h
			when "0100"	=> rtc_load(23 downto 16) <= ACMRDATA;  --address 43h
			when "1000"	=> rtc_load(31 downto 24) <= ACMRDATA;  --address 44h
			when others    => 
		end case;							
		
		if (rtc_ld = '1') then
			count_year		<= LOAD_year;		
			count_month		<= LOAD_month;		
			count_date		<= LOAD_date;			
			count_day			<= LOAD_day;		
			count_hours		<= LOAD_hours;		
			count_minutes		<= LOAD_minutes;	
			count_seconds		<= LOAD_seconds;
		elsif (rtc_init = '1') then
			count_year		<= "0000"  & rtc_match(38 downto 35);		
			count_month		<= "000"   & rtc_match(34 downto 30);		
			count_date		<= "00"    & rtc_match(29 downto 24);			
			count_day		<= "00000" & rtc_match(23 downto 21);		
			count_hours		<= '0'     & rtc_match(20 downto 14);		
			count_minutes	<= '0'     & rtc_match(13 downto 7);	
			count_seconds	<= '0'     & rtc_match(6 downto 0);								
		else		
		-- perform a rising edge detect on ACM read data bit 7
			if (rtc_act_int = '1') then
				reg_rtc7		<= ACMRDATA(7);	
			else
				reg_rtc7		<= '0';   
--			if (rtc_act_int = '1') then
--				reg_rtc7		<= clk1hz;	
--			else
--				reg_rtc7		<= '0';   
			end if;			
													
			if (clr_sec_flag = '1') then	
				sec_flag	<=	'0';		
			elsif (ACMRDATA(7) = '1' and reg_rtc7 = '0' and rtc_act_int = '1') then
--			elsif (clk1hz = '1' and reg_rtc7 = '0' and rtc_act_int = '1') then
				sec_flag	<=	'1';				
			end if;
			
		-- BCD counter for seconds			
			if (inc_sec = '1') then 
				if (count_seconds(6 downto 0) = "1011001") then
					count_seconds(6 downto 0) <=	"0000000"; 
				elsif (count_seconds(3 downto 0) = "1001") then
					count_seconds(3 downto 0) <=	"0000";
					count_seconds(6 downto 4) <=	count_seconds(6 downto 4) + 1;	
				else
					count_seconds(3 downto 0) <=	count_seconds(3 downto 0) + 1;
					count_seconds(6 downto 4) <=	count_seconds(6 downto 4);
				end if;		
			end if;																											
		-- BCD counter for minutes			
			if (inc_min = '1') then 
				if (count_minutes(6 downto 0) = "1011001") then
					count_minutes(6 downto 0) <= "0000000";
				elsif (count_minutes(3 downto 0) = "1001") then
					count_minutes(3 downto 0) <=	"0000"; 
					count_minutes(6 downto 4) <=	count_minutes(6 downto 4) + 1;	
				else
					count_minutes(3 downto 0) <=	count_minutes(3 downto 0) + 1;
					count_minutes(6 downto 4) <=	count_minutes(6 downto 4);
				end if;			
			end if;											
		-- BCD counter for hours			
			if (inc_hour = '1') then 
				if (count_hours(4 downto 0) = "01001") then		-- from 9-10
					count_hours(4 downto 0) <= "10000";		
				elsif (count_hours(4 downto 0) = "10000") then	-- from 10-11
					count_hours(4 downto 0) <= "10001";							 					
				elsif (count_hours(5 downto 0) = "010001") then	-- from 11am-12pm
					count_hours(5 downto 0) <= "110010";						
				elsif (count_hours(5 downto 0) = "110001") then	-- from 11pm-12am
					count_hours(5 downto 0) <= "010010";						 
				elsif (count_hours(4 downto 0) = "10010") then  	-- from 12-1 
					count_hours(4 downto 0) <= "00001";				
				else
					count_hours(3 downto 0)	<= count_hours(3 downto 0) + 1;					
				end if;														 							 																													
			end if;					
		-- BCD counter for day			
			if (inc_day = '1') then 
				if (count_day(2 downto 0) = "111") then			-- the 7th day
					count_day(2 downto 0) <= "001";		
				else
					count_day(2 downto 0) <= count_day(2 downto 0) + 1;
				end if;											 							 																													
			end if;	
		-- BCD counter for date			
			if (inc_day = '1') then 
				if (count_date(5 downto 0) = numdays_month(5 downto 0)) then
					count_date(5 downto 0) 	<= "000001";
				elsif (count_date(3 downto 0) = "1001") then
					count_date(3 downto 0)	<= "0000";
					count_date(5 downto 4) 	<= count_date(5 downto 4) + 1;											
				else
					count_date(3 downto 0) 	<= count_date(3 downto 0) + 1;
					count_date(5 downto 4) 	<= count_date(5 downto 4);
				end if;														 							 																													
			end if;				
		-- BCD counter for month			
			if (inc_month = '1') then 
				if (count_month(4 downto 0) = "10010") then
					count_month(4 downto 0) 	<= "00001";				
				elsif (count_month(3 downto 0) = "1001") then
					count_month(3 downto 0) 	<= "0000";
					count_month(4)			<= '1';										
				else
					count_month(3 downto 0) 	<= count_month(3 downto 0) + 1;
					count_month(4) 		<= count_month(4);	
				end if;														 							 																													
			end if;	
		-- BCD counter for year			
			if (inc_year = '1') then 
				count_year(3 downto 0) 	<= count_year(3 downto 0) + 1;											 							 																													
			end if;
		--																	
		-- Leap year and month adjustments
		--
			case count_month(4 downto 0) is
				when "00001" => numdays_month	<= x"31";		-- January
				when "00010" => 						-- February
					if (leap_year = '1') then			
						numdays_month	<= x"29";
					else
						numdays_month	<= x"28";
					end if;												
				when "00011" => numdays_month	<= x"31";		-- March
				when "00100" => numdays_month	<= x"30";		-- April
				when "00101" => numdays_month	<= x"31";		-- May
				when "00110" => numdays_month	<= x"30";		-- June
				when "00111" => numdays_month	<= x"31";		-- July
				when "01000" => numdays_month	<= x"31";		-- August
				when "01001" => numdays_month	<= x"30";		-- September
				when "01010" => numdays_month	<= x"31";		-- October
				when "01011" => numdays_month	<= x"30";		-- November
				when "01100" => numdays_month	<= x"31";		-- December
				when others  =>
			end case;				
						
			if ((count_year(3 downto 0) = "1000") or 
			    (count_year(3 downto 0) = "1100")) then
				leap_year		<= '1';
			else
				leap_year 	<= '0';
			end if;												
		end if;												
	end if;			-- CLK if statement end if
end process;

--
-- These async processes were necessary to ensure all counters get updated
-- on the same ACM clock cycle -- This simplifies the reading of these registers
--

inc_sec	<= trigger;
process (inc_sec,count_seconds)
begin
	if (inc_sec = '1') then 
		if (count_seconds(6 downto 0) = "1011001") then
			inc_min	<= '1';				
		else
			inc_min	<= '0';
		end if;
	else
		inc_min	<= '0';					
	end if;	
end process;	
																											
process (inc_min, count_minutes)
begin	
	if (inc_min = '1') then 
		if (count_minutes(6 downto 0) = "1011001") then
			inc_hour	<= '1';
		else
			inc_hour	<= '0';
		end if;
	else 
		inc_hour	<= '0';				
	end if;											
end process;

process (inc_hour, count_hours)
begin	
	if (inc_hour = '1') then 
		if (count_hours(5 downto 0) = "110001") then	-- from 11pm-12am	
			inc_day	<= '1';					 				
		else
			inc_day	<= '0';					
		end if;	
	else 
		inc_day	<= '0';														 							 																													
	end if;					
end process;

process (inc_day, count_date, numdays_month)
begin			
	if (inc_day = '1') then 
		if (count_date(5 downto 0) = numdays_month(5 downto 0)) then
			inc_month		<= '1';											
		else
			inc_month		<= '0';
		end if;	
	else 
		inc_month	  <= '0';														 							 																													
	end if;
end process;					

process (inc_month, count_month)
begin	
	if (inc_month = '1') then 
		if (count_month(4 downto 0) = "10010") then		
			inc_year		<= '1';										
		else	
			inc_year		<= '0';	
		end if;
	else 
		inc_year   <= '0';															 							 																													
	end if;	
end process;			
  
end DEF_ARCH;