From 6c1425f7356dc2d96ea0c90f9078e5608f02d59c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 16 May 2024 14:28:35 -0600 Subject: [PATCH] driver: Add driver for Dediprog SPI-flash emulator The EM100-pro is a SPI-flash emulator which supports a variety of chips. It can be used to provide an image to a booting system. Add a driver which allows images to be loaded ready for use. Signed-off-by: Simon Glass --- doc/configuration.rst | 37 +++++++++++ labgrid/driver/__init__.py | 1 + labgrid/driver/sfemulatordriver.py | 98 ++++++++++++++++++++++++++++++ labgrid/remote/exporter.py | 19 ++++++ labgrid/resource/__init__.py | 1 + labgrid/resource/sfemulator.py | 32 ++++++++++ 6 files changed, 188 insertions(+) create mode 100644 labgrid/driver/sfemulatordriver.py create mode 100644 labgrid/resource/sfemulator.py diff --git a/doc/configuration.rst b/doc/configuration.rst index f70bf2b66..af721083d 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -1168,6 +1168,34 @@ NetworkDediprogFlasher A :any:`NetworkDediprogFlasher` describes a `DediprogFlasher`_ available on a remote computer. +SFEmulator +~~~~~~~~~~ +A :any:`SFEmulator` resource is used to configure the parameters for a +SPI-flash emulator. This allows a DUT's SPI flash to be replaced or +overridden with a USB-connected emulator, which is much faster to load than +reprogramming the DUT's actual SPI flash. + +So far only the Dediprog EM100-PRO is supported, so there is no 'model' property +defined. + +Arguments: + - serial (str): Serial number of the device + - chip (str): SPI-flash chip to emulate + +.. code-block:: yaml + + SFEmulator: + serial: DP025143 + chip: W25Q64CV + +Used by: + - `SFEmulatorDriver`_ + +NetworkSFEmulator +~~~~~~~~~~~~~~~~~ +A :any:`NetworkSFEmulator` describes a `SFEmulator`_ available on a +remote computer. + XenaManager ~~~~~~~~~~~ A :any:`XenaManager` resource describes a *Xena Manager* instance which is the @@ -3052,6 +3080,15 @@ devices. It is assumed that the device flashing is an exporter wired, via a *Dediprog SF100 SPI NOR Flash Programmer* for instance, to the device being flashed. +SFEmulatorDriver +~~~~~~~~~~~~~~~~ +The :any:`SFEmulatorDriver` is used to emulate a SPI-flash device on the DUT. + +Binds to: + flasher: + - `SFEmulator`_ + - `NetworkSFEmulator`_ + XenaDriver ~~~~~~~~~~ The :any:`XenaDriver` allows to use Xena networking test equipment. diff --git a/labgrid/driver/__init__.py b/labgrid/driver/__init__.py index 721256bbf..5d9e361f4 100644 --- a/labgrid/driver/__init__.py +++ b/labgrid/driver/__init__.py @@ -48,3 +48,4 @@ from .deditecrelaisdriver import DeditecRelaisDriver from .dediprogflashdriver import DediprogFlashDriver from .httpdigitaloutput import HttpDigitalOutputDriver +from .sfemulatordriver import SFEmulatorDriver diff --git a/labgrid/driver/sfemulatordriver.py b/labgrid/driver/sfemulatordriver.py new file mode 100644 index 000000000..8809c1949 --- /dev/null +++ b/labgrid/driver/sfemulatordriver.py @@ -0,0 +1,98 @@ +import subprocess +from threading import Thread +import time + +import attr +from .common import Driver +from ..factory import target_factory +from ..step import step +from ..util.helper import processwrapper, ProcessRunner +from ..util.managedfile import ManagedFile + +@target_factory.reg_driver +@attr.s(eq=False) +class SFEmulatorDriver(Driver): + """Provides access to em100 features + + Args: + bindings (dict): driver to use with + _proc (subprocess.Popen): Process running em100 (used only in trace + mode) + _trace (str): Filename of trace file, if enabled + _thread (Thread): Thread which monitors the subprocess for errors + """ + bindings = { + 'emul': {'SFEmulator', 'NetworkSFEmulator'}, + } + trace = attr.ib(default=False, validator=attr.validators.instance_of(bool)) + + def __attrs_post_init__(self): + super().__attrs_post_init__() + if self.target.env: + self.tool = self.target.env.config.get_tool('em100') + else: + self.tool = 'em100' + self._trace = None + self._thread = None + + @Driver.check_active + @step(title='write_image', args=['filename']) + def write_image(self, filename): + '''Write an image to the SPI-flash emulator + + Args: + filename (str): Filename to write + ''' + self._trace = None # Provide this filename later + + mf = ManagedFile(filename, self.emul) + mf.sync_to_resource() + cmd = self.emul.command_prefix + [ + self.tool, + '-x', str(self.emul.serial), + '-s', + '-p', 'LOW', + '-c', self.emul.chip, + '-d', mf.get_remote_path(), + '-r', + ] + + # For trace mode, start a process which will run for the duration of + # the Labgrid client, all going well + if self._trace: + cmd.append('-t') + logfile = open(self._trace, 'w') + self._thread = Thread(target=self.monitor_thread, + args=(cmd, logfile)) + self._thread.daemon = True + self._thread.start() + else: + processwrapper.check_output(cmd) + + def __str__(self): + return f'SFEmulatorDriver({self.emul.serial})' + + def monitor_thread(self, cmd, logfile): + """Thread to monitor the em100 process + + This is mostly just here to check if it dies, so a useful error can be + shown. + + Args: + cmd (list of str): Command to run + logfile (file): Output file for trace log + """ + proc = ProcessRunner(cmd, stdin=subprocess.DEVNULL, stdout=logfile) + while True: + if proc.check(): + break + time.sleep(.1) + try: + proc.kill() + proc.finish() + except subprocess.CalledProcessError as exc: + # If there is something wrong, the error will be in the logfile, so + # print it out + print('em100 failed', exc.returncode) + with open(self._trace, 'r') as inf: + print('err', inf.read(1024)) diff --git a/labgrid/remote/exporter.py b/labgrid/remote/exporter.py index 86a261c92..dcb2219f4 100755 --- a/labgrid/remote/exporter.py +++ b/labgrid/remote/exporter.py @@ -771,6 +771,25 @@ def _get_params(self): exports["YKUSHPowerPort"] = YKUSHPowerPortExport +@attr.s(eq=False) +class SFEmulatorExport(ResourceExport): + """ResourceExport for SFEmulator devices""" + + def __attrs_post_init__(self): + super().__attrs_post_init__() + local_cls_name = self.cls + self.data['cls'] = f"Network{local_cls_name}" + from ..resource import sfemulator + local_cls = getattr(sfemulator, local_cls_name) + self.local = local_cls(target=None, name=None, **self.local_params) + + def _get_params(self): + return { + "host": self.host, + **self.local_params + } + +exports["SFEmulator"] = SFEmulatorExport class Exporter: def __init__(self, config) -> None: diff --git a/labgrid/resource/__init__.py b/labgrid/resource/__init__.py index dd7554dff..f59afd5f7 100644 --- a/labgrid/resource/__init__.py +++ b/labgrid/resource/__init__.py @@ -47,3 +47,4 @@ from .httpdigitalout import HttpDigitalOutput from .sigrok import SigrokDevice from .fastboot import AndroidNetFastboot +from .sfemulator import SFEmulator, NetworkSFEmulator diff --git a/labgrid/resource/sfemulator.py b/labgrid/resource/sfemulator.py new file mode 100644 index 000000000..284c8a86e --- /dev/null +++ b/labgrid/resource/sfemulator.py @@ -0,0 +1,32 @@ +import attr + +from ..factory import target_factory +from .common import NetworkResource, Resource + +@target_factory.reg_resource +@attr.s(eq=False) +class SFEmulator(Resource): + """"This resource describes a Dediprog em100 SPI-Flash Emulator + + This provides serial consoles along with reset control + + Args: + serial (str): serial number of the em100 device, e.g. DP025143 + chip (str): SPI-flash chip to emulate, e.g. W25Q64CV + """ + serial = attr.ib(validator=attr.validators.instance_of(str)) + chip = attr.ib(validator=attr.validators.instance_of(str)) + +@target_factory.reg_resource +@attr.s(eq=False) +class NetworkSFEmulator(NetworkResource): + """"This resource describes a remote Dediprog em100 SPI-Flash Emulator + + This provides serial consoles along with reset control + + Args: + serial (str): serial number of the em100 device, e.g. DP025143 + chip (str): SPI-flash chip to emulate, e.g. W25Q64CV + """ + serial = attr.ib(validator=attr.validators.instance_of(str)) + chip = attr.ib(validator=attr.validators.instance_of(str))