read_value function added to read result variable values

This commit is contained in:
Nicolas 2025-10-03 23:26:51 +02:00
parent 6625da1a47
commit e8bf9f1a26
8 changed files with 233 additions and 52 deletions

View File

@ -3,6 +3,8 @@ from typing import Generator, Iterable, Any
from . import binwrite as binw
from .stencil_db import stencil_database
from collections import defaultdict, deque
from coparun_module import coparun, read_data_mem
import struct
Operand = type['Net'] | float | int
@ -60,10 +62,31 @@ class Net:
def __rtruediv__(self, other: Any) -> 'Net':
return _add_op('div', [other, self])
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])
def __eq__(self, other: Any) -> 'Net':
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])
def __repr__(self) -> str:
names = get_var_name(self)
return f"{'name:' + names[0] if names else 'id:' + str(id(self))[-5:]}"
def __hash__(self) -> int:
return id(self)
class Const(Node):
def __init__(self, value: float | int | bool):
@ -239,7 +262,7 @@ def add_read_ops(node_list: list[Node]) -> Generator[tuple[Net | None, Node], No
for node in node_list:
if not node.name.startswith('const_'):
for i, net in enumerate(node.args):
if net != registers[i]:
if id(net) != id(registers[i]):
#if net in registers:
# print('x swap registers')
type_list = ['int' if r is None else r.dtype for r in registers]
@ -295,11 +318,8 @@ def get_nets(*inputs: Iterable[Iterable[Any]]) -> list[Net]:
return list(nets)
def compile_to_instruction_list(end_nodes: Iterable[Node] | Node) -> binw.data_writer:
if isinstance(end_nodes, Node):
node_list = [end_nodes]
else:
node_list = list(end_nodes)
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()
ordered_ops = list(stable_toposort(get_all_dag_edges(node_list)))
const_net_list = get_const_nets(ordered_ops)
@ -329,7 +349,7 @@ def compile_to_instruction_list(end_nodes: Iterable[Node] | Node) -> binw.data_w
dw.write_int(data_section_lengths)
for net, out_offs, lengths in object_list:
dw.add_variable(net, out_offs, lengths, net.dtype)
variables[net] = (out_offs, lengths, net.dtype)
if isinstance(net.source, Const):
dw.write_com(binw.Command.COPY_DATA)
dw.write_int(out_offs)
@ -389,16 +409,72 @@ def compile_to_instruction_list(end_nodes: Iterable[Node] | Node) -> binw.data_w
dw.write_int(patch_type)
dw.write_int(object_addr, signed=True)
# set entry point
return dw, variables
class Target():
def __init__(self, arch: str = 'x86_64', optimization: str = 'O3') -> None:
self.sdb = stencil_database(f"src/copapy/obj/stencils_{arch}_{optimization}.o")
self._variables: dict[Net, tuple[int, int, str]] = dict()
def compile(self, *variables: list[Net] | list[list[Net]]) -> None:
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)
dw.write_com(binw.Command.SET_ENTR_POINT)
dw.write_int(0)
return dw
dw.write_com(binw.Command.END_PROG)
assert coparun(dw.get_data()) > 0
def read_variable(bw: binw.data_writer, net: Net) -> None:
assert net in bw.variables, f"Variable {net} not found in data writer variables"
addr, lengths, _ = bw.variables[net]
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]
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]
assert isinstance(value, float)
return value
elif lengths == 8:
value = struct.unpack(en + 'd', data)[0]
assert isinstance(value, float)
return value
else:
raise ValueError(f"Unsupported float length: {lengths}")
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:
raise ValueError(f"Unsupported int length: {lengths}")
else:
raise ValueError(f"Unsupported variable type: {var_type}")
def read_variable_remote(self, bw: binw.data_writer, net: Net) -> None:
assert net in self._variables, f"Variable {net} not found in data writer variables"
dw = binw.data_writer(self.sdb.byteorder)
addr, lengths, _ = self._variables[net]
bw.write_com(binw.Command.READ_DATA)
bw.write_int(addr)
bw.write_int(lengths)

View File

@ -14,10 +14,6 @@ class data_writer():
def __init__(self, byteorder: Literal['little', 'big']):
self._data: list[tuple[str, bytes, int]] = list()
self.byteorder: Literal['little', 'big'] = byteorder
self.variables: dict[Any, tuple[int, int, str]] = dict()
def add_variable(self, net: Any, addr: int, lengths: int, var_type: str) -> None:
self.variables[net] = (addr, lengths, var_type)
def write_int(self, value: int, num_bytes: int = 4, signed: bool = False) -> None:
self._data.append((f"INT {value}", value.to_bytes(length=num_bytes, byteorder=self.byteorder, signed=signed), 0))

View File

@ -1,7 +1,8 @@
from typing import Generator
op_signs = {'add': '+', 'sub': '-', 'mul': '*', 'div': '/'}
op_signs = {'add': '+', 'sub': '-', 'mul': '*', 'div': '/',
'gt': '>', 'eq': '==', 'mod': '%'}
def get_function_start() -> str:
@ -89,7 +90,7 @@ def permutate(*lists: list[str]) -> Generator[list[str], None, None]:
if __name__ == "__main__":
types = ['int', 'float']
ops = ['add', 'sub', 'mul', 'div']
ops = ['add', 'sub', 'mul', 'div', 'gt', 'eq']
code = """
// Auto-generated stencils for copapy
@ -109,6 +110,8 @@ if __name__ == "__main__":
t_out = t1 if t1 == t2 else 'float'
code += get_op_code(op, t1, t2, t_out)
code += get_op_code('mod', 'int', 'int', 'int')
for t1, t2, t_out in permutate(types, types, types):
code += get_read_reg0_code(t1, t2, t_out)
code += get_read_reg1_code(t1, t2, t_out)

View File

@ -113,6 +113,60 @@
asm volatile (".long 0xF27ECAFE");
}
void gt_int_int(int arg1, int arg2) {
asm volatile (".long 0xF17ECAFE");
result_int_int(arg1 > arg2, arg2);
asm volatile (".long 0xF27ECAFE");
}
void gt_int_float(int arg1, float arg2) {
asm volatile (".long 0xF17ECAFE");
result_float_float(arg1 > arg2, arg2);
asm volatile (".long 0xF27ECAFE");
}
void gt_float_int(float arg1, int arg2) {
asm volatile (".long 0xF17ECAFE");
result_float_int(arg1 > arg2, arg2);
asm volatile (".long 0xF27ECAFE");
}
void gt_float_float(float arg1, float arg2) {
asm volatile (".long 0xF17ECAFE");
result_float_float(arg1 > arg2, arg2);
asm volatile (".long 0xF27ECAFE");
}
void eq_int_int(int arg1, int arg2) {
asm volatile (".long 0xF17ECAFE");
result_int_int(arg1 == arg2, arg2);
asm volatile (".long 0xF27ECAFE");
}
void eq_int_float(int arg1, float arg2) {
asm volatile (".long 0xF17ECAFE");
result_float_float(arg1 == arg2, arg2);
asm volatile (".long 0xF27ECAFE");
}
void eq_float_int(float arg1, int arg2) {
asm volatile (".long 0xF17ECAFE");
result_float_int(arg1 == arg2, arg2);
asm volatile (".long 0xF27ECAFE");
}
void eq_float_float(float arg1, float arg2) {
asm volatile (".long 0xF17ECAFE");
result_float_float(arg1 == arg2, arg2);
asm volatile (".long 0xF27ECAFE");
}
void mod_int_int(int arg1, int arg2) {
asm volatile (".long 0xF17ECAFE");
result_int_int(arg1 % arg2, arg2);
asm volatile (".long 0xF27ECAFE");
}
void read_int_reg0_int_int(int arg1, int arg2) {
asm volatile (".long 0xF17ECAFE");
result_int_int(dummy_int, arg2);

View File

@ -28,8 +28,33 @@ static PyObject* coparun(PyObject* self, PyObject* args) {
return PyLong_FromLong(result);
}
static PyObject* read_data_mem(PyObject* self, PyObject* args) {
unsigned long rel_addr;
Py_ssize_t length;
// Parse arguments: unsigned long (relative address), Py_ssize_t (length)
if (!PyArg_ParseTuple(args, "nk", &rel_addr, &length)) {
return NULL;
}
if (length <= 0) {
PyErr_SetString(PyExc_ValueError, "Length must be positive");
return NULL;
}
uint8_t *ptr = data_memory + rel_addr;
PyObject *result = PyBytes_FromStringAndSize((const char *)ptr, length);
if (!result) {
return PyErr_NoMemory();
}
return result;
}
static PyMethodDef MyMethods[] = {
{"coparun", coparun, METH_VARARGS, "Pass raw command data to coparun"},
{"read_data_mem", read_data_mem, METH_VARARGS, "Read memory and return as bytes"},
{NULL, NULL, 0, NULL}
};

View File

@ -55,16 +55,16 @@ def test_compile():
r2 = i1 + 9
out = [Write(r1), Write(r2)]
il = copapy.compile_to_instruction_list(out)
il, _ = copapy.compile_to_instruction_list(out, copapy.sdb)
copapy.read_variable(il, r1)
copapy.read_variable(il, r2)
# run program command
il.write_com(binwrite.Command.SET_ENTR_POINT)
il.write_int(0)
il.write_com(binwrite.Command.READ_DATA)
il.write_int(0)
il.write_int(36)
# run program command
il.write_com(binwrite.Command.END_PROG)
print('* Data to runner:')

View File

@ -1,5 +1,5 @@
from coparun_module import coparun
from copapy import Write, const
from copapy import Write, const, Target
import copapy
from copapy import binwrite
@ -7,32 +7,19 @@ from copapy import binwrite
def test_compile():
c1 = const(4)
c2 = const(2)
c2 = const(2) * 4
i1 = c1 * 2
r1 = i1 + 7 + (c2 + 7 * 9)
i1 = c2 * 2
r1 = i1 + 7 + (c1 + 7 * 9)
r2 = i1 + 9
out = [Write(r1), Write(r2)]
il = copapy.compile_to_instruction_list(out)
tg = Target()
tg.compile(r1, r2, c2)
tg.run()
copapy.read_variable(il, r1)
copapy.read_variable(il, r2)
il.write_com(binwrite.Command.READ_DATA)
il.write_int(0)
il.write_int(36)
# run program command
il.write_com(binwrite.Command.END_PROG)
#print('* Data to runner:')
#il.print()
print('+ run coparun')
result = coparun(il.get_data())
assert result == 1
print(tg.read_value(r1))
print(tg.read_value(r2))
print(tg.read_value(c2))
if __name__ == "__main__":

View File

@ -0,0 +1,40 @@
from coparun_module import coparun
from copapy import Write, const
import copapy
from copapy import binwrite
def test_compile():
c1 = const(4)
c2 = const(2) * 4
i1 = c2 * 2
r1 = i1 + 7 + (c1 + 7 * 9)
r2 = i1 + 9
out = [Write(r1), Write(r2), Write(c2)]
il, _ = copapy.compile_to_instruction_list(out, copapy.sdb)
# run program command
il.write_com(binwrite.Command.SET_ENTR_POINT)
il.write_int(0)
il.write_com(binwrite.Command.READ_DATA)
il.write_int(0)
il.write_int(36)
# run program command
il.write_com(binwrite.Command.END_PROG)
#print('* Data to runner:')
#il.print()
print('+ run coparun')
result = coparun(il.get_data())
assert result == 1
if __name__ == "__main__":
test_compile()