URL
https://opencores.org/ocsvn/open8_urisc/open8_urisc/trunk
Subversion Repositories open8_urisc
[/] [open8_urisc/] [trunk/] [VHDL/] [o8_rtc.vhd] - Rev 167
Go to most recent revision | Compare with Previous | Blame | View Log
-- Copyright (c)2013 Jeremy Seth Henry -- All rights reserved. -- -- Redistribution and use in source and binary forms, with or without -- modification, are permitted provided that the following conditions are met: -- * Redistributions of source code must retain the above copyright -- notice, this list of conditions and the following disclaimer. -- * Redistributions in binary form must reproduce the above copyright -- notice, this list of conditions and the following disclaimer in the -- documentation and/or other materials provided with the distribution, -- where applicable (as part of a user interface, debugging port, etc.) -- -- THIS SOFTWARE IS PROVIDED BY JEREMY SETH HENRY ``AS IS'' AND ANY -- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -- DISCLAIMED. IN NO EVENT SHALL JEREMY SETH HENRY BE LIABLE FOR ANY -- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- -- VHDL Units : realtime_clock -- Description: Provides automatically updated registers that maintain the -- : time of day. Keeps track of the day of week, hours, minutes -- : seconds, and tenths of a second. Module is doubled buffered -- : to ensure time consistency during accesses. Also provides -- : a programmable periodic interrupt timer, as well as a uSec -- : tick for external use. -- -- Register Map: -- Offset Bitfield Description Read/Write -- 0x0 AAAAAAAA Periodic Interval Timer in uS (RW) -- 0x1 -AAAAAAA Tenths (0x00 - 0x63) (RW) -- 0x2 --AAAAAA Seconds (0x00 - 0x3B) (RW) -- 0x3 --AAAAAA Minutes (0x00 - 0x3B) (RW) -- 0x4 ---AAAAA Hours (0x00 - 0x17) (RW) -- 0x5 -----AAA Day of Week (0x00 - 0x06) (RW) -- 0x6 -------- Update RTC regs from Shadow Regs (WO) -- 0x7 A------- Update Shadow Regs from RTC regs (RW) -- A = Update is Busy library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; use ieee.std_logic_arith.all; use ieee.std_logic_misc.all; library work; use work.open8_pkg.all; entity o8_rtc is generic( Sys_Freq : real; Reset_Level : std_logic; Address : ADDRESS_TYPE ); port( Clock : in std_logic; Reset : in std_logic; uSec_Tick : out std_logic; -- Bus_Address : in ADDRESS_TYPE; Wr_Enable : in std_logic; Wr_Data : in DATA_TYPE; Rd_Enable : in std_logic; Rd_Data : out DATA_TYPE; -- Interrupt_PIT : out std_logic; Interrupt_RTC : out std_logic ); end entity; architecture behave of o8_rtc is -- The ceil_log2 function returns the minimum register width required to -- hold the supplied integer. function ceil_log2 (x : in natural) return natural is variable retval : natural; begin retval := 1; while ((2**retval) - 1) < x loop retval := retval + 1; end loop; return retval; end ceil_log2; constant User_Addr : std_logic_vector(15 downto 3) := Address(15 downto 3); alias Comp_Addr is Bus_Address(15 downto 3); signal Addr_Match : std_logic; alias Reg_Addr is Bus_Address(2 downto 0); signal Reg_Addr_q : std_logic_vector(2 downto 0); signal Wr_En : std_logic; signal Wr_Data_q : DATA_TYPE; signal Rd_En : std_logic; constant DLY_1USEC_VAL: integer := integer(Sys_Freq / 1000000.0); constant DLY_1USEC_WDT: integer := ceil_log2(DLY_1USEC_VAL - 1); constant DLY_1USEC : std_logic_vector := conv_std_logic_vector( DLY_1USEC_VAL - 1, DLY_1USEC_WDT); signal uSec_Cntr : std_logic_vector( DLY_1USEC_WDT - 1 downto 0 ) := (others => '0'); signal uSec_Tick_i : std_logic; signal interval : DATA_TYPE; signal timer_cnt : DATA_TYPE; signal timer_ro : std_logic; signal rtc_frac : std_logic_vector(15 downto 0); signal frac_ro : std_logic; signal rtc_tens : DATA_TYPE; signal tens_ro : std_logic; signal rtc_secs : DATA_TYPE; signal secs_ro : std_logic; signal rtc_mins : DATA_TYPE; signal mins_ro : std_logic; signal rtc_hours : DATA_TYPE; signal hours_ro : std_logic; signal rtc_dow : DATA_TYPE; signal shd_tens : DATA_TYPE; signal shd_secs : DATA_TYPE; signal shd_mins : DATA_TYPE; signal shd_hours : DATA_TYPE; signal shd_dow : DATA_TYPE; signal update_rtc : std_logic; signal update_shd : std_logic; signal update_ctmr : std_logic_vector(3 downto 0); begin uSec_Tick <= uSec_Tick_i; Addr_Match <= '1' when Comp_Addr = User_Addr else '0'; Interrupt_PIT <= timer_ro; Interrupt_RTC <= frac_ro; io_reg: process( Clock, Reset ) begin if( Reset = Reset_Level )then uSec_Cntr <= (others => '0'); uSec_Tick_i <= '0'; interval <= x"00"; timer_cnt <= x"00"; timer_ro <= '0'; rtc_frac <= (others => '0'); frac_ro <= '0'; rtc_tens <= (others => '0'); tens_ro <= '0'; rtc_secs <= (others => '0'); secs_ro <= '0'; rtc_mins <= (others => '0'); mins_ro <= '0'; rtc_hours <= (others => '0'); hours_ro <= '0'; rtc_dow <= (others => '0'); shd_tens <= (others => '0'); shd_secs <= (others => '0'); shd_mins <= (others => '0'); shd_hours <= (others => '0'); shd_dow <= (others => '0'); update_rtc <= '0'; update_shd <= '0'; update_ctmr <= (others => '0'); Wr_Data_q <= (others => '0'); Reg_Addr_q <= (others => '0'); Wr_En <= '0'; Rd_En <= '0'; Rd_Data <= x"00"; elsif( rising_edge( Clock ) )then uSec_Cntr <= uSec_Cntr - 1; uSec_Tick_i <= '0'; if( uSec_Cntr = 0 )then uSec_Cntr <= DLY_1USEC; uSec_Tick_i <= or_reduce(Interval); end if; timer_ro <= '0'; frac_ro <= '0'; tens_ro <= '0'; secs_ro <= '0'; mins_ro <= '0'; hours_ro <= '0'; -- Periodic Interval Timer timer_cnt <= timer_cnt - uSec_Tick_i; if( or_reduce(timer_cnt) = '0' )then timer_cnt <= interval; timer_ro <= or_reduce(interval); -- Only issue output on Int > 0 end if; -- Fractional decisecond counter - cycles every 10k microseconds rtc_frac <= rtc_frac - uSec_Tick_i; if( or_reduce(rtc_frac) = '0' or update_rtc = '1' )then rtc_frac <= x"2710"; frac_ro <= not update_rtc; end if; -- Decisecond counter rtc_tens <= rtc_tens + frac_ro; if( update_rtc = '1' )then rtc_tens <= shd_tens; elsif( rtc_tens = x"64")then rtc_tens <= (others => '0'); tens_ro <= '1'; end if; -- Second counter rtc_secs <= rtc_secs + tens_ro; if( update_rtc = '1' )then rtc_secs <= shd_secs; elsif( rtc_secs = x"3C")then rtc_secs <= (others => '0'); secs_ro <= '1'; end if; -- Minute counter rtc_mins <= rtc_mins + secs_ro; if( update_rtc = '1' )then rtc_mins <= shd_mins; elsif( rtc_mins = x"3C")then rtc_mins <= (others => '0'); mins_ro <= '1'; end if; -- Hour counter rtc_hours <= rtc_hours + mins_ro; if( update_rtc = '1' )then rtc_hours <= shd_hours; elsif( rtc_hours = x"18")then rtc_hours <= (others => '0'); hours_ro <= '1'; end if; -- Day of Week counter rtc_dow <= rtc_dow + hours_ro; if( update_rtc = '1' )then rtc_dow <= shd_dow; elsif( rtc_dow = x"07")then rtc_dow <= (others => '0'); end if; -- Coherency timer - ensures that the shadow registers are updated with -- valid time data by delaying updates until the rtc registers have -- finished cascading. update_rtc <= '0'; update_ctmr <= update_ctmr - or_reduce(update_ctmr); if( frac_ro = '1' )then update_ctmr <= x"9"; end if; -- Copy the RTC registers to the shadow registers when the coherency -- timer is zero (RTC registers are static) if( update_shd = '1' and or_reduce(update_ctmr) = '0' )then shd_tens <= rtc_tens; shd_secs <= rtc_secs; shd_mins <= rtc_mins; shd_hours <= rtc_hours; shd_dow <= rtc_dow; update_shd <= '0'; end if; Reg_Addr_q <= Reg_Addr; Wr_Data_q <= Wr_Data; Wr_En <= Addr_Match and Wr_Enable; if( Wr_En = '1' )then case( Reg_Addr_q )is when "000" => interval <= Wr_Data_q; when "001" => shd_tens <= '0' & Wr_Data_q(6 downto 0); when "010" => shd_secs <= "00" & Wr_Data_q(5 downto 0); when "011" => shd_mins <= "00" & Wr_Data_q(5 downto 0); when "100" => shd_hours <= "000" & Wr_Data_q(4 downto 0); when "101" => shd_dow <= "00000" & Wr_Data_q(2 downto 0); when "110" => update_rtc <= '1'; when "111" => update_shd <= '1'; when others => null; end case; end if; Rd_Data <= (others => '0'); Rd_En <= Addr_Match and Rd_Enable; if( Rd_En = '1' )then case( Reg_Addr_q )is when "000" => Rd_Data <= interval; when "001" => Rd_Data <= shd_tens; when "010" => Rd_Data <= shd_secs; when "011" => Rd_Data <= shd_mins; when "100" => Rd_Data <= shd_hours; when "101" => Rd_Data <= shd_dow; when "110" => null; when "111" => Rd_Data <= update_shd & "0000000"; when others => null; end case; end if; end if; end process; end architecture;
Go to most recent revision | Compare with Previous | Blame | View Log