Source code for elasticai.creator.hdl_generator.factory

"""HDL generators for VHDL and Verilog code generation.

This module provides concrete implementations for generating VHDL and Verilog code.
Each generator encapsulates language-specific factories and provides a unified interface.
"""

from enum import Enum, auto
from typing import Literal, Protocol, overload

from elasticai.creator.hdl_ir import Node
from elasticai.creator.ir2verilog import BaseWire
from elasticai.creator.ir2vhdl import Signal

from . import verilog_impl, vhdl_impl
from .protocols import HDLInstance, HDLSignal, HDLTemplateDirector

# TypeVar for signal types - covariant so HDLGenerator[Signal] is a subtype of HDLGenerator[HDLSignal]


# Type alias for generic/parameter dictionaries
type GenericMap = dict[str, str]
type ParameterMap = dict[str, str]
type PortMap = dict[str, HDLSignal]


[docs] class HDLLanguage(Enum): """Supported HDL languages.""" VHDL = auto() VERILOG = auto()
[docs] class HDLGenerator[TSignal](Protocol): """Protocol defining the interface for HDL code generation. This protocol is generic over the signal type to provide static type safety. When you create a VHDL generator, signals are typed as VHDL Signal objects. When you create a Verilog generator, signals are typed as Verilog BaseWire objects. This prevents accidentally mixing VHDL signals with Verilog instances (and vice versa) at compile time, catching errors before runtime. Example: ```python from elasticai.creator.hdl_generator import create_generator, HDLLanguage from elasticai.creator.hdl_ir import Node, Shape # Create a generator for VHDL - returns HDLGenerator[Signal] vhdl_gen = create_generator(HDLLanguage.VHDL) # Create signals - returns Signal (VHDL-specific type) clk = vhdl_gen.create_signal("clk", width=1) data_in = vhdl_gen.create_signal("data_in", width=8) # Create a Verilog generator - returns HDLGenerator[BaseWire] verilog_gen = create_generator(HDLLanguage.VERILOG) # This would be a TYPE ERROR caught by the type checker: # vhdl_gen.create_instance(ports={"clk": verilog_gen.create_signal("clk")}) ``` """
[docs] def create_signal(self, name: str, width: int | None = None) -> TSignal: """Create a signal/wire. Creates a VHDL signal or Verilog wire depending on the language. Args: name: The signal/wire name. width: The bit width. If None or 1, creates a single-bit signal. Otherwise creates a vector signal/wire. Returns: A language-specific signal that can be used with create_instance. Example: ```python gen = VHDLGenerator() clk = gen.create_signal("clk") # Single bit data = gen.create_signal("data", width=8) # 8-bit vector ``` """ ...
[docs] def create_null_signal(self, name: str) -> TSignal: """Create a signal/wire that doesn't need definition. Useful for input ports or signals that are defined elsewhere. Args: name: The signal/wire name. Returns: A language-specific signal that produces no definition code. """ ...
[docs] def create_instance( self, node: Node, generics: dict[str, str] | None = None, ports: dict[str, TSignal] | None = None, ) -> HDLInstance: """Create a module/entity instance. Args: node: The node representing the module/entity to instantiate. generics: Generic/parameter values (name -> value). Optional. ports: Port connections (port_name -> signal). Must be signals created by the same generator. Optional. Returns: An HDL instance that can generate instantiation code. Note: The type system ensures you can only pass signals created by the same generator. Mixing VHDL signals with a Verilog generator (or vice versa) will be caught as a type error. """ ...
[docs] def create_template_director(self) -> HDLTemplateDirector: """Create a template director for building HDL templates. Template directors allow you to convert prototype HDL code into parameterized templates. Returns: An HDL template director for building templates. Example: ```python gen = VHDLGenerator() prototype_code = ''' entity adder is generic (WIDTH : natural := 8); port ( a, b : in std_logic_vector(WIDTH-1 downto 0); sum : out std_logic_vector(WIDTH-1 downto 0) ); end entity; ''' template = ( gen.create_template_director() .set_prototype(prototype_code) .add_parameter("WIDTH") .build() ) # Use the template code = template.substitute(entity="my_adder", WIDTH="16") ``` """ ...
class _VHDLGenerator: """VHDL implementation of HDL generator. Private class - use create_generator() instead to ensure proper abstraction. """ def create_signal(self, name: str, width: int | None = None) -> Signal: """Create a VHDL signal. Args: name: The signal name. width: The bit width. If None or 1, creates a single-bit signal. Otherwise creates a vector signal. Returns: A VHDL signal. """ return vhdl_impl.create_signal(name, width) def create_null_signal(self, name: str) -> Signal: """Create a VHDL signal that doesn't need definition. Args: name: The signal name. Returns: A VHDL null signal. """ return vhdl_impl.create_null_signal(name) def create_instance( self, node: Node, generics: dict[str, str] | None = None, ports: dict[str, Signal] | None = None, ) -> HDLInstance: """Create a VHDL entity instance. Args: node: The node representing the entity to instantiate. generics: Generic values (name -> value). Optional. ports: Port connections (port_name -> signal). Optional. Returns: A VHDL instance. """ if generics is None: generics = {} if ports is None: ports = {} return vhdl_impl.create_instance(node, generics, ports) def create_template_director(self) -> HDLTemplateDirector: """Create a VHDL template director. Returns: A VHDL template director. """ return vhdl_impl.VHDLTemplateDirector() class _VerilogGenerator: """Verilog implementation of HDL generator. Private class - use create_generator() instead to ensure proper abstraction. """ def create_signal(self, name: str, width: int | None = None) -> BaseWire: """Create a Verilog wire. Args: name: The wire name. width: The bit width. If None or 1, creates a single-bit wire. Otherwise creates a vector wire. Returns: A Verilog wire. """ return verilog_impl.create_signal(name, width) def create_null_signal(self, name: str) -> BaseWire: """Create a Verilog wire that doesn't need definition. Args: name: The wire name. Returns: A Verilog null wire. """ return verilog_impl.create_null_signal(name) def create_instance( self, node: Node, generics: dict[str, str] | None = None, ports: dict[str, BaseWire] | None = None, ) -> HDLInstance: """Create a Verilog module instance. Args: node: The node representing the module to instantiate. generics: Parameter values (name -> value). Optional. ports: Port connections (port_name -> wire). Optional. Returns: A Verilog instance. """ if generics is None: generics = {} if ports is None: ports = {} return verilog_impl.create_instance(node, generics, ports) def create_template_director(self) -> HDLTemplateDirector: """Create a Verilog template director. Returns: A Verilog template director. """ return verilog_impl.VerilogTemplateDirector() @overload def create_generator(language: Literal[HDLLanguage.VHDL]) -> HDLGenerator[Signal]: ... @overload def create_generator( language: Literal[HDLLanguage.VERILOG], ) -> HDLGenerator[BaseWire]: ...
[docs] def create_generator( language: HDLLanguage, ) -> HDLGenerator[Signal] | HDLGenerator[BaseWire]: """Create an HDL generator for the specified language. This factory function provides static type safety through overloads: - When you pass HDLLanguage.VHDL, you get HDLGenerator[Signal] - When you pass HDLLanguage.VERILOG, you get HDLGenerator[BaseWire] This means the type checker can verify that you don't mix VHDL signals with Verilog instances (or vice versa) at compile time. Args: language: The target HDL language (VHDL or VERILOG). Returns: An HDL generator with the appropriate signal type. Example: ```python from elasticai.creator.hdl_generator import create_generator, HDLLanguage # Create VHDL generator - typed as HDLGenerator[Signal] vhdl_gen = create_generator(HDLLanguage.VHDL) vhdl_signal = vhdl_gen.create_signal("data", width=8) # Returns Signal # Create Verilog generator - typed as HDLGenerator[BaseWire] verilog_gen = create_generator(HDLLanguage.VERILOG) verilog_wire = verilog_gen.create_signal("data", width=8) # Returns BaseWire # Type checker will catch this error: # vhdl_gen.create_instance(ports={"data": verilog_wire}) # TYPE ERROR! ``` """ if language == HDLLanguage.VHDL: return _VHDLGenerator() elif language == HDLLanguage.VERILOG: return _VerilogGenerator() else: raise ValueError(f"Unsupported HDL language: {language}")