Skip to content

Core Modules

Modules:

  • fxp_arithmetic

Classes:

FxpArithmetic dataclass

FxpArithmetic(fxp_params: FxpParams)

Methods:

cut_as_integer

cut_as_integer(number: float | int) -> int
cut_as_integer(number: list) -> list
cut_as_integer(number: T) -> T

Cutting input number to integer directly (more like in hardware)

round_to_integer

round_to_integer(number: float | int) -> int
round_to_integer(number: T) -> T

Mathematical Round function for number

Modules:

  • base_graph
  • graph_iterators
  • graph_rewriting
  • vf2

Classes:

  • BaseGraph

    All iterators in this class are guaranteed to have a fixed

Functions:

BaseGraph

BaseGraph()

Bases: Graph[T]

All iterators in this class are guaranteed to have a fixed order. That means, the order of iteration is only allowed to change when the data Tucture is altered. If the content of two GraphDelegates is the same and they were built in the same way, then their iteration order is the same as well.

Note

This class is not thread-safe.

Note

We are not providing methods for removal of nodes or edges on purpose. If you need to remove nodes or edges, you should create a new GraphDelegate. Manipulation of the graph should usually be done in a dedicated build phase.

Methods:

  • get_edges

    Iterator over edges in a fixed but unspecified order.

  • get_predecessors

    Iterator over node predecessors in a fixed but unspecified order.

  • get_successors

    Iterator over node successors in a fixed but unspecified order.

  • iter_edges

    Iterator over edges in a fixed but unspecified order.

  • iter_nodes

    Iterator over nodes in a fixed but unspecified order.

get_edges

get_edges() -> Iterator[tuple[T, T]]

Iterator over edges in a fixed but unspecified order.

get_predecessors

get_predecessors(node: T) -> Iterator[T]

Iterator over node predecessors in a fixed but unspecified order.

get_successors

get_successors(node: T) -> Iterator[T]

Iterator over node successors in a fixed but unspecified order.

iter_edges

iter_edges() -> Iterator[tuple[T, T]]

Iterator over edges in a fixed but unspecified order.

iter_nodes

iter_nodes() -> Iterator[T]

Iterator over nodes in a fixed but unspecified order.

bfs_iter_down

bfs_iter_down(
    successors: NodeNeighbourFn, predecessors: NodeNeighbourFn, start: set[T]
) -> Iterator[T]
bfs_iter_down(
    successors: NodeNeighbourFn, predecessors: NodeNeighbourFn, start: T
) -> Iterator[T]

Iterate graph nodes in breadth first.

This ensures that no node will be visited before each of its predecessors was visited.

bfs_iter_up

bfs_iter_up(
    successors: NodeNeighbourFn, predecessors: NodeNeighbourFn, start: T
) -> Iterator[T]
bfs_iter_up(
    successors: NodeNeighbourFn, predecessors: NodeNeighbourFn, start: set[T]
) -> Iterator[T]

Iterate graph nodes in breadth first.

This ensures that no node will be visited before each of its successors was visited.

get_rewriteable_matches

get_rewriteable_matches(
    original: Graph[T], matches: Iterable[dict[TP, T]], interface_nodes: Iterable[TP]
) -> Iterator[dict[TP, T]]

Yield all matches that do produce dangling edges and do not overlap with previous matches.

The matches returned by this function are considerd safe to be rewritten in a single rewriting step in any order, without having to run an additional matching step and without producing dangling edges.

:param original: The original graph. :param matches: The matches to check. :param interface_nodes: All nodes in the pattern that are belong to the interface. These nodes are considered to be preserved during rewriting. Thus, the edges connected to these nodes are not considered dangling.

produces_dangling_edge

produces_dangling_edge(
    graph: Graph[T], match: dict[TP, T], interface_nodes: Iterable[TP]
) -> bool

Check if there are dangling edges attached to non-interface nodes.

Intermediate Representation

Modules:

  • datagraph
  • datagraph_impl
  • datagraph_rewriting
  • deserializer
  • serializer

Classes:

Functions:

  • attribute

    Create AttributeMapping from other (native) data types recursively.

AttributeMapping

AttributeMapping(**kwargs: Attribute)

Bases: Mapping[str, Attribute]

Methods:

  • merge

    merge over nested mappings recursively

  • new_with

    replace key, value pairs in self by key, value pairs in kwargs

  • update_path

    update the entry found when following the path into nested Mappings

merge

merge(other: Mapping) -> Self

merge over nested mappings recursively

So instead of replacing a value found under a key, this checks wether that value is again an AttributeMapping and if so, updates it by the corresponding Mapping found in other. This happens recursively.

Opposed to that new_with and the | operator only allow to update values in the most outer AttributeMapping. Therefore using these to update a value in a nested structure, users would have to recreate the whole outer mapping structure until they reach the mapping they want to update.

Use Case:

If you would want to write

data = dict(a=dict(b=1, c=2))
data["a"]["b"] = 3

assert data["a"]["c"] == 2

You can instead

data = AttributeMapping(a=AttributeMapping(b=1, c=2))
data = data.merge(dict(a=dict(b=3)))
assert data["a"]["c"] == 2

If you want to update a single value, you can use the update_path() function instead to avoid having to build all the nested dictionaries.

new_with

new_with(**kwargs: Attribute) -> AttributeMapping

replace key, value pairs in self by key, value pairs in kwargs

update_path

update_path(path: tuple[str, ...], value: Attribute) -> Self

update the entry found when following the path into nested Mappings

DataGraph

Bases: ReadOnlyDataGraph[N, E], Graph[str, AttributeMapping], Protocol[N, E]

Note that the fact, that add_node and similar methods take an argument of type Node while the nodes and edges properties produce mappings over the generic type parameters E and N. This makes DataGraph covariant over N and E, meaning that DataGraph[SpecialNode, SpecialEdge] is a subtype of DataGraph[Node, Edge]. It also means that every implementation detail in a DataGraph implementation needs to assume that nodes and edges are of the most general Node or Edge type.

Methods:

  • add_edges

    Updates edge in case it exists already. Possibly already existing nodes remain unchanged.

  • add_nodes

    Updates node in case it exists already.

  • clear

    Get a new empty graph with attributes, nodes, edges removed.

  • remove_edge

    Will not remove nodes, even if they become isolated.

  • remove_node

    Will remove node and all connected edges.

add_edges abstractmethod

add_edges(*args: Edge | tuple[str, str, AttributeMapping] | tuple[str, str]) -> Self

Updates edge in case it exists already. Possibly already existing nodes remain unchanged.

add_nodes abstractmethod

add_nodes(*args: Node | tuple[str, AttributeMapping] | str) -> Self

Updates node in case it exists already.

clear abstractmethod

clear() -> Self

Get a new empty graph with attributes, nodes, edges removed.

remove_edge abstractmethod

remove_edge(src: str, dst: str) -> Self

Will not remove nodes, even if they become isolated.

remove_node abstractmethod

remove_node(node: str) -> Self

Will remove node and all connected edges.

DataGraphImpl

DataGraphImpl(
    factory: NodeEdgeFactory[N, E],
    attributes: AttributeMapping,
    graph: Graph[str, AttributeMapping],
    node_attributes: AttributeMapping,
)

Bases: DataGraph[N, E]

Keep constructor signature when subclassing!

Methods:

  • add_edge

    Updates edge in case it exists already. Possibly already existing nodes remain unchanged.

  • add_edges

    Updates edges in case they exist already. Possibly already existing nodes remain unchanged.

  • add_node

    Updates node in case it exists already.

  • add_nodes

    Updates nodes in case they exist already.

  • clear

    Get a new empty graph with attributes, nodes, edges removed.

  • remove_edge

    Will not remove nodes, even if they become isolated.

  • remove_node

    Will remove node and all connected edges.

add_edge

add_edge(
    src: str | Edge, dst: str | None = None, attributes: AttributeMapping | None = None
) -> Self

Updates edge in case it exists already. Possibly already existing nodes remain unchanged.

add_edges

add_edges(*edges: Edge | tuple[str, str, AttributeMapping] | tuple[str, str]) -> Self

Updates edges in case they exist already. Possibly already existing nodes remain unchanged.

add_node

add_node(name: str | Node, attributes: AttributeMapping | None = None) -> Self

Updates node in case it exists already.

add_nodes

add_nodes(*nodes: Node | tuple[str, AttributeMapping] | str) -> Self

Updates nodes in case they exist already.

clear

clear() -> Self

Get a new empty graph with attributes, nodes, edges removed.

remove_edge

remove_edge(src: str, dst: str) -> Self

Will not remove nodes, even if they become isolated.

remove_node

remove_node(node: str) -> Self

Will remove node and all connected edges.

IrDeserializerLegacy

IrDeserializerLegacy(factory: IrFactory[N, E, G])

Deserializes to the legacy format.

The only difference is that attributes are not stored in a dedicated field but at the top-level dict. Use this if you need to load an IR from data that was serialized using the IrData data types.

IrSerializerLegacy

IrSerializerLegacy()

Serializer for the legacy format.

The only difference is that the attributes are not saved in a dedicated field, but instead directly in the top-level dict.

Use this if you need to store the data in a format that can be read by the old IrData based implementations.

attribute

attribute(**kwargs: AttributeConvertable) -> AttributeMapping
attribute(
    mapping: Mapping[str, AttributeConvertable], /, **kwargs: AttributeConvertable
) -> AttributeMapping
attribute(attribute: Attribute) -> Attribute

Create AttributeMapping from other (native) data types recursively.

The implementation assumes that any encountered AttributeMapping objects are correct, ie. they only contain Attributes themselves.

Plugin System

Modules:

  • loading
  • plugin_loader_base
  • plugin_spec
  • read_specs_from_toml

    Provides ways to use and extend the elasticai.creator plugin system.

Classes:

Functions:

PluginLoaderBase

PluginLoaderBase(spec: type[PS])

PluginLoader for Ir2Verilog passes.

PluginSpec dataclass

PluginSpec(
    name: str,
    target_platform: str,
    target_runtime: str,
    version: str,
    api_version: str,
    package: str,
)

The specification of a plugin.

Typically built by reading a dictionary from a toml file and building the spec using <build_plugin_spec()>>. The dataclass is only used to provide convenient access to the fields, support type checking and improve code readability.

You can achieve your goals just as well with the PluginDict dictionary. That is defined as an alias for dict[str, str | tuple[str, ...]].

build_plugin_spec

build_plugin_spec(d: PluginMap, spec_type: type[SpecT]) -> SpecT

inspect spec_type and build an instance of it from the dictionary d.

Missing field raise an error while extra fields will be ignored.

import_symbols

import_symbols(module: str, names: Iterable[str]) -> Iterator[Any]

import names from a module and yield the resulting objects.

read_plugin_dicts_from_package

read_plugin_dicts_from_package(package: str) -> Iterable[PluginMap]

read the meta.toml file from the package returning the list of plugin dictionaries.

Torch2IR

Transform PyTorch models to IR.

The translation process is highly customizable. (Torch2Ir)[elasticai.creator.torch2ir.Torch2Ir] is responsible for the translation process and will call module handlers to extract attributes from modules. These attributes are then attached to the corresponding nodes in the IR.

Each module handler is a function that takes a PyTorch module and returns a dictionary with the extracted attributes.

The Torch2Ir class features some factory methods as class methods, e.g., (Torch2Ir.get_default_converter)[elasticai.creator.torch2ir.Torch2Ir.get_default_converter]. Those will create a new Torch2Ir instance and register some preconfigured module handlers. However, you are free to extend or alter the behaviour of the translation process by registering your own module handlers.

IR2Torch

Modules:

  • ir2torch

Classes:

Ir2Torch

Methods:

  • __call__

    Rebuild the original pytorch model from a given IR.

__call__

__call__(
    ir_root: DataGraph,
    registry: Registry[DataGraph],
    state_dict: dict[str, Any] | None = None,
) -> Module

Rebuild the original pytorch model from a given IR.

Implemenation names containing dots will result in the corresponding modules sorted into a corresponding object hierarchy. E.g., for the implementation name 'top.linear' we will create a pytorch container module under the name 'top' and add the linear layer to it under the name 'linear'. Note that this is an implementation detail of Ir2Torch and not a semantic meaning assigned to the '.' character.

:param: ir: You need to make sure that Ir2Torch has a type handler for each implementation in ir :param: state_dict: You can optionally pass a state dict. This should be a state dict created from the original model via nn.Module.state_dict. As the Torch2Ir stage got rid of all duplicate submodules, we will strip all unknown keys from the state_dict and then load it.

IR2VHDL

Modules:

  • ir2vhdl
  • language
  • vhdl_template

Classes:

EntityTemplateDirector

EntityTemplateDirector()

Turn a vhdl prototype into a template.

Resulting template will have a parameter called entity that you can set to define the name of the represented entity. Replace constants/variables/generics with add_value().

Methods:

  • add_value

    add a parameter for a generic/constant/variable/signal.

add_value

add_value(name: str) -> EntityTemplateDirector

add a parameter for a generic/constant/variable/signal.

EntityTemplateParameter

EntityTemplateParameter()

Bases: AnalysingTemplateParameter

Find the entity in vhdl code and make its name available as a template paramter.

Assumes that there is only one entity in the provided prototype.

NOTE: Will only replace occurences where name is followed and preceded by a non-word character, ie. for an entity named 'skeleton' the occurence 'architecture rtl of skeleton is' will be replaced by 'architecture rtl of $entity is' but the occurences 'skeleton_pkg', 'skeleton;', 'skeleton-' or skeleton. will remain unaltered. IMPORTANT: This detail is likely to change in the future.

Instance

Instance(node: Node, generic_map: dict[str, str], port_map: dict[str, Signal])

Represents an entity that we can/want instantiate.

The aggregates all the knowledge that is necessary to instantiate and use the corresponding entity programmatically, when generating vhdl code.

InstanceFactory

Automatically creates Instances from VhdlNodes based on their type field.

Node

Bases: Node, Protocol

Extending ir.core.Node to an hdl specific node.

This node contains all knowledge that we need to create and use an instance of an hdl component. However, this becomes a little bit complicated because vhdl differentiates between

Attributes:

implementation:: The name of the implementation, e.g., entity name in vhdl or module name for verilog, will be used to derive the architecture name. E.g., if the implementation is "adder", we will instantiate the entity work.adder(rtl). CAUTION: This behaviour is subject to change. Future versions might require the full entity name

PluginLoader

PluginLoader(lowering: Ir2Vhdl)

Bases: PluginLoaderBase

PluginLoader for Ir2Vhdl passes.

Shape

Shape(width: int)
Shape(depth: int, width: int)
Shape(depth: int, width: int, height: int)
  • depth, width
  • depth, width, height

Usually width is kernel_size, depth is channels

ValueTemplateParameter

ValueTemplateParameter(name: str)

Bases: TemplateParameter

Find a value definition and make it settable via a template parameter.

Searches for vhdl value definitions of the form identifier : type or identifier : type := and transforms them into identifier : type := $identifier.

Essentially allows to replace generics as well as variable or signal initializations.

IR2Verilog

Modules:

  • ir2verilog
  • language
  • templates

Classes:

  • Instance

    Represents a Verilog module instance.

  • Node

    Extending ir.core.Node to an hdl specific node.

  • PluginLoader

    PluginLoader for Ir2Verilog passes.

  • Shape
  • TemplateDirector

    Director for building verilog templates.

Instance

Instance(node: Node, parameters: dict[str, str], ports: dict[str, BaseWire])

Represents a Verilog module instance.

Aggregates all knowledge necessary to instantiate and use a Verilog module programmatically when generating code.

Methods:

define_signals

define_signals() -> Iterator[str]

Generate wire definitions for all ports.

instantiate

instantiate() -> Iterator[str]

Generate Verilog module instantiation code.

Node

Bases: Node, Protocol

Extending ir.core.Node to an hdl specific node.

This node contains all knowledge that we need to create and use an instance of an hdl component. However, this becomes a little bit complicated because vhdl differentiates between

Attributes:

implementation:: The name of the implementation, e.g., entity name in vhdl or module name for verilog, will be used to derive the architecture name. E.g., if the implementation is "adder", we will instantiate the entity work.adder(rtl). CAUTION: This behaviour is subject to change. Future versions might require the full entity name

PluginLoader

PluginLoader(lowering: Ir2Verilog)

Bases: PluginLoaderBase

PluginLoader for Ir2Verilog passes.

Shape

Shape(width: int)
Shape(depth: int, width: int)
Shape(depth: int, width: int, height: int)
  • depth, width
  • depth, width, height

Usually width is kernel_size, depth is channels

TemplateDirector

TemplateDirector()

Director for building verilog templates.

Most methods correspond to verilog language constructs. Building the final template can be expensive! Typically you only want to do this once. E.g.:

from string import Template
from elasticai.creator.ir2verilog import type_handler, Code, TemplateDirector, Implementation

class _ExplodingTemplate:
    def substitute(self, params):
        raise RuntimeError("Template not initialized!")

_template = None

@type_handler
def fir_filter(impl: Implementation) -> Code:
    global _template
    if _template is None:
        _template = (
            TemplateDirector()
            .define_scoped_switch("USE_EXT_WEIGHTS", False)
            .define_scoped_switch("USE_EXT_MAC", False)
            .parameter("BITWIDTH")
            .parameter("LENGTH")
            .localparam("FILT_COEFFS")
            .localparam("NUM_MULT_UNIT")
            .add_module_name()
            .set_prototype(res.read_text(package_path, "verilog/filter_fir_full.v"))
            .build()
        )
    return _template.substitute(impl.attributes)

Methods:

define_scoped_switch

define_scoped_switch(name: str, default: bool) -> Self

Add a switch for a define that is scoped to the module name.

The switch will be prefixed with the value that users provide as module_name to the render call. :param name: String with name of the switch/define name :param default: Setting switch for defining output state (True=set, False=undefine)

HDL Generator

HDL (Hardware Description Language) Generator Abstraction.

This module provides a unified interface for generating VHDL and Verilog code. It abstracts away the differences between the two languages, allowing you to write code generation logic once and target either HDL.

The abstraction uses a Protocol to define the interface, with private concrete implementations for each language. Use create_generator() to obtain a generator.

Example
from elasticai.creator.hdl_generator import create_generator, HDLLanguage

# Create a generator for VHDL - returns HDLGenerator protocol type
generator = create_generator(HDLLanguage.VHDL)

# Create signals/wires - returns HDLSignal protocol type
clk = generator.create_signal("clk", width=1)
data = generator.create_signal("data", width=8)

# Instantiate a module/entity - returns HDLInstance protocol type
instance = generator.create_instance(
    node=my_node,
    generics={"WIDTH": "8"},
    ports={"clk": clk, "data": data}
)

# Generate code
for line in instance.instantiate():
    print(line)

Modules:

  • factory

    HDL generators for VHDL and Verilog code generation.

  • protocols

    Protocol definitions for HDL abstraction.

  • verilog_impl

    Verilog implementation of HDL abstractions.

  • vhdl_impl

    VHDL implementation of HDL abstractions.

Classes:

Functions:

HDLGenerator

Bases: 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
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")})

Methods:

create_instance

create_instance(
    node: Node,
    generics: dict[str, str] | None = None,
    ports: dict[str, TSignal] | None = None,
) -> HDLInstance

Create a module/entity instance.

Parameters:

  • node

    (Node) –

    The node representing the module/entity to instantiate.

  • generics

    (dict[str, str] | None, default: None ) –

    Generic/parameter values (name -> value). Optional.

  • ports

    (dict[str, TSignal] | None, default: None ) –

    Port connections (port_name -> signal). Must be signals created by the same generator. Optional.

Returns:

  • HDLInstance

    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.

create_null_signal

create_null_signal(name: str) -> TSignal

Create a signal/wire that doesn't need definition.

Useful for input ports or signals that are defined elsewhere.

Parameters:

  • name

    (str) –

    The signal/wire name.

Returns:

  • TSignal

    A language-specific signal that produces no definition code.

create_signal

create_signal(name: str, width: int | None = None) -> TSignal

Create a signal/wire.

Creates a VHDL signal or Verilog wire depending on the language.

Parameters:

  • name

    (str) –

    The signal/wire name.

  • width

    (int | None, default: None ) –

    The bit width. If None or 1, creates a single-bit signal. Otherwise creates a vector signal/wire.

Returns:

  • TSignal

    A language-specific signal that can be used with create_instance.

Example
gen = VHDLGenerator()
clk = gen.create_signal("clk")  # Single bit
data = gen.create_signal("data", width=8)  # 8-bit vector

create_template_director

create_template_director() -> HDLTemplateDirector

Create a template director for building HDL templates.

Template directors allow you to convert prototype HDL code into parameterized templates.

Returns:

Example
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")

HDLInstance

Bases: Protocol

Protocol for HDL module/entity instances.

Represents either a VHDL entity instance or a Verilog module instance.

Methods:

  • define_signals

    Generate HDL code lines to define all signals/wires used by this instance.

  • instantiate

    Generate HDL code lines to instantiate this module/entity.

Attributes:

implementation abstractmethod property

implementation: str

The name of the module/entity being instantiated.

name abstractmethod property

name: str

The instance name.

define_signals abstractmethod

define_signals() -> Iterator[str]

Generate HDL code lines to define all signals/wires used by this instance.

instantiate abstractmethod

instantiate() -> Iterator[str]

Generate HDL code lines to instantiate this module/entity.

HDLLanguage

Bases: Enum

Supported HDL languages.

HDLSignal

Bases: Protocol

Protocol for HDL signals/wires.

Represents either a VHDL signal or a Verilog wire.

Methods:

  • define

    Generate HDL code lines to define this signal/wire.

  • make_instance_specific

    Create a signal/wire with an instance-specific name.

Attributes:

  • name (str) –

    The name of the signal/wire.

name abstractmethod property

name: str

The name of the signal/wire.

define abstractmethod

define() -> Iterator[str]

Generate HDL code lines to define this signal/wire.

make_instance_specific abstractmethod

make_instance_specific(instance: str) -> HDLSignal

Create a signal/wire with an instance-specific name.

Parameters:

  • instance

    (str) –

    The instance name to append to the signal name.

Returns:

  • HDLSignal

    A new signal with the modified name.

HDLTemplateDirector

Bases: Protocol

Protocol for HDL template directors.

Provides a builder interface for creating HDL templates from prototype code.

Methods:

  • add_parameter

    Add a template parameter (generic/parameter).

  • build

    Build the final template.

  • set_prototype

    Set the prototype HDL code to use as a template base.

add_parameter abstractmethod

add_parameter(name: str) -> HDLTemplateDirector

Add a template parameter (generic/parameter).

Parameters:

  • name

    (str) –

    The name of the parameter to make templatable.

Returns:

build abstractmethod

build() -> Template

Build the final template.

Returns:

  • Template

    A string.Template that can be used to generate code.

set_prototype abstractmethod

set_prototype(prototype: str) -> HDLTemplateDirector

Set the prototype HDL code to use as a template base.

Parameters:

  • prototype

    (str) –

    The HDL code to convert into a template.

Returns:

create_generator

create_generator(language: Literal[VHDL]) -> HDLGenerator[Signal]
create_generator(language: Literal[VERILOG]) -> 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.

Parameters:

  • language

    (HDLLanguage) –

    The target HDL language (VHDL or VERILOG).

Returns:

Example
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!

HDL IR

Classes:

  • Node

    Extending ir.core.Node to an hdl specific node.

  • Shape

Node

Bases: Node, Protocol

Extending ir.core.Node to an hdl specific node.

This node contains all knowledge that we need to create and use an instance of an hdl component. However, this becomes a little bit complicated because vhdl differentiates between

Attributes:

implementation:: The name of the implementation, e.g., entity name in vhdl or module name for verilog, will be used to derive the architecture name. E.g., if the implementation is "adder", we will instantiate the entity work.adder(rtl). CAUTION: This behaviour is subject to change. Future versions might require the full entity name

Shape

Shape(width: int)
Shape(depth: int, width: int)
Shape(depth: int, width: int, height: int)
  • depth, width
  • depth, width, height

Usually width is kernel_size, depth is channels

VHDL

Modules:

  • auto_wire_protocols
  • system_integrations

File Generation

Modules:

  • resource_utils
  • template

Base Modules