From d393532109d2b9a7f1c6900775c81821bb34400b Mon Sep 17 00:00:00 2001 From: Simon Humpohl Date: Fri, 23 Jun 2023 12:52:26 +0200 Subject: [PATCH 1/4] Add a simple implementation of the ELFManager that is used by default --- qupulse/hardware/awgs/zihdawg.py | 96 +++++++++++++++++++++++++++++--- 1 file changed, 87 insertions(+), 9 deletions(-) diff --git a/qupulse/hardware/awgs/zihdawg.py b/qupulse/hardware/awgs/zihdawg.py index 6d6972a9..9bcf1cf2 100644 --- a/qupulse/hardware/awgs/zihdawg.py +++ b/qupulse/hardware/awgs/zihdawg.py @@ -10,7 +10,7 @@ import hashlib import argparse import re -from abc import abstractmethod +from abc import abstractmethod, ABC try: # zhinst fires a DeprecationWarning from its own code in some versions... @@ -394,7 +394,7 @@ def _initialize_awg_module(self): self._awg_module.set('awgModule/device', self.master_device.serial) self._awg_module.set('awgModule/index', self.awg_group_index) self._awg_module.execute() - self._elf_manager = ELFManager(self._awg_module) + self._elf_manager = ELFManager.DEFAULT_CLS(self._awg_module) self._upload_generator = () @property @@ -501,6 +501,7 @@ def upload(self, name: str, def _start_compile_and_upload(self): self._uploaded_seqc_source = None self._upload_generator = self._elf_manager.compile_and_upload(self._required_seqc_source) + logger.debug(f"_start_compile_and_upload: %r", next(self._upload_generator, "Finished")) def _wait_for_compile_and_upload(self): for state in self._upload_generator: @@ -560,6 +561,8 @@ def arm(self, name: Optional[str]) -> None: if self._required_seqc_source != self._uploaded_seqc_source: self._wait_for_compile_and_upload() + assert self._required_seqc_source == self._uploaded_seqc_source, "_wait_for_compile_and_upload did not work " \ + "as expected." self.user_register(self._program_manager.Constants.TRIGGER_REGISTER, 0) @@ -581,10 +584,8 @@ def arm(self, name: Optional[str]) -> None: self.user_register(self._program_manager.Constants.PROG_SEL_REGISTER, self._program_manager.name_to_index(name) | int(self._program_manager.Constants.NO_RESET_MASK, 2)) - # this was a workaround for problems in the past and I totally forgot why it was here - # for ch_pair in self.master.channel_tuples: - # ch_pair._wait_for_compile_and_upload() - self.enable(True) + if name is not None: + self.enable(True) def run_current_program(self) -> None: """Run armed program.""" @@ -802,7 +803,9 @@ def offsets(self) -> Tuple[float, ...]: return tuple(map(self.master_device.offset, self._channels())) -class ELFManager: +class ELFManager(ABC): + DEFAULT_CLS = None + class AWGModule: def __init__(self, awg_module: zhinst_core.AwgModule): """Provide an easily mockable interface to the zhinst AwgModule object""" @@ -838,6 +841,14 @@ def compiler_source_file(self) -> str: def compiler_source_file(self, source_file: str): self._module.set('compiler/sourcefile', source_file) + @property + def compiler_source_string(self) -> str: + return self._module.getString('compiler/sourcestring') + + @compiler_source_string.setter + def compiler_source_string(self, source_string: str): + self._module.set('compiler/sourcestring', source_string) + @property def compiler_upload(self) -> bool: """auto upload after compiling""" @@ -912,6 +923,74 @@ def _source_hash(source_string: str) -> str: # use utf-16 because str is UTF16 on most relevant machines (Windows) return hashlib.sha512(bytes(source_string, 'utf-16')).hexdigest() + @abstractmethod + def compile_and_upload(self, source_string: str) -> Generator[str, str, None]: + """The function returns a generator that yields the current state of the progress. The generator is empty iff + the upload is complete. An exception is raised if there is an error. + + To abort send 'abort' to the generator. (not implemented :P) + + Example: + >>> my_source = 'playWave("my_wave");' + >>> for state in elf_manager.compile_and_upload(my_source): + ... print('Current state:', state) + ... time.sleep(1) + + Args: + source_string: Source code to compile + + Returns: + Generator object that needs to be consumed + """ + + +class SimpleELFManager(ELFManager): + def __init__(self, awg_module: zhinst.ziPython.AwgModule): + """This implementation does not attempt to do something clever like caching.""" + super().__init__(awg_module) + + def compile_and_upload(self, source_string: str) -> Generator[str, str, None]: + self.awg_module.compiler_upload = True + self.awg_module.compiler_source_string = source_string + + while True: + status, msg = self.awg_module.compiler_status + if status == - 1: + yield 'compiling' + elif status == 0: + break + elif status == 1: + raise HDAWGCompilationException(msg) + elif status == 2: + logger.warning("Compiler warings: %s", msg) + break + else: + raise RuntimeError("Unexpected status", status, msg) + + while True: + status_int, progress = self.awg_module.elf_status + if progress == 1.0: + break + elif status_int == 1: + HDAWGUploadException(self.awg_module.compiler_status) + else: + yield 'uploading @ %d%%' % (100*progress) + + +ELFManager.DEFAULT_CLS = SimpleELFManager + + +class CachingELFManager(ELFManager): + def __init__(self, awg_module: zhinst.ziPython.AwgModule): + """FAILS TO UPLOAD THE CORRECT ELF FOR SOME REASON""" + super().__init__(awg_module) + + # automatically upload after successful compilation + self.awg_module.compiler_upload = True + + self._compile_job = None # type: Optional[Union[str, Tuple[str, int, str]]] + self._upload_job = None # type: Optional[Union[Tuple[str, float], Tuple[str, int]]] + def _update_compile_job_status(self): """Store current compile status in self._compile_job.""" compiler_start = self.awg_module.compiler_start @@ -920,8 +999,7 @@ def _update_compile_job_status(self): elif isinstance(self._compile_job, str): if compiler_start: - # compilation is running - pass + logger.debug("Compiler is running.") else: compiler_status, status_string = self.awg_module.compiler_status From 4bd685512ed4628a9be3b57050729aefe7bd6eb6 Mon Sep 17 00:00:00 2001 From: Simon Humpohl Date: Fri, 23 Jun 2023 15:59:28 +0200 Subject: [PATCH 2/4] specify zhinst max version for python<3.9 --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 8c68a1ad..fbc526a2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -42,7 +42,7 @@ docs = plotting = matplotlib tabor-instruments = tabor_control>=0.1.1 -zurich-instruments = zhinst +zurich-instruments = zhinst<=20.7.2701;python_version<'3.9' Faster-fractions = gmpy2 tektronix = tek_awg>=0.2.1 autologging = autologging From e2d838ef37d2f630010091953a55224db7786b73 Mon Sep 17 00:00:00 2001 From: Simon Humpohl Date: Fri, 23 Jun 2023 16:32:50 +0200 Subject: [PATCH 3/4] Fix requirement marker --- setup.cfg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index fbc526a2..6ca9c3e0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -42,7 +42,8 @@ docs = plotting = matplotlib tabor-instruments = tabor_control>=0.1.1 -zurich-instruments = zhinst<=20.7.2701;python_version<'3.9' +zurich-instruments = + zhinst<=20.7.2701;python_version<'3.9' Faster-fractions = gmpy2 tektronix = tek_awg>=0.2.1 autologging = autologging From 0b311abaa99880d9e803127f10513452464b03f7 Mon Sep 17 00:00:00 2001 From: Simon Humpohl Date: Fri, 23 Jun 2023 16:38:49 +0200 Subject: [PATCH 4/4] Make Test use default class --- tests/hardware/zihdawg_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/hardware/zihdawg_tests.py b/tests/hardware/zihdawg_tests.py index 9fe675ea..cc86ad15 100644 --- a/tests/hardware/zihdawg_tests.py +++ b/tests/hardware/zihdawg_tests.py @@ -199,7 +199,7 @@ def test_upload(self): @mock.patch('qupulse.hardware.awgs.zihdawg.ELFManager.AWGModule.compiler_upload', new_callable=mock.PropertyMock) class ELFManagerTests(unittest.TestCase): def test_init(self, compiler_upload): - manager = ELFManager(None) + manager = ELFManager.DEFAULT_CLS(None) compiler_upload.assert_called_once_with(True) self.assertIsNone(manager._compile_job) self.assertIsNone(manager._upload_job)