diff options
Diffstat (limited to 'hdl/timer0.vhd')
| -rw-r--r-- | hdl/timer0.vhd | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/hdl/timer0.vhd b/hdl/timer0.vhd new file mode 100644 index 0000000..c2846dc --- /dev/null +++ b/hdl/timer0.vhd @@ -0,0 +1,131 @@ +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; |
