library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity timer0 is generic ( CLK_FREQ : positive := 50_000_000 ); port ( clk : in std_logic; reset : in std_logic; -- External clock pin (used when T0CS='1') t0cki : in std_logic; -- OPTION_REG control bits t0cs : in std_logic; -- 0=Fosc/4, 1=T0CKI t0se : in std_logic; -- 0=rising edge, 1=falling edge psa : in std_logic; -- 0=prescaler->Timer0, 1=prescaler->WDT ps : in std_logic_vector(2 downto 0); -- prescaler rate select -- TMR0 register interface (mapped at 0x01) tmr0_we : in std_logic; tmr0_in : in std_logic_vector(7 downto 0); tmr0_out : out std_logic_vector(7 downto 0); -- Overflow flag: pulses one clock on FFh->00h rollover t0if : out std_logic ); end entity timer0; architecture rtl of timer0 is pure function ps_rate(sel : std_logic_vector(2 downto 0)) return natural is begin case sel is when "000" => return 2; when "001" => return 4; when "010" => return 8; when "011" => return 16; when "100" => return 32; when "101" => return 64; when "110" => return 128; when others => return 256; end case; end function; -- Fosc/4 tick generator (one pulse every 4 clocks) constant FOSC4_DIV : positive := 4; signal fosc4_cnt : natural range 0 to FOSC4_DIV - 1 := 0; signal fosc4_tick : std_logic := '0'; -- 2FF synchroniser for the post-mux clock input signal sync_ff1 : std_logic := '0'; signal sync_ff2 : std_logic := '0'; signal sync_prev : std_logic := '0'; -- for edge detection after sync signal sync_tick : std_logic := '0'; -- one-cycle tick on selected edge -- Prescaler signal prescaler : unsigned(7 downto 0) := (others => '0'); signal ps_tick : std_logic := '0'; -- one-cycle pulse out of prescaler -- TMR0 counter signal tmr0 : unsigned(7 downto 0) := (others => '0'); -- Write inhibit: per datasheet, 2 cycles after a TMR0 write the -- prescaler increment is blocked to avoid a spurious count signal wr_inhibit : natural range 0 to 2 := 0; begin fosc4_timer_proc : process(clk) begin if rising_edge(clk) then fosc4_tick <= '0'; if fosc4_cnt = FOSC4_DIV - 1 then fosc4_tick <= '1'; fosc4_cnt <= 0; else fosc4_cnt <= fosc4_cnt + 1; end if; end if; end process fosc4_timer_proc; prescaler_proc : process(clk) begin if rising_edge(clk) then ps_tick <= '0'; if tmr0_we = '1' then prescaler <= (others => '0'); elsif fosc4_tick = '1' and psa = '0' then if to_integer(prescaler) + 1 >= ps_rate(ps) then prescaler <= (others => '0'); ps_tick <= '1'; else prescaler <= prescaler + 1; end if; end if; end if; end process prescaler_proc; tmr0_out <= std_logic_vector(tmr0); timer0_proc : process(clk) begin if rising_edge(clk) then t0if <= '0'; -- 2FF sync sync_ff1 <= '1' when (psa = '1' and fosc4_tick = '1') or (psa = '0' and ps_tick = '1') else '0'; sync_ff2 <= sync_ff1; if reset = '1' then tmr0 <= (others => '0'); sync_ff1 <= '0'; sync_ff2 <= '0'; elsif tmr0_we = '1' then tmr0 <= unsigned(tmr0_in); elsif sync_ff2 = '1' then if tmr0 = x"FF" then t0if <= '1'; end if; tmr0 <= tmr0 + 1; end if; end if; end process timer0_proc; end architecture rtl;