mirror of https://github.com/Nonannet/copapy.git
read_value function added to read result variable values
This commit is contained in:
parent
6625da1a47
commit
e8bf9f1a26
|
|
@ -3,6 +3,8 @@ from typing import Generator, Iterable, Any
|
||||||
from . import binwrite as binw
|
from . import binwrite as binw
|
||||||
from .stencil_db import stencil_database
|
from .stencil_db import stencil_database
|
||||||
from collections import defaultdict, deque
|
from collections import defaultdict, deque
|
||||||
|
from coparun_module import coparun, read_data_mem
|
||||||
|
import struct
|
||||||
|
|
||||||
Operand = type['Net'] | float | int
|
Operand = type['Net'] | float | int
|
||||||
|
|
||||||
|
|
@ -60,10 +62,31 @@ class Net:
|
||||||
def __rtruediv__(self, other: Any) -> 'Net':
|
def __rtruediv__(self, other: Any) -> 'Net':
|
||||||
return _add_op('div', [other, self])
|
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:
|
def __repr__(self) -> str:
|
||||||
names = get_var_name(self)
|
names = get_var_name(self)
|
||||||
return f"{'name:' + names[0] if names else 'id:' + str(id(self))[-5:]}"
|
return f"{'name:' + names[0] if names else 'id:' + str(id(self))[-5:]}"
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return id(self)
|
||||||
|
|
||||||
|
|
||||||
class Const(Node):
|
class Const(Node):
|
||||||
def __init__(self, value: float | int | bool):
|
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:
|
for node in node_list:
|
||||||
if not node.name.startswith('const_'):
|
if not node.name.startswith('const_'):
|
||||||
for i, net in enumerate(node.args):
|
for i, net in enumerate(node.args):
|
||||||
if net != registers[i]:
|
if id(net) != id(registers[i]):
|
||||||
#if net in registers:
|
#if net in registers:
|
||||||
# print('x swap registers')
|
# print('x swap registers')
|
||||||
type_list = ['int' if r is None else r.dtype for r in 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)
|
return list(nets)
|
||||||
|
|
||||||
|
|
||||||
def compile_to_instruction_list(end_nodes: Iterable[Node] | Node) -> binw.data_writer:
|
def compile_to_instruction_list(node_list: Iterable[Node], sdb: stencil_database) -> tuple[binw.data_writer, dict[Net, tuple[int, int, str]]]:
|
||||||
if isinstance(end_nodes, Node):
|
variables: dict[Net, tuple[int, int, str]] = dict()
|
||||||
node_list = [end_nodes]
|
|
||||||
else:
|
|
||||||
node_list = list(end_nodes)
|
|
||||||
|
|
||||||
ordered_ops = list(stable_toposort(get_all_dag_edges(node_list)))
|
ordered_ops = list(stable_toposort(get_all_dag_edges(node_list)))
|
||||||
const_net_list = get_const_nets(ordered_ops)
|
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)
|
dw.write_int(data_section_lengths)
|
||||||
|
|
||||||
for net, out_offs, lengths in object_list:
|
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):
|
if isinstance(net.source, Const):
|
||||||
dw.write_com(binw.Command.COPY_DATA)
|
dw.write_com(binw.Command.COPY_DATA)
|
||||||
dw.write_int(out_offs)
|
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(patch_type)
|
||||||
dw.write_int(object_addr, signed=True)
|
dw.write_int(object_addr, signed=True)
|
||||||
|
|
||||||
# set entry point
|
return dw, variables
|
||||||
dw.write_com(binw.Command.SET_ENTR_POINT)
|
|
||||||
dw.write_int(0)
|
|
||||||
|
|
||||||
return dw
|
|
||||||
|
|
||||||
|
|
||||||
def read_variable(bw: binw.data_writer, net: Net) -> None:
|
class Target():
|
||||||
assert net in bw.variables, f"Variable {net} not found in data writer variables"
|
|
||||||
addr, lengths, _ = bw.variables[net]
|
def __init__(self, arch: str = 'x86_64', optimization: str = 'O3') -> None:
|
||||||
bw.write_com(binw.Command.READ_DATA)
|
self.sdb = stencil_database(f"src/copapy/obj/stencils_{arch}_{optimization}.o")
|
||||||
bw.write_int(addr)
|
self._variables: dict[Net, tuple[int, int, str]] = dict()
|
||||||
bw.write_int(lengths)
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
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]
|
||||||
|
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)
|
||||||
|
|
@ -14,10 +14,6 @@ class data_writer():
|
||||||
def __init__(self, byteorder: Literal['little', 'big']):
|
def __init__(self, byteorder: Literal['little', 'big']):
|
||||||
self._data: list[tuple[str, bytes, int]] = list()
|
self._data: list[tuple[str, bytes, int]] = list()
|
||||||
self.byteorder: Literal['little', 'big'] = byteorder
|
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:
|
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))
|
self._data.append((f"INT {value}", value.to_bytes(length=num_bytes, byteorder=self.byteorder, signed=signed), 0))
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
from typing import Generator
|
from typing import Generator
|
||||||
|
|
||||||
|
|
||||||
op_signs = {'add': '+', 'sub': '-', 'mul': '*', 'div': '/'}
|
op_signs = {'add': '+', 'sub': '-', 'mul': '*', 'div': '/',
|
||||||
|
'gt': '>', 'eq': '==', 'mod': '%'}
|
||||||
|
|
||||||
|
|
||||||
def get_function_start() -> str:
|
def get_function_start() -> str:
|
||||||
|
|
@ -89,7 +90,7 @@ def permutate(*lists: list[str]) -> Generator[list[str], None, None]:
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
types = ['int', 'float']
|
types = ['int', 'float']
|
||||||
ops = ['add', 'sub', 'mul', 'div']
|
ops = ['add', 'sub', 'mul', 'div', 'gt', 'eq']
|
||||||
|
|
||||||
code = """
|
code = """
|
||||||
// Auto-generated stencils for copapy
|
// Auto-generated stencils for copapy
|
||||||
|
|
@ -109,6 +110,8 @@ if __name__ == "__main__":
|
||||||
t_out = t1 if t1 == t2 else 'float'
|
t_out = t1 if t1 == t2 else 'float'
|
||||||
code += get_op_code(op, t1, t2, t_out)
|
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):
|
for t1, t2, t_out in permutate(types, types, types):
|
||||||
code += get_read_reg0_code(t1, t2, t_out)
|
code += get_read_reg0_code(t1, t2, t_out)
|
||||||
code += get_read_reg1_code(t1, t2, t_out)
|
code += get_read_reg1_code(t1, t2, t_out)
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,60 @@
|
||||||
asm volatile (".long 0xF27ECAFE");
|
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) {
|
void read_int_reg0_int_int(int arg1, int arg2) {
|
||||||
asm volatile (".long 0xF17ECAFE");
|
asm volatile (".long 0xF17ECAFE");
|
||||||
result_int_int(dummy_int, arg2);
|
result_int_int(dummy_int, arg2);
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,33 @@ static PyObject* coparun(PyObject* self, PyObject* args) {
|
||||||
return PyLong_FromLong(result);
|
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[] = {
|
static PyMethodDef MyMethods[] = {
|
||||||
{"coparun", coparun, METH_VARARGS, "Pass raw command data to coparun"},
|
{"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}
|
{NULL, NULL, 0, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,16 +55,16 @@ def test_compile():
|
||||||
r2 = i1 + 9
|
r2 = i1 + 9
|
||||||
out = [Write(r1), Write(r2)]
|
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)
|
# run program command
|
||||||
copapy.read_variable(il, r2)
|
il.write_com(binwrite.Command.SET_ENTR_POINT)
|
||||||
|
il.write_int(0)
|
||||||
|
|
||||||
il.write_com(binwrite.Command.READ_DATA)
|
il.write_com(binwrite.Command.READ_DATA)
|
||||||
il.write_int(0)
|
il.write_int(0)
|
||||||
il.write_int(36)
|
il.write_int(36)
|
||||||
|
|
||||||
# run program command
|
|
||||||
il.write_com(binwrite.Command.END_PROG)
|
il.write_com(binwrite.Command.END_PROG)
|
||||||
|
|
||||||
print('* Data to runner:')
|
print('* Data to runner:')
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from coparun_module import coparun
|
from coparun_module import coparun
|
||||||
from copapy import Write, const
|
from copapy import Write, const, Target
|
||||||
import copapy
|
import copapy
|
||||||
from copapy import binwrite
|
from copapy import binwrite
|
||||||
|
|
||||||
|
|
@ -7,32 +7,19 @@ from copapy import binwrite
|
||||||
def test_compile():
|
def test_compile():
|
||||||
|
|
||||||
c1 = const(4)
|
c1 = const(4)
|
||||||
c2 = const(2)
|
c2 = const(2) * 4
|
||||||
|
|
||||||
i1 = c1 * 2
|
i1 = c2 * 2
|
||||||
r1 = i1 + 7 + (c2 + 7 * 9)
|
r1 = i1 + 7 + (c1 + 7 * 9)
|
||||||
r2 = i1 + 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)
|
print(tg.read_value(r1))
|
||||||
copapy.read_variable(il, r2)
|
print(tg.read_value(r2))
|
||||||
|
print(tg.read_value(c2))
|
||||||
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__":
|
if __name__ == "__main__":
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
Loading…
Reference in New Issue