1+ from amaranth import Module , Signal , unsigned
2+ from amaranth .lib .wiring import Component , In , Out , connect , flipped
3+
4+ from amaranth_soc import csr
5+
6+ """
7+ General-Purpose Timer:
8+ - 32-bit up-counter
9+ - 8-bit prescaler
10+ - compare match + auto-reload
11+ - level-high IRQ on match
12+ """
13+
14+
15+ __all__ = ["GPTimer" ]
16+
17+
18+ class GPTimer (Component ):
19+ class Ctrl (csr .Register , access = "rw" ):
20+ """CTRL: [0]=EN, [1]=RST, [2]=AR, [3]=IRQ_EN
21+ EN = Enables the counter
22+ RST = Resets the counter
23+ AR = Enable auto reload
24+ IRQ_EN = Enable interrupt on match
25+ """
26+
27+ en : csr .Field (csr .action .RW , unsigned (1 ))
28+ rst : csr .Field (csr .action .RW , unsigned (1 ))
29+ ar : csr .Field (csr .action .RW , unsigned (1 ))
30+ irq_en : csr .Field (csr .action .RW , unsigned (1 ))
31+ # bits [4:8) reserved
32+
33+ class Presc (csr .Register , access = "rw" ):
34+ """Prescaler divisor (0 => /1, 255 => /256)"""
35+
36+ val : csr .Field (csr .action .RW , unsigned (8 ))
37+
38+ class Compare (csr .Register , access = "rw" ):
39+ """32-bit compare value"""
40+
41+ val : csr .Field (csr .action .RW , unsigned (32 ))
42+
43+ class Count (csr .Register , access = "r" ):
44+ """32-bit free-running counter (read-only)"""
45+
46+ val : csr .Field (csr .action .R , unsigned (32 ))
47+
48+ class Status (csr .Register , access = "rw" ):
49+ """STATUS: [0]=MATCH (W1C)"""
50+
51+ match : csr .Field (csr .action .RW1C , unsigned (1 ))
52+ # bits [1:8) reserved
53+
54+ def __init__ (self ):
55+ # CSR bank: 5-bit address, 8-bit data bus
56+ regs = csr .Builder (addr_width = 5 , data_width = 8 )
57+ self ._ctrl = regs .add ("ctrl" , self .Ctrl (), offset = 0x00 )
58+ self ._presc = regs .add ("presc" , self .Presc (), offset = 0x04 )
59+ self ._count = regs .add ("count" , self .Count (), offset = 0x08 )
60+ self ._compare = regs .add ("compare" , self .Compare (), offset = 0x0C )
61+ self ._status = regs .add ("status" , self .Status (), offset = 0x10 )
62+
63+ self ._bridge = csr .Bridge (regs .as_memory_map ())
64+
65+ super ().__init__ (
66+ {
67+ "bus" : In (
68+ csr .Signature (
69+ addr_width = regs .addr_width , data_width = regs .data_width
70+ )
71+ ),
72+ "irq" : Out (1 ),
73+ }
74+ )
75+ self .bus .memory_map = self ._bridge .bus .memory_map
76+
77+ def elaborate (self , platform ):
78+ m = Module ()
79+ m .submodules .bridge = self ._bridge
80+ connect (m , flipped (self .bus ), self ._bridge .bus )
81+
82+ # shorthands to the field-ports
83+ ctrl = self ._ctrl .f
84+ presc = self ._presc .f
85+ cmp_ = self ._compare .f
86+ cnt = self ._count .f
87+ status = self ._status .f
88+
89+ # Internal timer signals:
90+ p_cnt = Signal (8 , name = "prescaler_cnt" )
91+ cnt_r = Signal (32 , name = "counter" )
92+ mflag = Signal (1 , name = "match_pulse" )
93+
94+ # prescaler & counter logic
95+ with m .If (ctrl .rst .data ):
96+ m .d .sync += [p_cnt .eq (0 ), cnt_r .eq (0 ), mflag .eq (0 )]
97+ with m .Elif (ctrl .en .data ):
98+ with m .If (p_cnt == presc .val .data ):
99+ m .d .sync += [p_cnt .eq (0 ), cnt_r .eq (cnt_r + 1 )]
100+ with m .Else ():
101+ m .d .sync += p_cnt .eq (p_cnt + 1 )
102+
103+ # default no pulse match
104+ m .d .sync += mflag .eq (0 )
105+
106+ # compare & auto-reload, set match-flag
107+ with m .If ((cnt_r == cmp_ .val .data ) & ctrl .en .data ):
108+ m .d .sync += mflag .eq (1 )
109+ with m .If (
110+ ctrl .ar .data
111+ ): # if auto reload isn't enabled the counter keeps incrementing after hitting the compare match value
112+ m .d .sync += cnt_r .eq (0 )
113+
114+ m .d .comb += status .match .set .eq (
115+ mflag
116+ ) # drive the RW1C “set” port from that pulse
117+ m .d .comb += cnt .val .r_data .eq (cnt_r ) # mirror into COUNT CSR
118+ m .d .comb += self .irq .eq (
119+ status .match .data & ctrl .irq_en .data
120+ ) # irq follows the *CSR storage
121+
122+ return m
0 commit comments