From 4a8f758a4597b7889e4c0a7cc024c25af7ab9b7a Mon Sep 17 00:00:00 2001 From: Nicolas Date: Mon, 24 Nov 2025 16:22:46 +0100 Subject: [PATCH] tests updated and extended for armv7 --- tests/test_comp_timing.py | 9 +- tests/test_ops_armv7.py | 169 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+), 5 deletions(-) create mode 100644 tests/test_ops_armv7.py diff --git a/tests/test_comp_timing.py b/tests/test_comp_timing.py index d13dd3b..9dc52b0 100644 --- a/tests/test_comp_timing.py +++ b/tests/test_comp_timing.py @@ -6,7 +6,7 @@ import copapy.backend as cpbe import copapy as cp import copapy._binwrite as binw from copapy._compiler import get_nets, get_section_layout, get_data_layout -from copapy._compiler import patch_entry, CPConstant, get_aux_function_mem_layout +from copapy._compiler import patch_entry, CPConstant, get_aux_func_layout def test_timing_compiler(): t1 = cp.vector([10, 11]*128) + cp.vector(cp.variable(v) for v in range(256)) @@ -88,7 +88,7 @@ def test_timing_compiler(): print('-- get_section_layout:') t0 = time.time() section_mem_layout, sections_length = get_section_layout(used_sections, sdb) - variable_mem_layout, variables_data_lengths = get_data_layout(variable_list, sdb, sections_length) + variable_mem_layout, _ = get_data_layout(variable_list, sdb, sections_length) t1 = time.time() print(f"time: {t1-t0:.6f}s") @@ -123,8 +123,7 @@ def test_timing_compiler(): # prep auxiliary_functions - aux_function_mem_layout, aux_function_lengths = get_aux_function_mem_layout(aux_function_names, sdb) - aux_func_addr_lookup = {name: offs for name, offs, _ in aux_function_mem_layout} + _, aux_func_addr_lookup, aux_function_lengths = get_aux_func_layout(aux_function_names, sdb) # Prepare program code and relocations object_addr_lookup = {net: offs for net, offs, _ in variable_mem_layout} @@ -179,7 +178,7 @@ def test_timing_compiler(): print('-- relocate aux functions:') t0 = time.time() # Patch aux functions - for name, start, _ in aux_function_mem_layout: + for name, start in aux_func_addr_lookup.items(): for reloc in sdb.get_relocations(name): #assert reloc.target_symbol_info != 'STT_FUNC', "Not tested yet!" diff --git a/tests/test_ops_armv7.py b/tests/test_ops_armv7.py new file mode 100644 index 0000000..0fd1b2b --- /dev/null +++ b/tests/test_ops_armv7.py @@ -0,0 +1,169 @@ +from copapy import NumLike, iif, variable +from copapy.backend import Write, compile_to_dag, add_read_command +import subprocess +from copapy import _binwrite +import copapy.backend as backend +import os +import warnings +import re +import struct +import pytest +import copapy as cp + +if os.name == 'nt': + # On Windows wsl and qemu-user is required: + # sudo apt install qemu-user + qemu_command = ['wsl', 'qemu-arm'] +else: + qemu_command = ['qemu-arm'] + +def parse_results(log_text: str) -> dict[int, bytes]: + regex = r"^READ_DATA offs=(\d*) size=(\d*) data=(.*)$" + matches = re.finditer(regex, log_text, re.MULTILINE) + var_dict: dict[int, bytes] = {} + + for match in matches: + value_str: list[str] = match.group(3).strip().split(' ') + #print('--', value_str) + value = bytes(int(v, base=16) for v in value_str) + if len(value) <= 8: + var_dict[int(match.group(1))] = value + + return var_dict + +def run_command(command: list[str]) -> str: + result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf8', check=False) + assert result.returncode != 11, f"SIGSEGV (segmentation fault)\n -Error occurred: {result.stderr}\n -Output: {result.stdout}" + assert result.returncode == 0, f"\n -Error occurred: {result.stderr}\n -Output: {result.stdout}" + return result.stdout + + +def check_for_qemu() -> bool: + command = qemu_command + ['--version'] + try: + result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf8', check=False) + except: + return False + return result.returncode == 0 + + +def function1(c1: NumLike) -> list[NumLike]: + return [c1 / 4, c1 / -4, c1 // 4, c1 // -4, (c1 * -1) // 4, + c1 * 4, c1 * -4, + c1 + 4, c1 - 4, + c1 > 2, c1 > 100, c1 < 4, c1 < 100] + + +def function2(c1: NumLike) -> list[NumLike]: + return [c1 * 4.44, c1 * -4.44] + + +def function3(c1: NumLike) -> list[NumLike]: + return [c1 / 4] + + +def function4(c1: NumLike) -> list[NumLike]: + return [c1 == 9, c1 == 4, c1 != 9, c1 != 4] + + +def function5(c1: NumLike) -> list[NumLike]: + return [c1 == True, c1 == False, c1 != True, c1 != False, c1 / 2, c1 + 2] + + +def function6(c1: NumLike) -> list[NumLike]: + return [c1 == True] + + +def iiftests(c1: NumLike) -> list[NumLike]: + return [iif(c1 > 5, 8, 9), + iif(c1 < 5, 8.5, 9.5), + iif(1 > 5, 3.3, 8.8) + c1, + iif(1 < 5, c1 * 3.3, 8.8), + iif(c1 < 5, c1 * 3.3, 8.8)] + + +@pytest.mark.runner +def test_compile(): + c_i = variable(9) + c_f = variable(1.111) + c_b = variable(True) + + ret_test = function1(c_i) + function1(c_f) + function2(c_i) + function2(c_f) + function3(c_i) + function4(c_i) + function5(c_b) + [variable(9) % 2] + iiftests(c_i) + iiftests(c_f) + [cp.asin(c_i/10)] + ret_ref = function1(9) + function1(1.111) + function2(9) + function2(1.111) + function3(9) + function4(9) + function5(True) + [9 % 2] + iiftests(9) + iiftests(1.111) + [cp.asin(9/10)] + + #ret_test = (c_i * 100 // 5, c_f * 10 // 5) + #ret_ref = (9 * 100 // 5, 1.111 * 10 // 5) + + out = [Write(r) for r in ret_test] + + sdb = backend.stencil_db_from_package('armv7') + dw, variables = compile_to_dag(out, sdb) + + #dw.write_com(_binwrite.Command.READ_DATA) + #dw.write_int(0) + #dw.write_int(28) + + # run program command + dw.write_com(_binwrite.Command.RUN_PROG) + #dw.write_com(_binwrite.Command.DUMP_CODE) + + for net in ret_test: + assert isinstance(net, backend.Net) + add_read_command(dw, variables, net) + + #dw.write_com(_binwrite.Command.READ_DATA) + #dw.write_int(0) + #dw.write_int(28) + + dw.write_com(_binwrite.Command.END_COM) + + print('* Data to runner:') + dw.print() + + dw.to_file('build/runner/test-armv7.copapy') + + if not check_for_qemu(): + warnings.warn("qemu-armv7 not found, armv7 test skipped!", UserWarning) + return + if not os.path.isfile('build/runner/coparun-armv7'): + warnings.warn("armv7 runner not found, armv7 test skipped!", UserWarning) + return + + command = qemu_command + ['build/runner/coparun-armv7', 'build/runner/test-armv7.copapy'] + ['build/runner/test-armv7.copapy.bin'] + #try: + result = run_command(command) + #except FileNotFoundError: + # warnings.warn(f"Test skipped, executable not found.", UserWarning) + # return + + print('* Output from runner:\n--') + print(result) + print('--') + + assert 'Return value: 1' in result + + result_data = parse_results(result) + + for test, ref in zip(ret_test, ret_ref): + assert isinstance(test, variable) + address = variables[test][0] + data = result_data[address] + if test.dtype == 'int': + val = int.from_bytes(data, sdb.byteorder, signed=True) + elif test.dtype == 'bool': + val = bool.from_bytes(data, sdb.byteorder) + elif test.dtype == 'float': + en = {'little': '<', 'big': '>'}[sdb.byteorder] + val = struct.unpack(en + 'f', data)[0] + assert isinstance(val, float) + else: + raise Exception(f"Unknown type: {test.dtype}") + print('+', val, ref, test.dtype, f" addr={address}") + for t in (int, float, bool): + assert isinstance(val, t) == isinstance(ref, t), f"Result type does not match for {val} and {ref}" + assert val == pytest.approx(ref, 1e-5), f"Result does not match: {val} and reference: {ref}" # pyright: ignore[reportUnknownMemberType] + + +if __name__ == "__main__": + #test_example() + test_compile()