Skip to content

Commit 822ddd1

Browse files
committed
[Circuit] Support mutable IO
1 parent ed03f44 commit 822ddd1

File tree

1 file changed

+47
-27
lines changed

1 file changed

+47
-27
lines changed

magma/interface.py

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -417,9 +417,9 @@ def make_interface(self):
417417
def __add__(self, other):
418418
raise NotImplementedError()
419419

420+
@abstractmethod
420421
def __iadd__(self, other):
421-
# __iadd__ is explicitly overriden to enforce that it is non-mutating.
422-
return self + other
422+
raise NotImplementedError()
423423

424424
def flip(self) -> "IOInterface":
425425
raise NotImplementedError()
@@ -436,11 +436,9 @@ class IO(IOInterface):
436436
# https://www.python.org/dev/peps/pep-0468/.
437437
def __init__(self, **kwargs):
438438
self._ports = {}
439-
self._decl = []
440439
self._bound = False
441440
for name, typ in kwargs.items():
442-
self.add(name, typ)
443-
self._decl.extend((name, typ))
441+
self._add(name, typ)
444442

445443
@property
446444
def ports(self):
@@ -454,36 +452,52 @@ def bind(self, defn):
454452
self._bound = True
455453

456454
def decl(self):
457-
return self._decl
455+
return _flatten(
456+
(name, type(port).flip()) for name, port in self._ports.items()
457+
)
458458

459459
def make_interface(self):
460460
decl = self.decl()
461461
name = _make_interface_name(decl)
462462
dct = dict(_io=self, _decl=decl, _initialized=False)
463463
return InterfaceKind(name, (_DeclareSingletonInterface,), dct)
464464

465-
def __add__(self, other):
466-
"""
467-
Attempts to combine this IO and @other. Returns a new IO object with the
468-
combined ports, unless:
469-
* @other is not of type IOInterface, in which case a TypeError is
470-
raised
465+
def __add__(self, other: 'IO') -> 'IO':
466+
"""Attempts to combine this IO and @other. Returns a new IO object with
467+
the combined ports, unless:
468+
* @other is not of type IO, in which case a TypeError is raised
471469
* this or @other has already been bound, in which case an Exception is
472470
raised
473471
* this and @other have common port names, in which case an Exception
474472
is raised
475473
"""
476-
if not isinstance(other, IOInterface):
477-
raise TypeError(f"unsupported operand type(s) for +: 'IO' and "
478-
f"'{type(other).__name__}'")
474+
if not isinstance(other, IO):
475+
raise TypeError(
476+
f"unsupported operand type(s) for +: 'IO' and "
477+
f"'{type(other).__name__}'"
478+
)
479+
if self._bound or other._bound:
480+
raise Exception("Adding bound IO not allowed")
481+
if self._ports.keys() & other._ports.keys():
482+
raise Exception("Adding IO with duplicate port names not allowed")
483+
return IO(**_dict_from_decl(self.decl() + other.decl()))
484+
485+
def __iadd__(self, other: 'IO') -> 'IO':
486+
"""Attempts to combine this @IO and other in place, with the same
487+
caveats as __add__.
488+
"""
489+
if not isinstance(other, IO):
490+
raise TypeError(
491+
f"unsupported operand type(s) for +: 'IO' and "
492+
f"'{type(other).__name__}'"
493+
)
479494
if self._bound or other._bound:
480495
raise Exception("Adding bound IO not allowed")
481496
if self._ports.keys() & other._ports.keys():
482497
raise Exception("Adding IO with duplicate port names not allowed")
483-
decl = self._decl + other._decl
484-
return IO(**_dict_from_decl(decl))
498+
self._ports.update(other._ports)
485499

486-
def add(self, name, typ):
500+
def _add(self, name, typ):
487501
if self._bound:
488502
raise RuntimeError("Can not add to a bound IO")
489503
# Definition port.
@@ -505,7 +519,7 @@ def __getattr__(self, key: str):
505519
return super().__getattribute__(key)
506520

507521
def fields(self):
508-
return _dict_from_decl(self._decl)
522+
return _dict_from_decl(self.decl())
509523

510524
def flip(self):
511525
return IO(**{name: T.flip() for name, T in self.fields().items()})
@@ -534,26 +548,32 @@ def inst_ports(self):
534548
return self._inst_ports.copy()
535549

536550
def decl(self):
537-
return _flatten((name, type(port))
538-
for name, port in self._ports.items())
551+
return _flatten(
552+
(name, type(port)) for name, port in self._ports.items()
553+
)
539554

540555
def make_interface(self):
541556
decl = self.decl()
542557
name = _make_interface_name(decl)
543-
dct = dict(_io=self, _decl=decl, _initialized=False,
544-
_initialized_inst=False)
558+
dct = {
559+
"_io": self,
560+
"_decl": decl,
561+
"_initialized": False,
562+
"_initialized_inst": False,
563+
}
545564
return InterfaceKind(name, (_DeclareSingletonInstanceInterface,), dct)
546565

547-
def add(self, name, typ):
548-
super().add(name, typ)
566+
def _add(self, name, typ):
567+
super()._add(name, typ)
549568
# Instance port.
550569
inst_ref = LazyInstRef(name=name)
551570
inst_port = _make_port(typ, inst_ref, flip=False)
552571
self._inst_ports[name] = inst_port
553572

554573
def __add__(self, other):
555-
raise NotImplementedError(f"Addition operator disallowed on "
556-
f"{cls.__name__}")
574+
raise NotImplementedError(
575+
f"Addition operator disallowed on {cls.__name__}"
576+
)
557577

558578
def flip(self):
559579
raise NotImplementedError()

0 commit comments

Comments
 (0)