2025-10-04 20:57:45 +00:00
|
|
|
import pkgutil
|
2025-09-21 21:08:30 +00:00
|
|
|
from typing import Generator, Iterable, Any
|
2025-08-29 20:58:10 +00:00
|
|
|
from . import binwrite as binw
|
2025-09-20 21:25:07 +00:00
|
|
|
from .stencil_db import stencil_database
|
2025-10-02 21:10:05 +00:00
|
|
|
from collections import defaultdict, deque
|
2025-10-03 21:26:51 +00:00
|
|
|
from coparun_module import coparun, read_data_mem
|
|
|
|
|
import struct
|
2025-10-04 20:57:45 +00:00
|
|
|
import platform
|
2025-05-25 21:23:02 +00:00
|
|
|
|
2025-10-01 21:09:49 +00:00
|
|
|
Operand = type['Net'] | float | int
|
|
|
|
|
|
2025-10-04 21:07:37 +00:00
|
|
|
|
2025-08-29 20:58:10 +00:00
|
|
|
def get_var_name(var: Any, scope: dict[str, Any] = globals()) -> list[str]:
|
2025-05-25 21:23:02 +00:00
|
|
|
return [name for name, value in scope.items() if value is var]
|
|
|
|
|
|
2025-10-04 21:07:37 +00:00
|
|
|
|
2025-10-04 21:07:47 +00:00
|
|
|
def stencil_db_from_package(arch: str = 'native', optimization: str = 'O3') -> stencil_database:
|
2025-10-04 21:07:37 +00:00
|
|
|
if arch == 'native':
|
2025-10-07 21:20:43 +00:00
|
|
|
arch = platform.machine()
|
2025-10-04 21:07:37 +00:00
|
|
|
stencil_data = pkgutil.get_data(__name__, f"obj/stencils_{arch}_{optimization}.o")
|
|
|
|
|
assert stencil_data, f"stencils_{arch}_{optimization} not found"
|
|
|
|
|
return stencil_database(stencil_data)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
generic_sdb = stencil_db_from_package()
|
2025-09-07 21:12:09 +00:00
|
|
|
|
2025-09-11 20:22:17 +00:00
|
|
|
|
2025-05-25 21:23:02 +00:00
|
|
|
class Node:
|
2025-10-01 21:09:49 +00:00
|
|
|
def __init__(self) -> None:
|
2025-08-29 20:58:10 +00:00
|
|
|
self.args: list[Net] = []
|
|
|
|
|
self.name: str = ''
|
|
|
|
|
|
2025-10-01 21:09:49 +00:00
|
|
|
def __repr__(self) -> str:
|
2025-05-25 21:23:02 +00:00
|
|
|
#return f"Node:{self.name}({', '.join(str(a) for a in self.args) if self.args else self.value})"
|
|
|
|
|
return f"Node:{self.name}({', '.join(str(a) for a in self.args) if self.args else (self.value if isinstance(self, Const) else '')})"
|
|
|
|
|
|
2025-09-07 21:12:09 +00:00
|
|
|
|
2025-05-25 21:23:02 +00:00
|
|
|
class Device():
|
|
|
|
|
pass
|
2025-09-07 21:12:09 +00:00
|
|
|
|
|
|
|
|
|
2025-05-25 21:23:02 +00:00
|
|
|
class Net:
|
|
|
|
|
def __init__(self, dtype: str, source: Node):
|
|
|
|
|
self.dtype = dtype
|
|
|
|
|
self.source = source
|
|
|
|
|
|
2025-08-29 20:58:10 +00:00
|
|
|
def __mul__(self, other: Any) -> 'Net':
|
2025-05-25 21:23:02 +00:00
|
|
|
return _add_op('mul', [self, other], True)
|
|
|
|
|
|
2025-08-29 20:58:10 +00:00
|
|
|
def __rmul__(self, other: Any) -> 'Net':
|
2025-05-25 21:23:02 +00:00
|
|
|
return _add_op('mul', [self, other], True)
|
2025-10-01 21:09:49 +00:00
|
|
|
|
2025-08-29 20:58:10 +00:00
|
|
|
def __add__(self, other: Any) -> 'Net':
|
2025-05-25 21:23:02 +00:00
|
|
|
return _add_op('add', [self, other], True)
|
|
|
|
|
|
2025-08-29 20:58:10 +00:00
|
|
|
def __radd__(self, other: Any) -> 'Net':
|
2025-05-25 21:23:02 +00:00
|
|
|
return _add_op('add', [self, other], True)
|
|
|
|
|
|
2025-10-01 21:09:49 +00:00
|
|
|
def __sub__(self, other: Any) -> 'Net':
|
2025-05-25 21:23:02 +00:00
|
|
|
return _add_op('sub', [self, other])
|
|
|
|
|
|
2025-10-01 21:09:49 +00:00
|
|
|
def __rsub__(self, other: Any) -> 'Net':
|
2025-05-25 21:23:02 +00:00
|
|
|
return _add_op('sub', [other, self])
|
|
|
|
|
|
2025-10-01 21:09:49 +00:00
|
|
|
def __truediv__(self, other: Any) -> 'Net':
|
2025-05-25 21:23:02 +00:00
|
|
|
return _add_op('div', [self, other])
|
|
|
|
|
|
2025-10-01 21:09:49 +00:00
|
|
|
def __rtruediv__(self, other: Any) -> 'Net':
|
2025-05-25 21:23:02 +00:00
|
|
|
return _add_op('div', [other, self])
|
|
|
|
|
|
2025-10-04 21:20:25 +00:00
|
|
|
def __floordiv__(self, other: Any) -> 'Net':
|
|
|
|
|
return _add_op('floordiv', [self, other])
|
|
|
|
|
|
|
|
|
|
def __rfloordiv__(self, other: Any) -> 'Net':
|
|
|
|
|
return _add_op('floordiv', [other, self])
|
|
|
|
|
|
2025-10-03 21:26:51 +00:00
|
|
|
def __neg__(self) -> 'Net':
|
|
|
|
|
return _add_op('sub', [const(0), self])
|
|
|
|
|
|
|
|
|
|
def __gt__(self, other: Any) -> 'Net':
|
|
|
|
|
return _add_op('gt', [self, other])
|
|
|
|
|
|
|
|
|
|
def __lt__(self, other: Any) -> 'Net':
|
|
|
|
|
return _add_op('gt', [other, self])
|
|
|
|
|
|
2025-10-04 20:57:45 +00:00
|
|
|
def __eq__(self, other: Any) -> 'Net': # type: ignore
|
2025-10-03 21:26:51 +00:00
|
|
|
return _add_op('eq', [self, other])
|
|
|
|
|
|
|
|
|
|
def __mod__(self, other: Any) -> 'Net':
|
|
|
|
|
return _add_op('mod', [self, other])
|
|
|
|
|
|
|
|
|
|
def __rmod__(self, other: Any) -> 'Net':
|
|
|
|
|
return _add_op('mod', [other, self])
|
|
|
|
|
|
2025-10-01 21:09:49 +00:00
|
|
|
def __repr__(self) -> str:
|
2025-05-25 21:23:02 +00:00
|
|
|
names = get_var_name(self)
|
|
|
|
|
return f"{'name:' + names[0] if names else 'id:' + str(id(self))[-5:]}"
|
|
|
|
|
|
2025-10-03 21:26:51 +00:00
|
|
|
def __hash__(self) -> int:
|
|
|
|
|
return id(self)
|
|
|
|
|
|
2025-05-25 21:23:02 +00:00
|
|
|
|
|
|
|
|
class Const(Node):
|
|
|
|
|
def __init__(self, value: float | int | bool):
|
|
|
|
|
self.dtype, self.value = _get_data_and_dtype(value)
|
|
|
|
|
self.name = 'const_' + self.dtype
|
|
|
|
|
|
|
|
|
|
#if self.name not in _function_definitions:
|
|
|
|
|
# raise ValueError(f"Unsupported operand type for a const: {self.dtype}")
|
|
|
|
|
|
|
|
|
|
self.args = []
|
|
|
|
|
|
2025-09-07 21:12:09 +00:00
|
|
|
|
2025-05-25 21:23:02 +00:00
|
|
|
class Write(Node):
|
|
|
|
|
def __init__(self, net: Net):
|
|
|
|
|
self.name = 'write_' + net.dtype
|
|
|
|
|
self.args = [net]
|
|
|
|
|
|
|
|
|
|
#if self.name not in _function_definitions:
|
|
|
|
|
# raise ValueError(f"Unsupported operand type for write: {net.dtype}")
|
|
|
|
|
|
2025-09-07 21:12:09 +00:00
|
|
|
|
2025-05-25 21:23:02 +00:00
|
|
|
class Op(Node):
|
|
|
|
|
def __init__(self, typed_op_name: str, args: list[Net]):
|
|
|
|
|
assert not args or any(isinstance(t, Net) for t in args), 'args parameter must be of type list[Net]'
|
2025-08-29 20:58:10 +00:00
|
|
|
self.name: str = typed_op_name
|
|
|
|
|
self.args: list[Net] = args
|
2025-05-25 21:23:02 +00:00
|
|
|
|
2025-09-07 21:12:09 +00:00
|
|
|
|
2025-08-29 20:58:10 +00:00
|
|
|
def _add_op(op: str, args: list[Any], commutative: bool = False) -> Net:
|
|
|
|
|
arg_nets = [a if isinstance(a, Net) else const(a) for a in args]
|
2025-05-25 21:23:02 +00:00
|
|
|
|
|
|
|
|
if commutative:
|
2025-08-29 20:58:10 +00:00
|
|
|
arg_nets = sorted(arg_nets, key=lambda a: a.dtype)
|
2025-05-25 21:23:02 +00:00
|
|
|
|
2025-10-01 21:09:49 +00:00
|
|
|
typed_op = '_'.join([op] + [a.dtype for a in arg_nets])
|
2025-05-25 21:23:02 +00:00
|
|
|
|
2025-10-04 20:57:45 +00:00
|
|
|
if typed_op not in generic_sdb.function_definitions:
|
2025-08-29 20:58:10 +00:00
|
|
|
raise ValueError(f"Unsupported operand type(s) for {op}: {' and '.join([a.dtype for a in arg_nets])}")
|
2025-05-25 21:23:02 +00:00
|
|
|
|
2025-10-04 20:57:45 +00:00
|
|
|
result_type = generic_sdb.function_definitions[typed_op].split('_')[0]
|
2025-05-25 21:23:02 +00:00
|
|
|
|
2025-08-29 20:58:10 +00:00
|
|
|
result_net = Net(result_type, Op(typed_op, arg_nets))
|
2025-05-25 21:23:02 +00:00
|
|
|
|
|
|
|
|
return result_net
|
|
|
|
|
|
2025-09-07 21:12:09 +00:00
|
|
|
|
2025-05-25 21:23:02 +00:00
|
|
|
#def read_input(hw: Device, test_value: float):
|
|
|
|
|
# return Net(type(value))
|
|
|
|
|
|
2025-09-07 21:12:09 +00:00
|
|
|
|
2025-08-29 20:58:10 +00:00
|
|
|
def const(value: Any) -> Net:
|
|
|
|
|
assert isinstance(value, (int, float, bool)), f'Unsupported type for const: {type(value).__name__}'
|
2025-05-25 21:23:02 +00:00
|
|
|
new_const = Const(value)
|
|
|
|
|
return Net(new_const.dtype, new_const)
|
|
|
|
|
|
2025-09-07 21:12:09 +00:00
|
|
|
|
2025-08-29 20:58:10 +00:00
|
|
|
def _get_data_and_dtype(value: Any) -> tuple[str, float | int]:
|
2025-05-25 21:23:02 +00:00
|
|
|
if isinstance(value, int):
|
|
|
|
|
return ('int', int(value))
|
|
|
|
|
elif isinstance(value, float):
|
|
|
|
|
return ('float', float(value))
|
|
|
|
|
elif isinstance(value, bool):
|
|
|
|
|
return ('bool', int(value))
|
|
|
|
|
else:
|
|
|
|
|
raise ValueError(f'Non supported data type: {type(value).__name__}')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class vec3d:
|
2025-08-29 20:58:10 +00:00
|
|
|
def __init__(self, value: tuple[Net, Net, Net]):
|
2025-05-25 21:23:02 +00:00
|
|
|
self.value = value
|
|
|
|
|
|
2025-08-29 20:58:10 +00:00
|
|
|
def __add__(self, other: 'vec3d') -> 'vec3d':
|
|
|
|
|
a1, a2, a3 = self.value
|
|
|
|
|
b1, b2, b3 = other.value
|
|
|
|
|
return vec3d((a1 + b1, a2 + b2, a3 + b3))
|
2025-05-25 21:23:02 +00:00
|
|
|
|
|
|
|
|
|
2025-08-29 20:58:10 +00:00
|
|
|
def const_vector3d(x: float, y: float, z: float) -> vec3d:
|
|
|
|
|
return vec3d((const(x), const(y), const(z)))
|
|
|
|
|
|
|
|
|
|
|
2025-10-02 21:10:05 +00:00
|
|
|
def stable_toposort(edges: Iterable[tuple[Node, Node]]) -> list[Node]:
|
2025-10-02 21:23:07 +00:00
|
|
|
"""Perform a stable topological sort on a directed acyclic graph (DAG).
|
|
|
|
|
Arguments:
|
|
|
|
|
edges: Iterable of (u, v) pairs meaning u -> v
|
2025-10-03 21:09:25 +00:00
|
|
|
|
2025-10-02 21:23:07 +00:00
|
|
|
Returns:
|
|
|
|
|
List of nodes in topologically sorted order.
|
|
|
|
|
"""
|
2025-10-03 21:09:25 +00:00
|
|
|
|
2025-10-02 21:10:05 +00:00
|
|
|
# Track adjacency and indegrees
|
|
|
|
|
adj: defaultdict[Node, list[Node]] = defaultdict(list)
|
|
|
|
|
indeg: defaultdict[Node, int] = defaultdict(int)
|
|
|
|
|
order: dict[Node, int] = {} # first-appearance order of each node
|
|
|
|
|
|
|
|
|
|
# Build graph and order map
|
|
|
|
|
pos = 0
|
|
|
|
|
for u, v in edges:
|
|
|
|
|
if u not in order:
|
2025-10-03 21:09:25 +00:00
|
|
|
order[u] = pos
|
|
|
|
|
pos += 1
|
2025-10-02 21:10:05 +00:00
|
|
|
if v not in order:
|
2025-10-03 21:09:25 +00:00
|
|
|
order[v] = pos
|
|
|
|
|
pos += 1
|
2025-10-02 21:10:05 +00:00
|
|
|
adj[u].append(v)
|
|
|
|
|
indeg[v] += 1
|
|
|
|
|
indeg.setdefault(u, 0)
|
|
|
|
|
|
|
|
|
|
# Initialize queue with nodes of indegree 0, sorted by first appearance
|
|
|
|
|
queue = deque(sorted([n for n in indeg if indeg[n] == 0], key=lambda x: order[x]))
|
|
|
|
|
result: list[Node] = []
|
|
|
|
|
|
|
|
|
|
while queue:
|
|
|
|
|
node = queue.popleft()
|
|
|
|
|
result.append(node)
|
|
|
|
|
|
|
|
|
|
for nei in adj[node]:
|
|
|
|
|
indeg[nei] -= 1
|
|
|
|
|
if indeg[nei] == 0:
|
|
|
|
|
queue.append(nei)
|
|
|
|
|
|
|
|
|
|
# Maintain stability: sort queue by appearance order
|
|
|
|
|
queue = deque(sorted(queue, key=lambda x: order[x]))
|
|
|
|
|
|
|
|
|
|
# Check if graph had a cycle (not all nodes output)
|
|
|
|
|
if len(result) != len(indeg):
|
|
|
|
|
raise ValueError("Graph contains a cycle — topological sort not possible")
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_all_dag_edges(nodes: Iterable[Node]) -> Generator[tuple[Node, Node], None, None]:
|
2025-10-02 21:23:07 +00:00
|
|
|
"""Get all edges in the DAG by traversing from the given nodes
|
2025-10-02 21:10:05 +00:00
|
|
|
|
2025-10-02 21:23:07 +00:00
|
|
|
Arguments:
|
|
|
|
|
nodes: Iterable of nodes to start the traversal from
|
2025-10-02 21:10:05 +00:00
|
|
|
|
2025-10-02 21:23:07 +00:00
|
|
|
Yields:
|
|
|
|
|
Tuples of (source_node, target_node) representing edges in the DAG
|
2025-05-25 21:23:02 +00:00
|
|
|
"""
|
2025-10-02 21:23:07 +00:00
|
|
|
for node in nodes:
|
|
|
|
|
yield from get_all_dag_edges(net.source for net in node.args)
|
|
|
|
|
yield from ((net.source, node) for net in node.args)
|
2025-05-25 21:23:02 +00:00
|
|
|
|
|
|
|
|
|
2025-10-02 21:23:07 +00:00
|
|
|
def get_const_nets(nodes: list[Node]) -> list[Net]:
|
|
|
|
|
"""Get all nets with a constant nodes value
|
2025-10-01 21:09:49 +00:00
|
|
|
|
2025-09-20 21:25:07 +00:00
|
|
|
Returns:
|
2025-10-02 21:23:07 +00:00
|
|
|
List of nets whose source node is a Const
|
|
|
|
|
"""
|
|
|
|
|
net_lookup = {net.source: net for node in nodes for net in node.args}
|
|
|
|
|
return [net_lookup[node] for node in nodes if isinstance(node, Const)]
|
2025-05-25 21:23:02 +00:00
|
|
|
|
|
|
|
|
|
2025-08-29 20:58:10 +00:00
|
|
|
def add_read_ops(node_list: list[Node]) -> Generator[tuple[Net | None, Node], None, None]:
|
2025-10-02 21:23:07 +00:00
|
|
|
"""Add read node before each op where arguments are not already positioned
|
2025-05-25 21:23:02 +00:00
|
|
|
correctly in the registers
|
2025-10-01 21:09:49 +00:00
|
|
|
|
2025-10-02 21:23:07 +00:00
|
|
|
Arguments:
|
|
|
|
|
node_list: List of nodes in the order of execution
|
|
|
|
|
|
2025-05-25 21:23:02 +00:00
|
|
|
Returns:
|
2025-10-02 21:23:07 +00:00
|
|
|
Yields tuples of a net and a node. The net is the result net
|
|
|
|
|
for the node. If the node has no result net None is returned in the tuple.
|
|
|
|
|
"""
|
|
|
|
|
|
2025-09-11 20:22:17 +00:00
|
|
|
registers: list[None | Net] = [None] * 2
|
2025-05-25 21:23:02 +00:00
|
|
|
|
2025-09-24 21:24:25 +00:00
|
|
|
# Generate result net lookup table
|
2025-08-29 20:58:10 +00:00
|
|
|
net_lookup = {net.source: net for node in node_list for net in node.args}
|
2025-10-01 21:09:49 +00:00
|
|
|
|
2025-08-29 20:58:10 +00:00
|
|
|
for node in node_list:
|
|
|
|
|
if not node.name.startswith('const_'):
|
|
|
|
|
for i, net in enumerate(node.args):
|
2025-10-03 21:26:51 +00:00
|
|
|
if id(net) != id(registers[i]):
|
2025-05-25 21:23:02 +00:00
|
|
|
#if net in registers:
|
|
|
|
|
# print('x swap registers')
|
2025-09-11 20:22:17 +00:00
|
|
|
type_list = ['int' if r is None else r.dtype for r in registers]
|
2025-09-11 20:46:53 +00:00
|
|
|
new_node = Op(f"read_{net.dtype}_reg{i}_" + '_'.join(type_list), [])
|
2025-08-29 20:58:10 +00:00
|
|
|
yield net, new_node
|
2025-05-25 21:23:02 +00:00
|
|
|
registers[i] = net
|
2025-10-01 21:09:49 +00:00
|
|
|
|
2025-08-29 20:58:10 +00:00
|
|
|
if node in net_lookup:
|
2025-10-02 21:23:07 +00:00
|
|
|
yield net_lookup[node], node
|
2025-08-29 20:58:10 +00:00
|
|
|
registers[0] = net_lookup[node]
|
|
|
|
|
else:
|
|
|
|
|
yield None, node
|
|
|
|
|
|
2025-05-25 21:23:02 +00:00
|
|
|
|
2025-10-02 21:23:07 +00:00
|
|
|
def add_write_ops(net_node_list: list[tuple[Net | None, Node]], const_nets: list[Net]) -> Generator[tuple[Net | None, Node], None, None]:
|
|
|
|
|
"""Add write operation for each new defined net if a read operation is later followed
|
2025-10-03 21:09:25 +00:00
|
|
|
|
2025-10-02 21:23:07 +00:00
|
|
|
Returns:
|
|
|
|
|
Yields tuples of a net and a node. The associated net is provided for read and write nodes.
|
|
|
|
|
Otherwise None is returned in the tuple.
|
|
|
|
|
"""
|
2025-09-24 21:24:25 +00:00
|
|
|
|
|
|
|
|
# Initialize set of nets with constants
|
2025-10-02 21:23:07 +00:00
|
|
|
stored_nets = set(const_nets)
|
2025-09-24 21:24:25 +00:00
|
|
|
|
2025-10-02 21:23:07 +00:00
|
|
|
#assert all(node.name.startswith('read_') for net, node in net_node_list if net)
|
|
|
|
|
read_back_nets = {
|
|
|
|
|
net for net, node in net_node_list
|
|
|
|
|
if net and node.name.startswith('read_')}
|
2025-09-24 21:24:25 +00:00
|
|
|
|
2025-08-29 20:58:10 +00:00
|
|
|
for net, node in net_node_list:
|
2025-09-24 21:24:25 +00:00
|
|
|
if isinstance(node, Write):
|
|
|
|
|
yield node.args[0], node
|
2025-10-02 21:23:07 +00:00
|
|
|
elif node.name.startswith('read_'):
|
2025-09-24 21:24:25 +00:00
|
|
|
yield net, node
|
2025-10-02 21:23:07 +00:00
|
|
|
else:
|
|
|
|
|
yield None, node
|
2025-09-24 21:24:25 +00:00
|
|
|
|
2025-10-04 20:57:45 +00:00
|
|
|
if net and net in read_back_nets and net not in stored_nets:
|
2025-09-24 21:24:25 +00:00
|
|
|
yield net, Write(net)
|
2025-08-29 20:58:10 +00:00
|
|
|
stored_nets.add(net)
|
|
|
|
|
|
|
|
|
|
|
2025-09-24 21:24:25 +00:00
|
|
|
def get_nets(*inputs: Iterable[Iterable[Any]]) -> list[Net]:
|
2025-09-20 21:25:07 +00:00
|
|
|
nets: set[Net] = set()
|
|
|
|
|
|
2025-09-24 21:24:25 +00:00
|
|
|
for input in inputs:
|
|
|
|
|
for el in input:
|
|
|
|
|
for net in el:
|
|
|
|
|
if isinstance(net, Net):
|
|
|
|
|
nets.add(net)
|
2025-09-20 21:25:07 +00:00
|
|
|
|
|
|
|
|
return list(nets)
|
2025-08-29 20:58:10 +00:00
|
|
|
|
2025-09-20 21:25:07 +00:00
|
|
|
|
2025-10-03 21:26:51 +00:00
|
|
|
def compile_to_instruction_list(node_list: Iterable[Node], sdb: stencil_database) -> tuple[binw.data_writer, dict[Net, tuple[int, int, str]]]:
|
|
|
|
|
variables: dict[Net, tuple[int, int, str]] = dict()
|
2025-10-04 21:07:37 +00:00
|
|
|
|
2025-10-02 21:10:05 +00:00
|
|
|
ordered_ops = list(stable_toposort(get_all_dag_edges(node_list)))
|
2025-10-02 21:23:07 +00:00
|
|
|
const_net_list = get_const_nets(ordered_ops)
|
2025-08-29 20:58:10 +00:00
|
|
|
output_ops = list(add_read_ops(ordered_ops))
|
2025-10-02 21:23:07 +00:00
|
|
|
extended_output_ops = list(add_write_ops(output_ops, const_net_list))
|
2025-09-20 21:25:07 +00:00
|
|
|
|
2025-09-24 21:24:25 +00:00
|
|
|
# Get all nets associated with heap memory
|
2025-10-02 21:23:07 +00:00
|
|
|
variable_list = get_nets([[const_net_list]], extended_output_ops)
|
2025-09-20 21:25:07 +00:00
|
|
|
|
|
|
|
|
dw = binw.data_writer(sdb.byteorder)
|
|
|
|
|
|
|
|
|
|
def variable_mem_layout(variable_list: list[Net]) -> tuple[list[tuple[Net, int, int]], int]:
|
|
|
|
|
offset: int = 0
|
|
|
|
|
object_list: list[tuple[Net, int, int]] = []
|
2025-08-29 20:58:10 +00:00
|
|
|
|
2025-09-20 21:25:07 +00:00
|
|
|
for variable in variable_list:
|
|
|
|
|
lengths = sdb.var_size['dummy_' + variable.dtype]
|
|
|
|
|
object_list.append((variable, offset, lengths))
|
|
|
|
|
offset += (lengths + 3) // 4 * 4
|
2025-08-29 20:58:10 +00:00
|
|
|
|
2025-09-20 21:25:07 +00:00
|
|
|
return object_list, offset
|
2025-08-29 20:58:10 +00:00
|
|
|
|
2025-09-20 21:25:07 +00:00
|
|
|
object_list, data_section_lengths = variable_mem_layout(variable_list)
|
2025-08-29 20:58:10 +00:00
|
|
|
|
2025-10-01 21:09:49 +00:00
|
|
|
# Write data
|
2025-08-29 20:58:10 +00:00
|
|
|
dw.write_com(binw.Command.ALLOCATE_DATA)
|
|
|
|
|
dw.write_int(data_section_lengths)
|
|
|
|
|
|
2025-09-20 21:25:07 +00:00
|
|
|
for net, out_offs, lengths in object_list:
|
2025-10-03 21:26:51 +00:00
|
|
|
variables[net] = (out_offs, lengths, net.dtype)
|
2025-09-20 21:25:07 +00:00
|
|
|
if isinstance(net.source, Const):
|
2025-08-29 20:58:10 +00:00
|
|
|
dw.write_com(binw.Command.COPY_DATA)
|
|
|
|
|
dw.write_int(out_offs)
|
|
|
|
|
dw.write_int(lengths)
|
2025-09-20 21:25:07 +00:00
|
|
|
dw.write_value(net.source.value, lengths)
|
2025-10-02 21:23:07 +00:00
|
|
|
# print(f'+ {net.dtype} {net.source.value}')
|
2025-08-29 20:58:10 +00:00
|
|
|
|
2025-09-07 21:12:09 +00:00
|
|
|
# write auxiliary_functions
|
|
|
|
|
# TODO
|
|
|
|
|
|
2025-10-01 21:09:49 +00:00
|
|
|
# Prepare program code and relocations
|
2025-09-21 21:08:30 +00:00
|
|
|
object_addr_lookp = {net: out_offs for net, out_offs, _ in object_list}
|
|
|
|
|
data_list: list[bytes] = []
|
|
|
|
|
patch_list: list[tuple[int, int, int]] = []
|
|
|
|
|
offset = 0 # offset in generated code chunk
|
|
|
|
|
|
2025-10-02 21:23:07 +00:00
|
|
|
# print('object_addr_lookp: ', object_addr_lookp)
|
2025-09-21 21:08:30 +00:00
|
|
|
|
2025-09-30 21:11:14 +00:00
|
|
|
data = sdb.get_func_data('function_start')
|
|
|
|
|
data_list.append(data)
|
|
|
|
|
offset += len(data)
|
|
|
|
|
|
2025-10-02 21:23:07 +00:00
|
|
|
for associated_net, node in extended_output_ops:
|
2025-09-20 21:25:07 +00:00
|
|
|
assert node.name in sdb.function_definitions, f"- Warning: {node.name} prototype not found"
|
|
|
|
|
data = sdb.get_func_data(node.name)
|
2025-09-21 21:08:30 +00:00
|
|
|
data_list.append(data)
|
2025-10-02 21:23:07 +00:00
|
|
|
# print(f"* {node.name} ({offset}) " + ' '.join(f'{d:02X}' for d in data))
|
2025-10-01 21:09:49 +00:00
|
|
|
|
2025-09-21 21:08:30 +00:00
|
|
|
for patch in sdb.get_patch_positions(node.name):
|
2025-10-02 21:23:07 +00:00
|
|
|
assert associated_net, f"Relocation found but no net defined for operation {node.name}"
|
|
|
|
|
object_addr = object_addr_lookp[associated_net]
|
2025-09-30 21:11:14 +00:00
|
|
|
patch_value = object_addr + patch.addend - (offset + patch.addr)
|
2025-10-02 21:23:07 +00:00
|
|
|
# print('patch: ', patch, object_addr, patch_value)
|
2025-09-30 21:11:14 +00:00
|
|
|
patch_list.append((patch.type.value, offset + patch.addr, patch_value))
|
2025-09-21 21:08:30 +00:00
|
|
|
|
|
|
|
|
offset += len(data)
|
|
|
|
|
|
2025-09-30 21:11:14 +00:00
|
|
|
data = sdb.get_func_data('function_end')
|
|
|
|
|
data_list.append(data)
|
|
|
|
|
offset += len(data)
|
2025-10-02 21:23:07 +00:00
|
|
|
# print('function_end', offset, data)
|
2025-09-30 21:11:14 +00:00
|
|
|
|
2025-09-24 21:24:25 +00:00
|
|
|
# allocate program data
|
2025-09-21 21:08:30 +00:00
|
|
|
dw.write_com(binw.Command.ALLOCATE_CODE)
|
|
|
|
|
dw.write_int(offset)
|
2025-09-24 21:24:25 +00:00
|
|
|
|
|
|
|
|
# write program data
|
|
|
|
|
dw.write_com(binw.Command.COPY_CODE)
|
|
|
|
|
dw.write_int(0)
|
|
|
|
|
dw.write_int(offset)
|
2025-09-21 21:08:30 +00:00
|
|
|
dw.write_bytes(b''.join(data_list))
|
|
|
|
|
|
|
|
|
|
# write relocations
|
|
|
|
|
for patch_type, patch_addr, object_addr in patch_list:
|
|
|
|
|
dw.write_com(binw.Command.PATCH_OBJECT)
|
|
|
|
|
dw.write_int(patch_addr)
|
|
|
|
|
dw.write_int(patch_type)
|
2025-09-30 21:11:14 +00:00
|
|
|
dw.write_int(object_addr, signed=True)
|
2025-09-07 21:12:09 +00:00
|
|
|
|
2025-10-03 21:26:51 +00:00
|
|
|
return dw, variables
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Target():
|
2025-09-26 21:25:51 +00:00
|
|
|
|
2025-10-04 20:57:45 +00:00
|
|
|
def __init__(self, arch: str = 'native', optimization: str = 'O3') -> None:
|
2025-10-04 21:07:37 +00:00
|
|
|
self.sdb = stencil_db_from_package(arch, optimization)
|
2025-10-03 21:26:51 +00:00
|
|
|
self._variables: dict[Net, tuple[int, int, str]] = dict()
|
2025-10-01 21:09:49 +00:00
|
|
|
|
2025-10-04 21:07:37 +00:00
|
|
|
def compile(self, *variables: Net | list[Net]) -> None:
|
2025-10-03 21:26:51 +00:00
|
|
|
nodes: list[Node] = []
|
|
|
|
|
for s in variables:
|
|
|
|
|
if isinstance(s, Net):
|
|
|
|
|
nodes.append(Write(s))
|
|
|
|
|
else:
|
|
|
|
|
for net in s:
|
|
|
|
|
assert isinstance(net, Net)
|
|
|
|
|
nodes.append(Write(net))
|
|
|
|
|
|
|
|
|
|
dw, self._variables = compile_to_instruction_list(nodes, self.sdb)
|
|
|
|
|
dw.write_com(binw.Command.END_PROG)
|
|
|
|
|
assert coparun(dw.get_data()) > 0
|
|
|
|
|
|
|
|
|
|
def run(self) -> None:
|
|
|
|
|
# set entry point and run code
|
|
|
|
|
dw = binw.data_writer(self.sdb.byteorder)
|
2025-10-04 20:57:45 +00:00
|
|
|
dw.write_com(binw.Command.RUN_PROG)
|
2025-10-03 21:26:51 +00:00
|
|
|
dw.write_int(0)
|
2025-10-07 20:56:04 +00:00
|
|
|
#for s in self._variables:
|
|
|
|
|
# add_read_command(dw, self._variables, s)
|
2025-10-03 21:26:51 +00:00
|
|
|
dw.write_com(binw.Command.END_PROG)
|
|
|
|
|
assert coparun(dw.get_data()) > 0
|
|
|
|
|
|
|
|
|
|
def read_value(self, net: Net) -> float | int:
|
|
|
|
|
assert net in self._variables, f"Variable {net} not found"
|
|
|
|
|
addr, lengths, var_type = self._variables[net]
|
2025-10-05 21:09:43 +00:00
|
|
|
print('read_value', addr, lengths)
|
2025-10-07 21:24:46 +00:00
|
|
|
assert lengths > 0
|
2025-10-03 21:26:51 +00:00
|
|
|
data = read_data_mem(addr, lengths)
|
|
|
|
|
assert data is not None and len(data) == lengths, f"Failed to read variable {net}"
|
|
|
|
|
en = {'little': '<', 'big': '>'}[self.sdb.byteorder]
|
|
|
|
|
if var_type == 'float':
|
|
|
|
|
if lengths == 4:
|
|
|
|
|
value = struct.unpack(en + 'f', data)[0]
|
|
|
|
|
elif lengths == 8:
|
|
|
|
|
value = struct.unpack(en + 'd', data)[0]
|
|
|
|
|
else:
|
2025-10-04 21:18:48 +00:00
|
|
|
raise ValueError(f"Unsupported float length: {lengths} bytes")
|
|
|
|
|
assert isinstance(value, float)
|
|
|
|
|
return value
|
2025-10-03 21:26:51 +00:00
|
|
|
elif var_type == 'int':
|
|
|
|
|
if lengths in (1, 2, 4, 8):
|
|
|
|
|
value = int.from_bytes(data, byteorder=self.sdb.byteorder, signed=True)
|
|
|
|
|
assert isinstance(value, int)
|
|
|
|
|
return value
|
|
|
|
|
else:
|
2025-10-04 21:18:48 +00:00
|
|
|
raise ValueError(f"Unsupported int length: {lengths} bytes")
|
2025-10-03 21:26:51 +00:00
|
|
|
else:
|
|
|
|
|
raise ValueError(f"Unsupported variable type: {var_type}")
|
|
|
|
|
|
2025-10-04 20:57:45 +00:00
|
|
|
def read_variable_remote(self, net: Net) -> None:
|
|
|
|
|
dw = binw.data_writer(self.sdb.byteorder)
|
2025-10-05 21:09:43 +00:00
|
|
|
add_read_command(dw, self._variables, net)
|
2025-10-04 21:07:37 +00:00
|
|
|
assert coparun(dw.get_data()) > 0
|
2025-10-05 21:09:43 +00:00
|
|
|
|
2025-10-07 21:20:43 +00:00
|
|
|
|
2025-10-05 21:09:43 +00:00
|
|
|
def add_read_command(dw: binw.data_writer, variables: dict[Net, tuple[int, int, str]], net: Net) -> None:
|
|
|
|
|
assert net in variables, f"Variable {net} not found in data writer variables"
|
|
|
|
|
addr, lengths, _ = variables[net]
|
|
|
|
|
dw.write_com(binw.Command.READ_DATA)
|
|
|
|
|
dw.write_int(addr)
|
|
|
|
|
dw.write_int(lengths)
|