blob: c2846dc0a1fce7d5e8324551dce50fe719292396 (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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;
|