diff --git a/examples/pyxsim/example_pyxsim.py b/examples/pyxsim/example_pyxsim.py index cfd6e21..0960661 100644 --- a/examples/pyxsim/example_pyxsim.py +++ b/examples/pyxsim/example_pyxsim.py @@ -1,5 +1,6 @@ # Copyright 2025-2026 XMOS LIMITED. # This Software is subject to the terms of the XMOS Public Licence: Version 1. +import os from pathlib import Path import Pyxsim @@ -20,7 +21,11 @@ def run(self) -> None: def main() -> None: - xe = Path(__file__).parent / "bin" / "example_pyxsim.xe" + XE_OVERRIDE = os.environ.get("XE_FILE_OVERRIDE") + if XE_OVERRIDE: + xe = Path(XE_OVERRIDE) + else: + xe = Path(__file__).parent / "bin" / "example_pyxsim.xe" assert xe.exists() Pyxsim.run_with_pyxsim( diff --git a/lib/python/Pyxsim/__init__.py b/lib/python/Pyxsim/__init__.py index 756ad1f..e5071fb 100644 --- a/lib/python/Pyxsim/__init__.py +++ b/lib/python/Pyxsim/__init__.py @@ -17,6 +17,7 @@ from . import pyxsim from Pyxsim.xe import Xe from Pyxsim.pyxsim import Xsi +from Pyxsim.pyxsim import XsiRemote # This function is called automatically by the runners @@ -195,7 +196,12 @@ def run_on_simulator(*args, **kwargs): def do_run_pyxsim(xe, simargs, appargs, simthreads, plugins=None): - xsi = pyxsim.Xsi(xe_path=xe, simargs=simargs, appargs=appargs) + # Get XSI_ENDPOINT environment variable if set, otherwise use local + xsi_endpoint = os.environ.get("XSI_ENDPOINT") + if xsi_endpoint: + xsi = pyxsim.XsiRemote(xsi_endpoint, xe_path=xe, simargs=simargs, appargs=appargs) + else: + xsi = pyxsim.Xsi(xe_path=xe, simargs=simargs, appargs=appargs) for x in simthreads: xsi.register_simthread(x) if plugins: @@ -257,6 +263,9 @@ def run_with_pyxsim( simargs += ["--vcd-tracing", vcd_args] + if os.environ.get("XSI_ENDPOINT"): + timeout = None # Don't timeout when running on remote server + p = ctx.Process( target=do_run_pyxsim, args=(xe_path, simargs, appargs, simthreads, plugins) ) diff --git a/lib/python/Pyxsim/pyxsim.py b/lib/python/Pyxsim/pyxsim.py index b95d867..52d8889 100644 --- a/lib/python/Pyxsim/pyxsim.py +++ b/lib/python/Pyxsim/pyxsim.py @@ -243,52 +243,44 @@ def run(self): self.resume_condition = lambda x: False self.complete_event.set() - -class Xsi: +class XsiBase: @staticmethod def get_xsi_tick_freq_hz(): """ Returns the tick frequency corresponding to the time resolution used in xsim """ - xsi_tick_freq_hz = float(1e15) # Time resolution used within xsim (1 femtosecond) - return xsi_tick_freq_hz + return float(1e15) # Time resolution used within xsim (1 femtosecond) @staticmethod def get_xsi_tick_freq_khz(): """ Returns the tick frequency used in xsim in KHz """ - return Xsi.get_xsi_tick_freq_hz()/1000.0 + return XsiBase.get_xsi_tick_freq_hz() / 1000.0 @staticmethod def get_xsi_tick_freq_mhz(): """ Returns the tick frequency used in xsim in MHz """ - return Xsi.get_xsi_tick_freq_hz()/1000000.0 + return XsiBase.get_xsi_tick_freq_hz() / 1000000.0 def __init__(self, xe_path=None, simargs=[], appargs=[]): - self.xsim = c_void_p() self.xe_path = xe_path - args = " ".join(['"{}"'.format(x) for x in simargs + [self.xe_path] + appargs]) - if platform_is_windows(): - args = args.replace("\\", "/") - c_args = c_char_p(args.encode("utf-8")) - xsi_lib.xsi_create(byref(self.xsim), c_args) self._plugins = [] self._simthreads = [] self._time = 0 self.xe = Xe(self.xe_path) - self._time_step = Xsi.get_xsi_tick_freq_mhz() / self.xe.freq # time-step in fs + self._time_step = self.get_xsi_tick_freq_mhz() / self.xe.freq def register_plugin(self, plugin): self._plugins.append(plugin) def register_simthread(self, fn): if isinstance(fn, tuple): - xs = list(fn) - fn = xs[0] - args = xs[1:] + simthread_args = list(fn) + fn = simthread_args[0] + args = simthread_args[1:] else: args = [] @@ -304,9 +296,8 @@ def wait_for_simthread(simthread): raise TestError("Simthread encoutered an exception") def clock(self): - status = xsi_lib.xsi_clock(self.xsim) - # time + (time_resolution/xe_freq_hz) = (time*xe_freq_hz + time_resolution)/xe_freq_hz - self._time = ((self._time * self.xe.freq) + (Xsi.get_xsi_tick_freq_mhz())) / self.xe.freq + status = self._clock() + self._time += self._time_step if XsiStatus.is_valid(status): for plugin in self._plugins: plugin.clock(self) @@ -329,14 +320,13 @@ def get_time_ns(self): """ Returns current time in nanoseconds """ - return (self._time * 1e9) / (Xsi.get_xsi_tick_freq_hz()) + return (self._time * 1e9) / self.get_xsi_tick_freq_hz() def get_time_us(self): """ Returns current time in microseconds """ - return (self._time * 1e6) / (Xsi.get_xsi_tick_freq_hz()) - + return (self._time * 1e6) / self.get_xsi_tick_freq_hz() def run(self): status = XsiStatus.OK @@ -347,6 +337,80 @@ def run(self): while status != XsiStatus.DONE: status = self.clock() XsiStatus.error_if_not_valid(status) + + def read_symbol_word(self, tile, symbol, offset=0): + address = self.xe.symtab[tile, symbol] + offset + buf = self.read_mem(tile, address, 4, return_ctype=True) + return struct.unpack("=0.4.6", + "grpcio>=1.82.1", + "protobuf>=7.35.1", ], )