def close(self) -> None: """Close the simulation. The signal manager will be closed. """ _logger.debug('Closing signal manager') get_signal_manager().close()
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)
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()
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)
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)
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)
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
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)
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)
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()
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)
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)
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)
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)
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)
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)
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')
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
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)
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
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