Source code for elasticai.creator.ir2verilog.language
from abc import ABC, abstractmethod
from typing import Iterator, Self
from elasticai.creator.hdl_ir import Node
[docs]
class BaseWire(ABC):
types: set[type["BaseWire"]] = set()
def __init__(self, name: str):
self._name = name
@property
def name(self) -> str:
return self._name
[docs]
@abstractmethod
def define(self) -> Iterator[str]: ...
[docs]
@classmethod
def from_code(cls, code: str) -> "BaseWire":
for t in cls.types:
if t.can_create_from_code(code):
return t.from_code(code)
return NullWire.from_code(code)
[docs]
@classmethod
@abstractmethod
def can_create_from_code(cls, code: str) -> bool: ...
[docs]
@classmethod
def register_type(cls, t: type["BaseWire"]) -> None:
cls.types.add(t)
[docs]
@abstractmethod
def make_instance_specific(self, instance: str) -> "BaseWire": ...
[docs]
class NullWire(BaseWire):
def __init__(self, name):
self._name = name
[docs]
def define(self) -> Iterator[str]:
yield from []
[docs]
@classmethod
def can_create_from_code(cls, code: str) -> bool:
return False
[docs]
@classmethod
def from_code(cls, code: str) -> BaseWire:
return cls("<unknown>")
[docs]
def make_instance_specific(self, instance: str) -> BaseWire:
return self
[docs]
class Wire(BaseWire):
[docs]
def define(self) -> Iterator[str]:
yield f"wire {self.name};"
[docs]
@classmethod
def can_create_from_code(cls, code: str) -> bool:
return False
[docs]
def make_instance_specific(self, instance) -> Self:
return type(self)(f"{self.name}_{instance}")
[docs]
class VectorWire(BaseWire):
def __init__(self, name: str, width: int):
super().__init__(name)
self._width = width
@property
def width(self) -> int:
return self._width
[docs]
def define(self) -> Iterator[str]:
yield f"wire [{self.width - 1}:0] {self.name};"
[docs]
@classmethod
def can_create_from_code(cls, code: str) -> bool:
return False
[docs]
def make_instance_specific(self, instance: str) -> Self:
return type(self)(f"{self.name}_{instance}", self._width)
[docs]
class Instance:
"""Represents a Verilog module instance.
Aggregates all knowledge necessary to instantiate and use
a Verilog module programmatically when generating code.
"""
def __init__(
self,
node: Node,
parameters: dict[str, str],
ports: dict[str, BaseWire],
) -> None:
self._node = node
self._parameters = {k.upper(): v for k, v in parameters.items()}
self._ports = {k: v.make_instance_specific(node.name) for k, v in ports.items()}
@property
def name(self) -> str:
return self._node.name
@property
def implementation(self) -> str:
return self._node.implementation
[docs]
def define_signals(self) -> Iterator[str]:
"""Generate wire definitions for all ports."""
for wire in self._ports.values():
yield from wire.define()
[docs]
def instantiate(self) -> Iterator[str]:
"""Generate Verilog module instantiation code."""
# Generate parameter list if any
params = tuple(self._parameters.items())
if params:
yield f"{self.implementation} #("
for i, (key, value) in enumerate(params):
if i < len(params) - 1:
yield f" .{key}({value}),"
else:
yield f" .{key}({value})"
yield f") {self.name} ("
else:
yield f"{self.implementation} {self.name} ("
# Generate port connections
port_list = tuple(self._ports.items())
for i, (port_name, wire) in enumerate(port_list):
if i < len(port_list) - 1:
yield f" .{port_name}({wire.name}),"
else:
yield f" .{port_name}({wire.name})"
yield ");"