hdl._nir, back.rtlil: use Format.* to emit enum attributes and wires for fields.

This commit is contained in:
Wanda 2024-04-11 19:53:49 +02:00 committed by Catherine
parent 4cb2dde25f
commit 580706fafd
7 changed files with 203 additions and 9 deletions

View file

@ -3,6 +3,7 @@ from contextlib import contextmanager
import io
from ..utils import bits_for
from .._utils import to_binary
from ..lib import wiring
from ..hdl import _repr, _ast, _ir, _nir
@ -421,6 +422,7 @@ class ModuleEmitter:
self.emit_cell_wires()
self.emit_submodule_wires()
self.emit_connects()
self.emit_signal_fields()
self.emit_submodules()
self.emit_cells()
@ -491,12 +493,12 @@ class ModuleEmitter:
attrs.update(signal.attrs)
self.value_src_loc[value] = signal.src_loc
for repr in signal._value_repr:
if repr.path == () and isinstance(repr.format, _repr.FormatEnum):
enum = repr.format.enum
attrs["enum_base_type"] = enum.__name__
for enum_value in enum:
attrs["enum_value_{:0{}b}".format(enum_value.value, len(signal))] = enum_value.name
field = self.netlist.signal_fields[signal][()]
if field.enum_name is not None:
attrs["enum_base_type"] = field.enum_name
if field.enum_variants is not None:
for var_val, var_name in field.enum_variants.items():
attrs["enum_value_" + to_binary(var_val, len(signal))] = var_name
if name in self.module.ports:
port_value, _flow = self.module.ports[name]
@ -666,6 +668,30 @@ class ModuleEmitter:
if name not in self.driven_sigports:
self.builder.connect(wire.name, self.sigspec(value))
def emit_signal_fields(self):
for signal, name in self.module.signal_names.items():
fields = self.netlist.signal_fields[signal]
for path, field in fields.items():
if path == ():
continue
name_parts = [name]
for component in path:
if isinstance(component, str):
name_parts.append(f".{component}")
elif isinstance(component, int):
name_parts.append(f"[{component}]")
else:
assert False # :nocov:
attrs = {}
if field.enum_name is not None:
attrs["enum_base_type"] = field.enum_name
if field.enum_variants is not None:
for var_val, var_name in field.enum_variants.items():
attrs["enum_value_" + to_binary(var_val, len(field.value))] = var_name
wire = self.builder.wire(width=len(field.value), signed=field.signed, attrs=attrs,
name="".join(name_parts), src_loc=signal.src_loc)
self.builder.connect(wire.name, self.sigspec(field.value))
def emit_submodules(self):
for submodule_idx in self.module.submodules:
submodule = self.netlist.modules[submodule_idx]

View file

@ -2095,6 +2095,11 @@ class Signal(Value, DUID, metaclass=_SignalMeta):
self._attrs = OrderedDict(() if attrs is None else attrs)
if isinstance(orig_shape, ShapeCastable):
self._format = orig_shape.format(orig_shape(self), "")
else:
self._format = Format("{}", self)
if decoder is not None:
# The value representation is specified explicitly. Since we do not expose `hdl._repr`,
# this is the only way to add a custom filter to the signal right now.

View file

@ -690,7 +690,7 @@ class NetlistDriver:
class NetlistEmitter:
def __init__(self, netlist: _nir.Netlist, design, *, all_undef_to_ff=False):
def __init__(self, netlist: _nir.Netlist, design: Design, *, all_undef_to_ff=False):
self.netlist = netlist
self.design = design
self.all_undef_to_ff = all_undef_to_ff
@ -776,7 +776,7 @@ class NetlistEmitter:
def emit_operator(self, module_idx: int, operator: str, *inputs: _nir.Value, src_loc):
op = _nir.Operator(module_idx, operator=operator, inputs=inputs, src_loc=src_loc)
return self.netlist.add_value_cell(op.width, op)
def emit_matches(self, module_idx: int, value: _nir.Value, patterns, *, src_loc):
key = module_idx, value, patterns, src_loc
try:
@ -1334,6 +1334,42 @@ class NetlistEmitter:
else:
raise ValueError(f"Invalid port direction {dir!r}")
def emit_signal_fields(self):
for signal, fragment in self.design.signal_lca.items():
module_idx = self.fragment_module_idx[fragment]
fields = {}
def emit_format(path, fmt):
if isinstance(fmt, _ast.Format):
specs = [
chunk[0]
for chunk in fmt._chunks
if not isinstance(chunk, str)
]
if len(specs) != 1:
return
val, signed = self.emit_rhs(module_idx, specs[0])
fields[path] = _nir.SignalField(val, signed=signed)
elif isinstance(fmt, _ast.Format.Enum):
val, signed = self.emit_rhs(module_idx, fmt._value)
fields[path] = _nir.SignalField(val, signed=signed,
enum_name=fmt._name,
enum_variants=fmt._variants)
elif isinstance(fmt, _ast.Format.Struct):
val, signed = self.emit_rhs(module_idx, fmt._value)
fields[path] = _nir.SignalField(val, signed=signed)
for name, subfmt in fmt._fields.items():
emit_format(path + (name,), subfmt)
elif isinstance(fmt, _ast.Format.Array):
val, signed = self.emit_rhs(module_idx, fmt._value)
fields[path] = _nir.SignalField(val, signed=signed)
for idx, subfmt in enumerate(fmt._fields):
emit_format(path + (idx,), subfmt)
emit_format((), signal._format)
val, signed = self.emit_rhs(module_idx, signal)
if () not in fields or fields[()].value != val:
fields[()] = _nir.SignalField(val, signed=signed)
self.netlist.signal_fields[signal] = fields
def emit_drivers(self):
for driver in self.drivers.values():
if (driver.domain is not None and
@ -1452,6 +1488,7 @@ class NetlistEmitter:
for subfragment, _name, sub_src_loc in fragment.subfragments:
self.emit_fragment(subfragment, module_idx, cell_src_loc=sub_src_loc)
if parent_module_idx is None:
self.emit_signal_fields()
self.emit_drivers()
self.emit_top_ports(fragment)
if self.all_undef_to_ff:

View file

@ -289,6 +289,22 @@ class Format:
chunk.value = netlist.resolve_value(chunk.value)
class SignalField:
"""Describes a single field of a signal."""
def __init__(self, value, *, signed, enum_name=None, enum_variants=None):
self.value = Value(value)
self.signed = bool(signed)
self.enum_name = enum_name
self.enum_variants = enum_variants
def __eq__(self, other):
return (type(self) is type(other) and
self.value == other.value and
self.signed == other.signed and
self.enum_name == other.enum_name and
self.enum_variants == other.enum_variants)
class Netlist:
"""A fine netlist. Consists of:
@ -321,6 +337,7 @@ class Netlist:
connections : dict of (negative) int to int
io_ports : list of ``IOPort``
signals : dict of Signal to ``Value``
signal_fields: dict of Signal to dict of tuple[str | int] to SignalField
last_late_net: int
"""
def __init__(self):
@ -329,6 +346,7 @@ class Netlist:
self.connections: dict[Net, Net] = {}
self.io_ports: list[_ast.IOPort] = []
self.signals = SignalDict()
self.signal_fields = SignalDict()
self.last_late_net = 0
def resolve_net(self, net: Net):
@ -345,6 +363,9 @@ class Netlist:
cell.resolve_nets(self)
for sig in self.signals:
self.signals[sig] = self.resolve_value(self.signals[sig])
for fields in self.signal_fields.values():
for field in fields.values():
field.value = self.resolve_value(field.value)
def __repr__(self):
result = ["("]