litex_sim: Rework Makefiles to put output files in gateware directory.
[litex.git] / litex / soc / interconnect / csr_eventmanager.py
1 # This file is Copyright (c) 2015 Sebastien Bourdeauducq <sb@m-labs.hk>
2 # This file is Copyright (c) 2016-2019 Tim 'mithro' Ansell <me@mith.ro>
3 # License: BSD
4
5 """
6 The event manager provides a systematic way to generate standard interrupt
7 controllers.
8 """
9
10 from functools import reduce
11 from operator import or_
12
13 from migen import *
14 from migen.util.misc import xdir
15 from migen.fhdl.tracer import get_obj_var_name
16
17 from litex.soc.interconnect.csr import *
18
19
20 class _EventSource(DUID):
21 """Base class for EventSources.
22
23 Attributes
24 ----------
25 trigger : Signal(), in
26 Signal which interfaces with the user design.
27
28 status : Signal(), out
29 Contains the current level of the trigger signal.
30 This value ends up in the ``status`` register.
31
32 pending : Signal(), out
33 A trigger event has occurred and not yet cleared.
34 This value ends up in the ``pending`` register.
35
36 clear : Signal(), in
37 Clear after a trigger event.
38 Ignored by some event sources.
39
40 name : str
41 A short name for this EventSource, usable as a Python identifier
42
43 description: str
44 A formatted description of this EventSource, including when
45 it will fire and how it behaves.
46 """
47
48 def __init__(self, name=None, description=None):
49 DUID.__init__(self)
50 self.status = Signal()
51 self.pending = Signal()
52 self.trigger = Signal()
53 self.clear = Signal()
54 self.name = get_obj_var_name(name)
55 self.description = description
56
57
58 class EventSourcePulse(Module, _EventSource):
59 """EventSource which triggers on a pulse.
60
61 The event stays asserted after the ``trigger`` signal goes low, and until
62 software acknowledges it.
63
64 An example use is to pulse ``trigger`` high for 1 cycle after the reception
65 of a character in a UART.
66 """
67
68 def __init__(self, name=None, description=None):
69 _EventSource.__init__(self, name, description)
70 self.comb += self.status.eq(0)
71 self.sync += [
72 If(self.clear, self.pending.eq(0)),
73 If(self.trigger, self.pending.eq(1))
74 ]
75
76
77 class EventSourceProcess(Module, _EventSource):
78 """EventSource which triggers on a falling edge.
79
80 The purpose of this event source is to monitor the status of processes and
81 generate an interrupt on their completion.
82 """
83 def __init__(self, name=None, description=None):
84 _EventSource.__init__(self, name, description)
85 self.comb += self.status.eq(self.trigger)
86 old_trigger = Signal()
87 self.sync += [
88 If(self.clear, self.pending.eq(0)),
89 old_trigger.eq(self.trigger),
90 If(~self.trigger & old_trigger, self.pending.eq(1))
91 ]
92
93
94 class EventSourceLevel(Module, _EventSource):
95 """EventSource which trigger contains the instantaneous state of the event.
96
97 It must be set and released by the user design. For example, a DMA
98 controller with several slots can use this event source to signal that one
99 or more slots require CPU attention.
100 """
101 def __init__(self, name=None, description=None):
102 _EventSource.__init__(self, name, description)
103 self.comb += [
104 self.status.eq(self.trigger),
105 self.pending.eq(self.trigger)
106 ]
107
108
109 class EventManager(Module, AutoCSR):
110 """Provide an IRQ and CSR registers for a set of event sources.
111
112 Each event source is assigned one bit in each of those registers.
113
114 Attributes
115 ----------
116 irq : Signal(), out
117 A signal which is driven high whenever there is a pending and unmasked
118 event.
119 It is typically connected to an interrupt line of a CPU.
120
121 status : CSR(n), read-only
122 Contains the current level of the trigger line of
123 ``EventSourceProcess`` and ``EventSourceLevel`` sources.
124 It is always 0 for ``EventSourcePulse``
125
126 pending : CSR(n), read-write
127 Contains the currently asserted events. Writing 1 to the bit assigned
128 to an event clears it.
129
130 enable : CSR(n), read-write
131 Defines which asserted events will cause the ``irq`` line to be
132 asserted.
133 """
134
135 def __init__(self):
136 self.irq = Signal()
137
138 def do_finalize(self):
139 sources_u = [v for k, v in xdir(self, True) if isinstance(v, _EventSource)]
140 sources = sorted(sources_u, key=lambda x: x.duid)
141 n = len(sources)
142 self.status = CSR(n)
143 self.pending = CSR(n)
144 self.enable = CSRStorage(n)
145
146 for i, source in enumerate(sources):
147 self.comb += [
148 self.status.w[i].eq(source.status),
149 If(self.pending.re & self.pending.r[i], source.clear.eq(1)),
150 self.pending.w[i].eq(source.pending)
151 ]
152
153 irqs = [self.pending.w[i] & self.enable.storage[i] for i in range(n)]
154 self.comb += self.irq.eq(reduce(or_, irqs))
155
156 def __setattr__(self, name, value):
157 object.__setattr__(self, name, value)
158 if isinstance(value, _EventSource):
159 if self.finalized:
160 raise FinalizeError
161 self.submodules += value
162
163
164 class SharedIRQ(Module):
165 """Allow an IRQ signal to be shared between multiple EventManager objects."""
166
167 def __init__(self, *event_managers):
168 self.irq = Signal()
169 self.comb += self.irq.eq(reduce(or_, [ev.irq for ev in event_managers]))