Skip to content

Commit

Permalink
Merge pull request #772 from qutech/issues/751_selene_fixes
Browse files Browse the repository at this point in the history
Add a simple implementation of the ELFManager that is used by default
  • Loading branch information
terrorfisch authored Aug 25, 2023
2 parents 86205e6 + 3a334c1 commit 0a2fa7e
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 11 deletions.
96 changes: 87 additions & 9 deletions qupulse/hardware/awgs/zihdawg.py
Original file line number Diff line number Diff line change
Expand Up @@ -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...
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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)

Expand All @@ -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."""
Expand Down Expand Up @@ -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"""
Expand Down Expand Up @@ -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"""
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
3 changes: 2 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ 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
Expand Down
2 changes: 1 addition & 1 deletion tests/hardware/zihdawg_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 0a2fa7e

Please sign in to comment.