Esempio n. 1
0
    def close(self) -> None:
        """Close the simulation.

        The signal manager will be closed.
        """
        _logger.debug('Closing signal manager')
        get_signal_manager().close()
Esempio n. 2
0
    def __init__(self,
                 dmgr,
                 clk_div=0,
                 rf_sw=0,
                 refclk=125e6,
                 att=0x00000000,
                 **kwargs):
        # Call super
        super(CPLD, self).__init__(dmgr, **kwargs)

        # Store attributes (from ARTIQ code)
        self.refclk = refclk
        assert 0 <= clk_div <= 3
        self.clk_div = clk_div
        self.att_reg = np.int32(np.int64(att))

        # Register signals
        self._signal_manager = get_signal_manager()
        self._init = self._signal_manager.register(self, 'init', bool, size=1)
        self._init_att = self._signal_manager.register(self,
                                                       'init_att',
                                                       bool,
                                                       size=1)
        self._att = [
            self._signal_manager.register(self, f'att_{i}', float)
            for i in range(4)
        ]
        self._sw = self._signal_manager.register(self, 'sw', bool, size=4)

        # Internal registers
        self._att_reg = [_mu_to_att(att >> (i * 8)) for i in range(4)]
        self._sw_reg = _state_to_sw_reg(rf_sw)
Esempio n. 3
0
    def test_gtk_wave_save_generator(self):
        with temp_dir():
            ddb = enable_dax_sim(ddb=_DEVICE_DB.copy(),
                                 enable=True,
                                 output='vcd',
                                 moninj_service=False)

            with get_managers(ddb) as managers:
                system = _TestSystem(managers)
                self.assertTrue(system.dax_sim_enabled)

                # Create GTKWave save generator object, which immediately writes the waves file
                GTKWSaveGenerator(system)

                # Manually close signal manager before leaving temp dir
                get_signal_manager().close()
Esempio n. 4
0
    def test_expect_bool_vector(self):
        test_data = [
            ('XX', 'XX'),
            ('xx', 'xx'),
            ('xZ', 'Xz'),
            ('ZX', 'zx'),
            ('zx', 'ZX'),
            ('Z0', 'z0'),
            ('10', '10'),
            ('01', '01'),
            ('11', '11'),
            ('00', '00'),
        ]

        # Get scope, signal name, and internal signal for manual adjustments
        scope = self.sys.ad9910
        signal_name = 'phase_mode'
        signal = self.sys.ad9910._phase_mode
        # Signal manager
        sm = get_signal_manager()

        # Test starting values
        self.expect(scope, signal_name, 'x')

        for val, ref in test_data:
            with self.subTest(value=val, reference=ref):
                # Set new value
                delay(1 * ns)
                sm.event(signal, val)
                # Test value
                self.expect(scope, signal_name, ref)
                delay(1 * us)
                self.expect(scope, signal_name, ref)
Esempio n. 5
0
    def test_signal_manager(self) -> None:
        # Create the system
        _TestSystem(self.managers)

        # Verify the signal manager type
        sm = typing.cast(NullSignalManager, get_signal_manager())
        self.assertIsInstance(sm, NullSignalManager)
Esempio n. 6
0
    def __init__(self,
                 dmgr,
                 chip_select,
                 cpld_device,
                 sw_device=None,
                 pll_n=10,
                 **kwargs):
        # Call super
        super(AD9912, self).__init__(dmgr, **kwargs)

        # Register signals
        self._signal_manager = get_signal_manager()
        self._init = self._signal_manager.register(self, 'init', bool, size=1)
        self._freq = self._signal_manager.register(self, 'freq', float)
        self._phase = self._signal_manager.register(self, 'phase', float)

        # CPLD device
        self.cpld = dmgr.get(cpld_device)
        # Chip select
        assert 4 <= chip_select <= 7
        self.chip_select = chip_select
        # Switch device
        if sw_device:
            self.sw = dmgr.get(sw_device)

        # Store attributes (from ARTIQ code)
        sysclk = self.cpld.refclk / [1, 1, 2, 4][self.cpld.clk_div] * pll_n
        assert sysclk <= 1e9
        self.ftw_per_hz = 1 / sysclk * (np.int64(1) << 48)
Esempio n. 7
0
    def test_signal_manager(self) -> None:
        # Verify the signal manager type by verifying if the same signal managers is checked as in setUp()
        self.assertIs(typing.cast(VcdSignalManager, get_signal_manager()),
                      self.sm)

        # Manually close signal manager before leaving temp dir
        self.sm.close()
        self.sm.close()  # Close twice, should not raise an exception
Esempio n. 8
0
    def __init__(self,
                 dmgr,
                 chip_select,
                 cpld_device,
                 sw_device=None,
                 pll_n=40,
                 pll_cp=7,
                 pll_vco=5,
                 pll_en=1,
                 **kwargs):
        # Call super
        super(AD9910, self).__init__(dmgr, **kwargs)

        # CPLD device
        self.cpld = dmgr.get(cpld_device)
        # Chip select
        assert 4 <= chip_select <= 7
        self.chip_select = chip_select
        # Switch device
        if sw_device:
            self.sw = dmgr.get(sw_device)

        # Store attributes (from ARTIQ code)
        clk = self.cpld.refclk / [4, 1, 2, 4][self.cpld.clk_div]
        self.pll_en = pll_en
        self.pll_n = pll_n
        self.pll_vco = pll_vco
        self.pll_cp = pll_cp
        if pll_en:
            sysclk = clk * pll_n
            assert clk <= 60e6
            assert 12 <= pll_n <= 127
            assert 0 <= pll_vco <= 5
            vco_min, vco_max = [(370, 510), (420, 590), (500, 700), (600, 880),
                                (700, 950), (820, 1150)][pll_vco]
            assert vco_min <= sysclk / 1e6 <= vco_max
            assert 0 <= pll_cp <= 7
        else:
            sysclk = clk
        assert sysclk <= 1e9
        self.ftw_per_hz = (1 << 32) / sysclk
        self.sysclk_per_mu = int(round(float(sysclk * self.core.ref_period)))
        self.sysclk = sysclk

        self.phase_mode = PHASE_MODE_CONTINUOUS

        # Register signals
        self._signal_manager = get_signal_manager()
        self._init = self._signal_manager.register(self, 'init', bool, size=1)
        self._freq = self._signal_manager.register(self, 'freq', float)
        self._phase = self._signal_manager.register(self, 'phase', float)
        self._phase_mode = self._signal_manager.register(self,
                                                         'phase_mode',
                                                         bool,
                                                         size=2)
        self._amp = self._signal_manager.register(self, 'amp', float)
Esempio n. 9
0
    def __init__(self, dmgr, acc_width=24, **kwargs):
        # Call super
        super(TTLClockGen, self).__init__(dmgr, **kwargs)

        # Store parameters
        self._acc_width = np.int64(acc_width)

        # Register signals
        self._signal_manager = get_signal_manager()
        self._freq = self._signal_manager.register(self, 'freq', float)
Esempio n. 10
0
    def test_gtk_wave_save_generator_invalid_signal_manager(self):
        with temp_dir():
            ddb = enable_dax_sim(ddb=_DEVICE_DB.copy(),
                                 enable=True,
                                 output='null',
                                 moninj_service=False)

            with get_managers(ddb) as managers:
                system = _TestSystem(managers)
                self.assertTrue(system.dax_sim_enabled)

                with self.assertRaises(
                        RuntimeError,
                        msg='Not using VCD signal manager did not raise'):
                    # Create GTKWave save generator object, which immediately writes the waves file
                    GTKWSaveGenerator(system)

                # Manually close signal manager before leaving temp dir
                get_signal_manager().close()
Esempio n. 11
0
    def __init__(self, dmgr: typing.Any, **kwargs: typing.Any):
        # Call super
        super(TTLOut, self).__init__(dmgr, **kwargs)

        # Register signals
        self._signal_manager = get_signal_manager()
        self._state = self._signal_manager.register(self,
                                                    'state',
                                                    bool,
                                                    size=1)
Esempio n. 12
0
    def setUp(self) -> None:
        # Create the system
        ddb = enable_dax_sim(_DEVICE_DB.copy(),
                             enable=True,
                             output='peek',
                             moninj_service=False)
        self.managers = get_managers(ddb)
        self.sys = _TestSystem(self.managers)

        # Get the peek signal manager
        self.sm = typing.cast(PeekSignalManager, get_signal_manager())
        self.assertIsInstance(self.sm, PeekSignalManager)
Esempio n. 13
0
    def __init__(self, dmgr: typing.Any, **kwargs: typing.Any):
        # Call super for DaxSimDevice
        DaxSimDevice.__init__(self, dmgr, **kwargs)

        # Register signal
        signal_manager = get_signal_manager()
        signal_call: typing.Any = signal_manager.register(self, 'call', object)
        signal_function: typing.Any = signal_manager.register(
            self, 'function', str)

        # Call super for _GenericBase
        _GenericBase.__init__(self, None, signal_manager, signal_call,
                              signal_function)
Esempio n. 14
0
    def __init__(self, core: typing.Any, name: str, record_signal: typing.Any):
        assert isinstance(name, str)

        # Store attributes
        self._core = core
        self._name = name

        # Signals
        self._signal_manager = get_signal_manager()
        self._record_signal = record_signal

        # Duration will be recorded using enter and exit
        self._duration = np.int64(0)
Esempio n. 15
0
    def __init__(self, dmgr: typing.Any, **kwargs: typing.Any):
        # Call super
        super(CoreDMA, self).__init__(dmgr, **kwargs)

        # Initialize epoch to zero
        self._epoch = 0
        # Dict for DMA traces
        self._dma_traces = {}

        # Register signal
        self._signal_manager = get_signal_manager()
        self._dma_record = self._signal_manager.register(self, 'record', str)
        self._dma_play = self._signal_manager.register(self, 'play', object)
        self._dma_play_name = self._signal_manager.register(
            self, 'play_name', str)
Esempio n. 16
0
    def setUp(self) -> None:
        self._temp_dir = temp_dir()
        self._temp_dir.__enter__()

        # Create the system
        ddb = enable_dax_sim(_DEVICE_DB.copy(),
                             enable=True,
                             output='vcd',
                             moninj_service=False)
        self.managers = get_managers(ddb)
        self.sys = _TestSystem(self.managers)

        # Get the signal manager
        self.sm: DaxSignalManager = typing.cast(VcdSignalManager,
                                                get_signal_manager())
        self.assertIsInstance(self.sm, VcdSignalManager)
Esempio n. 17
0
    def __init__(self,
                 dmgr: typing.Any,
                 gateware_width: int = 31,
                 input_freq: float = 0.0,
                 input_stdev: float = 0.0,
                 seed: typing.Optional[int] = None,
                 **kwargs: typing.Any):
        """Simulation driver for :class:`artiq.coredevice.edge_counter.EdgeCounter`.

        :param input_freq: Simulated input frequency for gate operations
        :param input_stdev: Simulated input frequency standard deviation for gate operations
        :param seed: Seed for the random number generator used for simulating input
        """
        assert isinstance(gateware_width,
                          int), 'Gateware width must be of type int'
        assert isinstance(
            input_freq, float
        ) and input_freq >= 0.0, 'Input frequency must be a positive float'
        assert isinstance(
            input_stdev, float
        ) and input_stdev >= 0.0, 'Input stdev must be a non-negative float'

        # Call super
        super(EdgeCounter, self).__init__(dmgr, **kwargs)

        # From ARTIQ code
        self.counter_max = (1 << (gateware_width - 1)) - 1

        # Store simulation settings
        self._input_freq = input_freq
        self._input_stdev = input_stdev
        self._rng = random.Random(seed)

        # Buffers to store counts
        self._count_buffer = collections.deque()
        # Single buffer to match set_config() calls
        self._prev_config = None

        # Register signals
        self._signal_manager = get_signal_manager()
        self._count = self._signal_manager.register(self,
                                                    'count',
                                                    int,
                                                    init='z')
Esempio n. 18
0
    def __init__(self, dmgr, vref=5., offset_dacs=8192, **kwargs):
        # Call super
        super(AD53xx, self).__init__(dmgr, **kwargs)

        # Register signals
        self._signal_manager = get_signal_manager()
        self._init = self._signal_manager.register(self, 'init', bool, size=1)
        self._dac = [self._signal_manager.register(self, f'v_out_{i}', float) for i in range(self._NUM_CHANNELS)]
        self._offset = [self._signal_manager.register(self, f'v_offset_{i}', float) for i in range(self._NUM_CHANNELS)]
        self._gain = [self._signal_manager.register(self, f'gain_{i}', float) for i in range(self._NUM_CHANNELS)]

        # Store attributes (from ARTIQ code)
        assert 2 * V <= vref <= 5 * V, 'Reference voltage out of range'
        self.vref = vref
        assert 0 <= offset_dacs < 2 ** 14, 'Offset DACs out of range'
        if vref == 5 * V:
            assert offset_dacs <= 8192
        self.offset_dacs = offset_dacs

        # Internal registers
        self._dac_reg_mu = [0] * self._NUM_CHANNELS  # Kept in machine units for JIT conversion
        self._offset_reg = [0.0] * self._NUM_CHANNELS  # Float signals can only take float values
        self._gain_reg = [0.0] * self._NUM_CHANNELS  # Float signals can only take float values
Esempio n. 19
0
    def __init__(self, system: dax.base.system.DaxSystem):
        """Instantiate a new GTKWave save file generator.

        :param system: The system of interest
        """
        assert isinstance(system, dax.base.system.DaxSystem)

        # Verify that we are in simulation
        _logger.debug(f'DAX.sim enabled: {system.dax_sim_enabled}')
        if not system.dax_sim_enabled:
            raise RuntimeError('GTKWave safe file can only be generated when dax.sim is enabled')

        # Get the signal manager and verify that the VCD signal manager is used
        signal_manager: VcdSignalManager = typing.cast(VcdSignalManager, get_signal_manager())
        _logger.debug(f'Signal manager type: {type(signal_manager).__name__}')
        if not isinstance(signal_manager, VcdSignalManager):
            raise RuntimeError('GTKWave safe file can only be generated when using the VCD signal manager')
        # Get the registered signals
        registered_signals = {k.key: v for k, v in signal_manager.get_registered_signals().items()}
        _logger.debug(f'Found {len(registered_signals)} registered signal(s)')

        # Generate file name
        file_name: str = get_file_name(system.get_device('scheduler'), 'waves', 'gtkw')

        with open(file_name, mode='w') as f:
            # Create save file object and add generic metadata
            gtkw = vcd.gtkw.GTKWSave(f)
            gtkw.comment(f'System ID: {system.SYS_ID}',
                         f'System version: {system.SYS_VER}',
                         datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
                         f'DAX version: {_dax_version}')
            gtkw.sst_expanded(False)

            # Iterator of registered devices grouped by parent
            parents = itertools.groupby(system.registry.get_device_parents().items(), operator.itemgetter(1))

            for (p, device_parent_iterator) in parents:
                # Unpack iterator
                devices: typing.List[str] = [d for d, _ in device_parent_iterator]
                _logger.debug(f'Found {len(devices)} device(s) for parent "{p.get_system_key()}"')

                if devices:
                    # Create a group for this parent (Parents are not nested)
                    gtkw.comment(f'Parent "{p}"')
                    with gtkw.group(p.get_system_key(), closed=True):
                        for d in devices:
                            # Add signals for each device in this parent
                            signals = registered_signals.pop(d, None)

                            if signals is not None:
                                # Add signals
                                self._add_signals(gtkw, d, signals)

            if registered_signals:
                # Handle leftover registered signals
                gtkw.comment('Leftover signals')
                _logger.debug(f'Adding signals for leftover device(s): {list(registered_signals)}')

                for d, signals in registered_signals.items():
                    # Add signals
                    self._add_signals(gtkw, d, signals)
Esempio n. 20
0
    def construct_env(
            self,
            env_class: typing.Type[__E_T],
            *,
            device_db: typing.Union[str, typing.Dict[str, typing.Any],
                                    None] = None,
            logging_level: typing.Union[int, str] = logging.NOTSET,
            build_args: typing.Sequence[typing.Any] = (),
            build_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None,
            env_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None,
            **kwargs: typing.Any) -> __E_T:
        """Construct an ARTIQ environment based on the given class.

        The constructed environment can be used for testing.

        Devices in the device manager are automatically closed by a finalizer.
        It is not required to close the devices in the device manager explicitly.

        :param env_class: The environment class to construct
        :param device_db: The device DB to use (defaults to file configured in :attr:`DEFAULT_DEVICE_DB`)
        :param logging_level: The desired logging level
        :param build_args: Positional arguments passed to the build function of the environment
        :param build_kwargs: Keyword arguments passed to the build function of the environment
        :param env_kwargs: Keyword arguments passed to the argument parser of the environment
        :param kwargs: Keyword arguments passed to the argument parser of the environment (updates ``env_kwargs``)
        :return: The constructed ARTIQ environment object
        """

        # Set default values
        if build_kwargs is None:
            build_kwargs = {}
        if env_kwargs is None:
            env_kwargs = {}

        assert issubclass(
            env_class, HasEnvironment
        ), 'The environment class must be a subclass of HasEnvironment'
        assert isinstance(
            device_db, (str, dict)
        ) or device_db is None, 'Device DB must be of type str, dict, or None'
        assert isinstance(
            logging_level,
            (int, str)), 'Logging level must be of type int or str'
        assert isinstance(
            build_args,
            collections.abc.Sequence), 'Build arguments must be a sequence'
        assert isinstance(build_kwargs,
                          dict), 'Build keyword arguments must be a dict'
        assert all(
            isinstance(k, str) for k in
            build_kwargs), 'Keys of the build kwargs dict must be of type str'
        assert isinstance(env_kwargs,
                          dict), 'Environment keyword arguments must be a dict'

        # Set level of module logger
        _logger.setLevel(logging_level)

        # Construct an expid object
        expid: typing.Dict[str, typing.Any] = {
            'log_level': logging_level,
            'class_name': env_class.__name__,
            'repo_rev': 'N/A'
        }

        if isinstance(device_db, dict):
            # Copy the device DB to not mutate the given one
            device_db = device_db.copy()
        else:
            # Obtain device DB from file
            _logger.debug('Obtaining device DB from file')
            with warnings.catch_warnings():
                # Ignore resource warnings that could be raised from evaluating the device DB
                # These warnings appear when starting the MonInjDummyService
                warnings.simplefilter('ignore', category=ResourceWarning)
                device_db = device_db_from_file(
                    self.DEFAULT_DEVICE_DB if device_db is None else device_db)

        # Convert and configure device DB
        _logger.debug('Converting device DB')
        enable_dax_sim(ddb=device_db,
                       enable=True,
                       logging_level=logging_level,
                       output='peek',
                       moninj_service=False)

        # Construct environment, which will also construct a new signal manager
        _logger.debug('Constructing environment')
        env = env_class(
            get_managers(device_db,
                         expid=expid,
                         arguments=env_kwargs,
                         **kwargs), *build_args, **build_kwargs)

        # Store the new signal manager
        _logger.debug('Retrieving peek signal manager')
        self.__signal_manager = typing.cast(PeekSignalManager,
                                            get_signal_manager())
        assert isinstance(
            self.__signal_manager,
            PeekSignalManager), 'Did not obtained correct signal manager type'

        # Return the environment
        return env
Esempio n. 21
0
    def __init__(self,
                 dmgr: typing.Any,
                 ref_period: float,
                 ref_multiplier: int = 8,
                 compile: bool = False,
                 **kwargs: typing.Any):
        """Simulation driver for :class:`artiq.coredevice.core.Core`.

        :param compile: If :const:`True`, compile kernels before simulation (see also :class:`Core`)
        """
        assert isinstance(compile, bool), 'Compile flag must be of type bool'

        # Get the virtual simulation configuration device, which will configure the simulation
        # DAX system already initializes the virtual sim config device, this is a fallback
        self._sim_config = dmgr.get(DAX_SIM_CONFIG_KEY)

        # Call super
        super(Core, self).__init__(dmgr,
                                   ref_period=ref_period,
                                   ref_multiplier=ref_multiplier,
                                   **kwargs)

        # Store arguments
        self._device_manager = dmgr

        # Get file name generator (explicitly in constructor to not obtain file name too late)
        if self._sim_config.output_enabled:
            # Requesting the generator creates the parent directory, only create if output is enabled
            self._file_name_generator = FileNameGenerator(
                self._device_manager.get('scheduler'))
        else:
            # For completeness, set a base file name generator if output is disabled
            self._file_name_generator = BaseFileNameGenerator()

        # Get the signal manager and register signals
        self._signal_manager = get_signal_manager()
        self._reset_signal = self._signal_manager.register(self,
                                                           'reset',
                                                           bool,
                                                           size=1)

        # Set initial call nesting level to zero
        self._level = 0

        # Counter for context switches
        self._context_switch_counter = 0
        # Counting dicts for function call profiling
        self._func_counter = collections.Counter()
        self._func_time = collections.Counter()

        # Configure compiler
        if compile:
            core_kwargs = {k: v for k, v in kwargs.items() if k in {'target'}}
            self._compiler = artiq.coredevice.core.Core(
                {},
                host=None,
                ref_period=ref_period,
                ref_multiplier=ref_multiplier,
                **core_kwargs)
            # Set the compiler's device manager core to reference its own core
            self._compiler.dmgr[self.key] = self._compiler
            _logger.debug('Kernel compilation during simulation enabled')
        else:
            self._compiler = None