diff --git a/nmigen_soc/periph.py b/nmigen_soc/periph.py new file mode 100644 index 0000000..6a8ad36 --- /dev/null +++ b/nmigen_soc/periph.py @@ -0,0 +1,59 @@ +from .memory import MemoryMap +from . import event + + +__all__ = ["PeripheralInfo"] + + +class PeripheralInfo: + """Peripheral metadata. + + A unified description of the local resources of a peripheral. It may be queried in order to + recover its memory windows, CSR registers and event sources. + + Parameters + ---------- + memory_map : :class:`MemoryMap` + Memory map of the peripheral. + irq : :class:`event.Source` + IRQ line of the peripheral. Optional. + """ + def __init__(self, *, memory_map, irq=None): + if not isinstance(memory_map, MemoryMap): + raise TypeError("Memory map must be an instance of MemoryMap, not {!r}" + .format(memory_map)) + memory_map.freeze() + self._memory_map = memory_map + + if irq is not None and not isinstance(irq, event.Source): + raise TypeError("IRQ line must be an instance of event.Source, not {!r}" + .format(irq)) + self._irq = irq + + @property + def memory_map(self): + """Memory map. + + Return value + ------------ + A :class:`MemoryMap` describing the local address space of the peripheral. + """ + return self._memory_map + + @property + def irq(self): + """IRQ line. + + Return value + ------------ + An :class:`event.Source` used by the peripheral to request interrupts. If provided, its + event map describes local events. + + Exceptions + ---------- + Raises :exn:`NotImplementedError` if the peripheral info does not have an IRQ line. + """ + if self._irq is None: + raise NotImplementedError("Peripheral info does not have an IRQ line" + .format(self)) + return self._irq diff --git a/nmigen_soc/test/test_periph.py b/nmigen_soc/test/test_periph.py new file mode 100644 index 0000000..5499181 --- /dev/null +++ b/nmigen_soc/test/test_periph.py @@ -0,0 +1,50 @@ +import unittest + +from ..periph import PeripheralInfo +from ..memory import MemoryMap +from .. import event + + +class PeripheralInfoTestCase(unittest.TestCase): + def test_memory_map(self): + memory_map = MemoryMap(addr_width=1, data_width=8) + info = PeripheralInfo(memory_map=memory_map) + self.assertIs(info.memory_map, memory_map) + + def test_memory_map_frozen(self): + memory_map = MemoryMap(addr_width=1, data_width=8) + info = PeripheralInfo(memory_map=memory_map) + with self.assertRaisesRegex(ValueError, + r"Memory map has been frozen. Address width cannot be extended further"): + memory_map.add_resource("a", size=3, extend=True) + + def test_memory_map_wrong(self): + with self.assertRaisesRegex(TypeError, + r"Memory map must be an instance of MemoryMap, not 'foo'"): + info = PeripheralInfo(memory_map="foo") + + def test_irq(self): + memory_map = MemoryMap(addr_width=1, data_width=8) + irq = event.Source() + info = PeripheralInfo(memory_map=memory_map, irq=irq) + self.assertIs(info.irq, irq) + + def test_irq_none(self): + memory_map = MemoryMap(addr_width=1, data_width=8) + info = PeripheralInfo(memory_map=memory_map, irq=None) + with self.assertRaisesRegex(NotImplementedError, + r"Peripheral info does not have an IRQ line"): + info.irq + + def test_irq_default(self): + memory_map = MemoryMap(addr_width=1, data_width=8) + info = PeripheralInfo(memory_map=memory_map) + with self.assertRaisesRegex(NotImplementedError, + r"Peripheral info does not have an IRQ line"): + info.irq + + def test_irq_wrong(self): + memory_map = MemoryMap(addr_width=1, data_width=8) + with self.assertRaisesRegex(TypeError, + r"IRQ line must be an instance of event.Source, not 'foo'"): + info = PeripheralInfo(memory_map=memory_map, irq="foo")