Skip to content

Commit

Permalink
[PyCDE] Refactor Input/Output ports to extend property (#6700)
Browse files Browse the repository at this point in the history
Specify fget in the port class itself to that users still have access to the port's properties through the class variable instead of replacing the port itself. As a result, we only have to store a list of ports.
  • Loading branch information
teqdruid committed Feb 15, 2024
1 parent 9e2c380 commit d0e332b
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 100 deletions.
6 changes: 3 additions & 3 deletions frontends/PyCDE/src/bsp/cosim.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ def build(ports):
# 20-bit address. Other than that, the ports are the same so use some
# PyCDE magic to wire them together.
cosim_mmio_wire_inputs = {
pn: Wire(ty)
for pn, ty in Cosim_MMIO.inputs()
if pn != "clk" and pn != "rst"
port.name: Wire(port.type)
for port in Cosim_MMIO.inputs()
if port.name != "clk" and port.name != "rst"
}
cosim_mmio = Cosim_MMIO(clk=ports.clk,
rst=ports.rst,
Expand Down
57 changes: 42 additions & 15 deletions frontends/PyCDE/src/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,60 +10,87 @@
from .types import Type, Bundle, Channel, ChannelSignaling, ClockType, Bits

from functools import singledispatchmethod
from typing import Optional
from typing import Callable, Optional


class ModuleDecl:
class ModuleDecl(property):
"""Represents an input or output port on a design module."""

__slots__ = ["name", "_type"]
__slots__ = ["idx", "name", "type"]

def __init__(self, type: Type, name: str = None):
self.name: str = name
self._type: Type = type

@property
def type(self):
return self._type
def __init__(self,
type: Type,
name: Optional[str] = None,
fget: Optional[Callable] = None,
fset: Optional[Callable] = None):
super().__init__(fget=fget, fset=fset)
self.idx: Optional[int] = None
self.name = name
self.type = type


class Output(ModuleDecl):
"""Create an RTL-level output port"""

def __init__(self, type: Type, name: Optional[str] = None):
from .signals import _FromCirctValue

def fget(mod_inst, self=self):
return _FromCirctValue(mod_inst.inst.operation.results[self.idx])

super().__init__(type, name, fget=fget)

def __repr__(self) -> str:
return f"output '{self.name}': {self.type}"


class OutputChannel(Output):
"""Create an ESI output channel port."""

def __init__(self,
type: Type,
signaling: int = ChannelSignaling.ValidReady,
name: str = None):
name: Optional[str] = None):
type = Channel(type, signaling)
super().__init__(type, name)


class SendBundle(Output):
"""Create an ESI bundle output port (aka sending port)."""

def __init__(self, bundle: Bundle, name: str = None):
def __init__(self, bundle: Bundle, name: Optional[str] = None):
super().__init__(bundle, name)


class Input(ModuleDecl):
"""Create an RTL-level input port."""

def __init__(self, type: Type, name: Optional[str] = None):
from .signals import _FromCirctValue

def fget(mod_inst, self=self):
return _FromCirctValue(mod_inst.inst.operation.operands[self.idx])

super().__init__(type, name, fget=fget)

def __repr__(self) -> str:
return f"input '{self.name}': {self.type}"


class Clock(Input):
"""Create a clock input"""

def __init__(self, name: str = None):
def __init__(self, name: Optional[str] = None):
super().__init__(ClockType(), name)

def __repr__(self) -> str:
return f"clock {self.name}"


class Reset(Input):
"""Create a reset input."""

def __init__(self, name: str = None):
def __init__(self, name: Optional[str] = None):
super().__init__(Bits(1), name)


Expand Down Expand Up @@ -104,7 +131,7 @@ def name(self) -> str:
def index(self) -> int:
return self._appid.index

def __str__(self) -> str:
def __repr__(self) -> str:
return f"{self.name}[{self.index}]"


Expand Down
4 changes: 2 additions & 2 deletions frontends/PyCDE/src/esi.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,11 +199,11 @@ def instantiate(self, impl, inputs: Dict[str, Signal], appid: AppID):
if impl.decl is not None:
decl_sym = ir.FlatSymbolRefAttr.get(impl.decl._materialize_service_decl())
return raw_esi.ServiceInstanceOp(
result=[t._type for _, t in self.outputs],
result=[p.type._type for p in self.outputs],
appID=appid._appid,
service_symbol=decl_sym,
impl_type=_ServiceGeneratorRegistry._impl_type_name,
inputs=[inputs[pn].value for pn, _ in self.inputs],
inputs=[inputs[p.name].value for p in self.inputs],
impl_opts=opts,
loc=self.loc)

Expand Down
57 changes: 38 additions & 19 deletions frontends/PyCDE/src/fsm.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
from hmac import new
from .common import Input, Output
from .dialects import fsm
from .module import Module, ModuleLikeBuilderBase
from .support import _obj_to_attribute
from .types import types
from .types import Bits, types

from .circt.ir import FlatSymbolRefAttr, InsertionPoint, StringAttr
from .circt.support import attribute_to_var
from .circt.dialects import fsm as raw_fsm

from typing import Callable
from typing import Callable, Set


class State:
Expand Down Expand Up @@ -110,11 +112,11 @@ def scan_cls(self):
initial_state = name

from .types import ClockType
for name, v in self.inputs:
if not (isinstance(v, ClockType) or
(hasattr(v, "width") and v.width == 1)):
for port in self.inputs:
if not (isinstance(port.type, ClockType) or
(hasattr(port.type, "width") and port.type.width == 1)):
raise ValueError(
f"Input port {name} has width {v.width}. For now, FSMs only "
f"Input port {port.name} has width {port.type.width}. For now, FSMs only "
"support i1 inputs.")

# At this point, the 'states' attribute should be considered an immutable,
Expand All @@ -127,33 +129,50 @@ def scan_cls(self):
"`initial=True`.")

# Add an output port for each state.
num_outputs = len(self.outputs)
for state_name, state in states.items():
state.output = len(self.outputs)
self.outputs.append(('is_' + state_name, types.i1))
o = Output(Bits(1), name="is_" + state_name)
o.idx = num_outputs
num_outputs += 1
setattr(self.modcls, o.name, o)
self.ports.append(o)

inputs_to_remove = []
inputs_to_remove: Set[Input] = set()
if len(self.clocks) > 1:
raise ValueError("FSMs must have at most one clock")
else:
self.clock_name = "clk"
if len(self.clocks) == 1:
idx = self.clocks.pop()
self.clock_name = self.inputs[idx][0]
inputs_to_remove.append(idx)
clock_port = self.inputs[idx]
self.clock_name = clock_port.name
inputs_to_remove.add(clock_port)

if len(self.resets) > 1:
raise ValueError("FSMs must have at most one reset")
else:
self.reset_name = "rst"
if len(self.resets) == 1:
idx = self.resets.pop()
self.reset_name = self.inputs[idx][0]
inputs_to_remove.append(idx)
reset_port = self.inputs[idx]
self.reset_name = reset_port.name
inputs_to_remove.add(reset_port)

# Remove the clock and reset inputs, if necessary.
inputs_to_remove.sort(reverse=True)
for idx in inputs_to_remove:
self.inputs.pop(idx)
new_ports = []
new_num_inputs = 0
for port in self.ports:
if not isinstance(port, Input):
new_ports.append(port)
else:
if port in inputs_to_remove:
port.idx = None
else:
port.idx = new_num_inputs
new_num_inputs += 1
new_ports.append(port)
self.ports = new_ports

def create_op(self, sys, symbol):
"""Creation callback for creating a FSM MachineOp."""
Expand All @@ -164,18 +183,18 @@ def create_op(self, sys, symbol):
# Add attributes for in- and output names.
attributes = {}
attributes["in_names"] = _obj_to_attribute(
[port_name for port_name, _ in self.inputs])
[port.name for port in self.inputs])
attributes["out_names"] = _obj_to_attribute(
[port_name for port_name, _ in self.outputs])
[port.name for port in self.outputs])

# Add attributes for clock and reset names.
attributes["clock_name"] = _obj_to_attribute(self.clock_name)
attributes["reset_name"] = _obj_to_attribute(self.reset_name)

machine_op = fsm.MachineOp(symbol,
self.initial_state,
[(n, t._type) for (n, t) in self.inputs],
[(n, t._type) for (n, t) in self.outputs],
[(p.name, p.type._type) for p in self.inputs],
[(p.name, p.type._type) for p in self.outputs],
attributes=attributes,
loc=self.loc,
ip=sys._get_ip())
Expand Down
Loading

0 comments on commit d0e332b

Please sign in to comment.