Add support for configuration constants.
[lambdasoc.git] / lambdasoc / periph / intc.py
1 from nmigen import *
2
3 from nmigen_soc.periph import ConstantMap
4
5 from . import Peripheral, IRQLine
6
7
8 __all__ = ["InterruptController", "GenericInterruptController"]
9
10
11 class InterruptController(Peripheral):
12 """Interrupt controller base class."""
13 def __init__(self, *args, **kwargs):
14 super().__init__(*args, **kwargs)
15 self.__irq_lines = set()
16 self.__irq_map = dict()
17
18 @property
19 def constant_map(self):
20 return ConstantMap(**{
21 line.name.upper(): index for index, line in self.iter_irqs()
22 })
23
24 def add_irq(self, line, index):
25 """Add an IRQ line.
26
27 Parameters
28 ----------
29 line : :class:`IRQLine`
30 IRQ line.
31 index : int
32 IRQ index.
33
34 Exceptions
35 ----------
36 Raises :exn:`ValueError` if ``line`` is added twice, or if ``index`` is already used.
37 """
38 if not isinstance(line, IRQLine):
39 raise TypeError("IRQ line must be an instance of IRQLine, not {!r}"
40 .format(line))
41 if not isinstance(index, int) or index < 0:
42 raise ValueError("IRQ index must be a non-negative integer, not {!r}"
43 .format(index))
44 if line in self.__irq_lines:
45 raise ValueError("IRQ line {!r} has already been mapped to IRQ index {}"
46 .format(line, self.find_index(line)))
47 if index in self.__irq_map:
48 raise ValueError("IRQ index {} has already been mapped to IRQ line {!r}"
49 .format(index, self.__irq_map[index]))
50 self.__irq_lines.add(line)
51 self.__irq_map[index] = line
52
53 def iter_irqs(self):
54 """Iterate IRQ lines.
55
56 Yield values
57 ------------
58 A tuple ``index, line`` describing an IRQ line and its index.
59 """
60 yield from sorted(self.__irq_map.items())
61
62 def find_index(self, line):
63 """Find the index at which an IRQ line is mapped.
64
65 Parameters
66 ----------
67 line : :class:`IRQLine`
68 IRQ line.
69
70 Return value
71 ------------
72 The index at which ``line`` is mapped, if present.
73
74 Exceptions
75 ----------
76 Raises :exn:`KeyError` if ``line`` is not present.
77 """
78 for irq_index, irq_line in self.iter_irqs():
79 if line is irq_line:
80 return irq_index
81 raise KeyError(line)
82
83
84 class GenericInterruptController(InterruptController, Elaboratable):
85 """Generic interrupt controller.
86
87 An interrupt "controller" acting as a passthrough for IRQ lines. Useful for CPU cores that do
88 interrupt management themselves.
89
90 Parameters
91 ----------
92 width : int
93 Output width.
94
95 Attributes
96 ----------
97 width : int
98 Output width.
99 ip : Signal, out
100 Pending IRQs.
101 """
102 def __init__(self, *, width):
103 super().__init__(src_loc_at=2)
104 if not isinstance(width, int) or width <= 0:
105 raise ValueError("Width must be a strictly positive integer, not {!r}"
106 .format(width))
107 self.width = width
108 self.ip = Signal(width)
109
110 def add_irq(self, line, index):
111 __doc__ = InterruptController.add_irq.__doc__
112 if not isinstance(index, int) or index not in range(0, self.width):
113 raise ValueError("IRQ index must be an integer ranging from 0 to {}, not {!r}"
114 .format(self.width - 1, index))
115 super().add_irq(line, index)
116
117 def elaborate(self, platform):
118 m = Module()
119
120 for irq_index, irq_line in self.iter_irqs():
121 m.d.comb += self.ip[irq_index].eq(irq_line)
122
123 return m